AI Search Plugin
AI-powered semantic search that understands natural language queries and finds relevant content based on meaning, not just keywords.
Overview
The AI Search plugin transforms how users find content in your SonicJS application. Instead of relying solely on keyword matching, it uses vector embeddings and semantic search to understand the intent behind queries and return the most relevant results.
Built on Cloudflare's edge infrastructure, it delivers fast, intelligent search experiences without sending data to external services.
Semantic Understanding
Understands natural language queries and finds content by meaning
Edge-Powered
Runs on Cloudflare Workers AI and Vectorize for low latency
Hybrid Search
Combines AI search with keyword fallback for best results
Analytics
Track search patterns, popular queries, and usage metrics
Features
Semantic Search
- Natural language query understanding
- Context-aware results based on content meaning
- Relevance scoring for better ranking
- Works across all content types
Keyword Search
- Traditional text matching as fallback
- Full-text search across titles, slugs, and content
- Supports filtering by collection, date, status, and author
Smart Autocomplete
- AI-powered search suggestions
- History-based suggestions as fallback
- Configurable suggestion count
Content Indexing
- Automatic collection discovery
- Selective collection indexing
- Real-time index updates
- Index status monitoring
Setup
Prerequisites
The AI Search plugin requires Cloudflare Workers AI and Vectorize bindings:
wrangler.toml
# Add AI binding for embeddings
[ai]
binding = "AI"
# Add Vectorize index for vector search
[[vectorize]]
binding = "VECTORIZE_INDEX"
index_name = "sonicjs-content"
Create Vectorize Index
Create a Vectorize index in your Cloudflare account:
Create Index
# Create a new Vectorize index
npx wrangler vectorize create sonicjs-content --dimensions=768 --metric=cosine
The dimensions must match the embedding model used by Workers AI. The default model uses 768 dimensions.
Enable the Plugin
The AI Search plugin is included in SonicJS core. Navigate to the admin panel to configure it:
- Go to Admin > Plugins > AI Search
- Enable AI mode for semantic search
- Select collections to index
- Save settings
Configuration
Plugin Settings
interface AISearchSettings {
enabled: boolean // Enable/disable search functionality
ai_mode_enabled: boolean // Enable semantic AI search
selected_collections: string[] // Collection IDs to index
dismissed_collections: string[] // Collections user chose not to index
autocomplete_enabled: boolean // Enable search suggestions
cache_duration: number // Cache duration in hours
results_limit: number // Max results per query (default: 20)
index_media: boolean // Index media file metadata
}
Default Settings
| Setting | Default | Description |
|---|---|---|
enabled | true | Search functionality active |
ai_mode_enabled | true | Use AI for semantic search |
autocomplete_enabled | true | Show search suggestions |
cache_duration | 1 | Cache results for 1 hour |
results_limit | 20 | Return up to 20 results |
index_media | false | Don't index media by default |
API Reference
POST /api/search
Execute a search query with optional filters.
Search Request
curl -X POST http://localhost:8787/api/search \
-H "Content-Type: application/json" \
-d '{
"query": "blog posts about security best practices",
"mode": "ai",
"filters": {
"collections": ["1", "2"],
"status": ["published"]
},
"limit": 10
}'
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Search query text |
mode | 'ai' | 'keyword' | No | Search mode (default: 'keyword') |
filters | object | No | Filter options |
limit | number | No | Max results (default: 20) |
offset | number | No | Pagination offset |
Filter Options
Filter Types
interface SearchFilters {
collections?: string[] // Filter by collection IDs
dateRange?: {
start: Date // Start date
end: Date // End date
field?: 'created_at' | 'updated_at'
}
status?: string[] // Filter by status (draft, published, etc.)
tags?: string[] // Filter by tags
author?: string // Filter by author ID
custom?: Record<string, any> // Custom metadata filters
}
GET /api/search/suggest
Get autocomplete suggestions for partial queries.
Suggestions Request
curl "http://localhost:8787/api/search/suggest?q=sec"
GET /api/search/analytics
Get search usage analytics (requires authentication).
Analytics Request
curl http://localhost:8787/api/search/analytics \
-H "Authorization: Bearer ${token}"
Search Modes
AI Mode (Semantic Search)
When AI mode is enabled and Vectorize is configured, queries are processed using vector embeddings:
- Query text is converted to a vector embedding using Workers AI
- Vectorize finds similar content vectors
- Results are ranked by semantic relevance
- Content snippets highlight relevant sections
Best for:
- Natural language queries ("how do I set up authentication?")
- Concept-based searches ("articles about performance")
- Finding related content
Keyword Mode
Traditional text-based search using SQL LIKE queries:
- Query is matched against title, slug, and content fields
- Results are ordered by update date
- Exact and partial matches are found
Best for:
- Specific term searches ("JWT token")
- Known titles or slugs
- When AI bindings aren't available
Automatic Fallback
If AI search fails or isn't available, the system automatically falls back to keyword search:
Fallback Logic
// Automatic fallback in the service
if (query.mode === 'ai' && settings.ai_mode_enabled && this.customRAG?.isAvailable()) {
return this.searchAI(query, settings)
}
// Fallback to keyword search
return this.searchKeyword(query, settings)
Collection Indexing
Selecting Collections
Not all collections need to be searchable. The admin interface lets you:
- Index - Include collection in search results
- Dismiss - Hide collection from indexing prompts
- New - Unreviewed collections awaiting decision
Index Management
Collection Info
interface CollectionInfo {
id: string // Collection ID
name: string // Internal name
display_name: string // Human-readable name
description?: string // Collection description
item_count?: number // Number of items
is_indexed: boolean // Currently indexed
is_dismissed: boolean // User dismissed
is_new?: boolean // Awaiting decision
}
New Collection Detection
The plugin automatically detects new collections and prompts you to decide whether to index them:
Detection API
// Detect collections not yet indexed or dismissed
const notifications = await aiSearchService.detectNewCollections()
// Returns array of new collection notifications
// [{
// collection: { id: '5', name: 'faqs', ... },
// message: 'New collection "FAQs" with 25 items available for indexing'
// }]
Frontend Integration
Basic Search Form
Search Component
<form id="searchForm">
<input
type="text"
name="query"
placeholder="Search..."
autocomplete="off"
/>
<select name="mode">
<option value="ai">AI Search</option>
<option value="keyword">Keyword</option>
</select>
<button type="submit">Search</button>
</form>
<div id="results"></div>
<script>
document.getElementById('searchForm').addEventListener('submit', async (e) => {
e.preventDefault()
const formData = new FormData(e.target)
const response = await fetch('/api/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: formData.get('query'),
mode: formData.get('mode')
})
})
const { data } = await response.json()
document.getElementById('results').innerHTML = data.results
.map(r => `
<div class="result">
<h3><a href="/content/${r.slug}">${r.title}</a></h3>
<p>${r.snippet}</p>
<small>${r.collection_name} - ${r.relevance_score?.toFixed(2) || 'N/A'}</small>
</div>
`)
.join('')
})
</script>
With Autocomplete
Autocomplete Integration
const searchInput = document.getElementById('searchInput')
const suggestionsDiv = document.getElementById('suggestions')
let debounceTimer
searchInput.addEventListener('input', async (e) => {
clearTimeout(debounceTimer)
const query = e.target.value
if (query.length < 2) {
suggestionsDiv.innerHTML = ''
return
}
debounceTimer = setTimeout(async () => {
const response = await fetch(`/api/search/suggest?q=${encodeURIComponent(query)}`)
const { data } = await response.json()
suggestionsDiv.innerHTML = data
.map(s => `<div class="suggestion" data-value="${s}">${s}</div>`)
.join('')
}, 300)
})
suggestionsDiv.addEventListener('click', (e) => {
if (e.target.classList.contains('suggestion')) {
searchInput.value = e.target.dataset.value
suggestionsDiv.innerHTML = ''
// Trigger search
}
})
React Example
React Hook
import { useState, useCallback } from 'react'
interface SearchResult {
id: string
title: string
slug: string
snippet?: string
relevance_score?: number
}
export function useAISearch() {
const [results, setResults] = useState<SearchResult[]>([])
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const search = useCallback(async (query: string, mode: 'ai' | 'keyword' = 'ai') => {
setLoading(true)
setError(null)
try {
const response = await fetch('/api/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, mode })
})
const { data, success, error } = await response.json()
if (!success) throw new Error(error)
setResults(data.results)
return data
} catch (err) {
setError(err instanceof Error ? err.message : 'Search failed')
return null
} finally {
setLoading(false)
}
}, [])
return { results, loading, error, search }
}
Analytics
The plugin tracks search patterns to help you understand user behavior:
Tracked Metrics
- Total queries - Number of searches in the last 30 days
- AI vs Keyword - Breakdown by search mode
- Popular queries - Most common search terms
- Query timing - Average search duration
Using Analytics Data
Analytics Integration
// Fetch analytics data
const analytics = await aiSearchService.getSearchAnalytics()
// Use for dashboard
console.log(`Total searches: ${analytics.total_queries}`)
console.log(`AI usage: ${(analytics.ai_queries / analytics.total_queries * 100).toFixed(1)}%`)
// Top searches
analytics.popular_queries.forEach(({ query, count }) => {
console.log(`"${query}" - ${count} searches`)
})
Admin Interface
Settings Page
Navigate to Admin > Plugins > AI Search to access:
- Enable/Disable - Toggle search functionality
- AI Mode - Enable semantic search (requires Vectorize)
- Collection Management - Select which collections to index
- Autocomplete - Toggle search suggestions
- Cache Settings - Configure result caching
Collection Manager
The collection manager shows:
- All available collections
- Indexing status for each
- Item counts
- Actions to index or dismiss
Search Testing
Test search directly from the admin interface:
- Enter a test query
- Select search mode (AI or Keyword)
- View results with relevance scores
- Check query timing
Troubleshooting
AI Search Not Working
Check Vectorize binding:
- Ensure
VECTORIZE_INDEXis configured inwrangler.toml - Verify the index was created with correct dimensions (768)
Check AI binding:
- Ensure
AIbinding is configured - Verify Workers AI is available in your plan
No Results Returned
Check collection indexing:
- Ensure collections are selected for indexing
- Verify content exists in indexed collections
Check filters:
- Ensure status filter includes your content status
- Verify date range includes your content
Slow Search Performance
Enable caching:
- Set
cache_durationto cache frequent queries
Reduce index size:
- Only index collections that need search
- Disable media indexing if not needed