Search
Use full-text search to find records across text fields in any collection.
#Search
Full-text search lets you find records by matching a search term across all text-type fields in a collection. Use the search query parameter or the SDK's search option.
#How search works
When you pass a search term, EmuView generates LIKE '%term%' clauses for every searchable field in the collection. The clauses are combined with OR, so a match on any field returns the record.
Searchable field types: text, email, url, slug, richtext, markdown, enum.
Fields of other types (number, boolean, json, relation, file, geo) are not included in search queries.
#Steps
#1. Create a collection with searchable fields
Searchable fields are identified by their type — no extra configuration is needed. If your collection has text, email, or richtext fields, they are searchable automatically.
// This collection has three searchable fields: title (text), description (richtext), status (enum)
await fetch('https://your-api.example.com/api/v1/collections', {
method: 'POST',
headers: {
Authorization: 'Bearer sk-your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'products',
storageType: 'd1',
fields: [
{ name: 'title', type: 'text', required: true, indexed: true },
{ name: 'price', type: 'decimal', required: true },
{ name: 'description', type: 'richtext' },
{ name: 'status', type: 'enum', enumValues: ['draft', 'published', 'archived'] }
]
})
});
#2. Search for records
#HTTP
GET /api/v1/collections/products/records?search=widget
Authorization: Bearer sk-your-api-key
#SDK
const results = await sdk.collection('products').list({
search: 'widget'
});
console.log(`Found ${results.total} records matching "widget"`);
#3. Combine search with filters
Search and filters work together with AND logic. The record must match the search term AND satisfy all filter conditions:
#HTTP
GET /api/v1/collections/products/records?search=widget&filter={"status":{"_eq":"published"}}
Authorization: Bearer sk-your-api-key
#SDK
const results = await sdk.collection('products').list({
search: 'widget',
filter: { status: { _eq: 'published' } },
sortBy: '-created_at',
limit: 20
});
This query returns records where any text field contains "widget" AND the status is "published".
#4. Combine search with sorting and pagination
const results = await sdk.collection('products').list({
search: 'premium',
filter: { price: { _gte: 10 } },
sortBy: '-created_at',
page: 1,
limit: 25
});
#Full-text and semantic search (search indexes)
The search parameter above is a simple LIKE scan — fine for admin views and small collections, but it reads every row. For real search features (a public search page, relevance ranking, typo tolerance, multi-collection search), build a search index:
POST /api/v1/search-indexescreates a named index compiled from one or more source collections into a dedicated FTS5 artifact;GET /api/v1/search-indexes/:name/query?q=…queries it with BM25 relevance ranking. Manage and rebuild viaPOST /api/v1/search-indexes/:name/rebuild. The SDK covers the full lifecycle:sdk.search.create(config),.update(),.rebuild(name),.status(name),.delete(name), and.query(name, { q, semantic: true }).- Semantic search is built in: set
"semanticSearch": true(optionallyembeddingModel) on the index config and passsemantic=trueon queries — results are re-ranked by vector similarity fused with the FTS ranking. This requires the gateway's Workers AI (AI) binding, which the standard deploy templates include; on an instance without it,semantic=truequeries return a typed 503semantic_unavailableerror (lexical queries keep working), andGET /healthlistsAIundermissing.optional. See Enabling semantic search for the full setup. - Access control is a post-filter over collection policy. Every hit on a returned page is re-authorized against the source collection's row-level
item_filterfor the calling user (anonymous callers use thepublicrole's filter), so per-row visibility must be encoded in the collection's policy — app-side visibility logic is not replicated by the index.totalandfacetsare aggregates over the matched set, which is never authorized row by row, so they come back asnullunless the caller may read every row of every collection the index serves — refused rather than approximated (ADR 0025). UsehasMoreto page whentotalisnull. - Indexes are not auto-rebuilt when source data changes heavily (bulk imports, truncate, collection delete) — they are marked stale and keep serving the compiled data until you rebuild. Check
is_stale/stale_reasonon the index list; the dashboard surfaces stale indexes with a one-click Rebuild on Settings → Databases. - Filtering an index uses its own grammar, not the one above. The
filter={"status":{"_eq":"published"}}syntax on this page belongs to the collection records API. A search index is filtered withfilter: { status: 'published' }(scalar = equality, object = range) on fields carrying thefilterrole — see Filtering results in the SDK search guide.
#What you learned
- Search matches across all text-type fields using
LIKE '%term%' - Searchable types: text, email, url, slug, richtext, markdown, enum
- Search combines with filters using AND logic
- No additional configuration is needed — searchable fields are identified by type
- For ranked, typo-tolerant, or semantic search, create a search index (
/api/v1/search-indexes)
#Next steps
- Filtering records — Precise field-level queries with operators
- Field types — See which field types support search