Getting Started with Unified Search Engine
Enable the module, configure which collections to index, set up semantic search (optional), test queries — 4 steps to enterprise search.
Enable Search Module
Admin → Modules → Search → Enable:
``` Module enabled. Search API: /search Default mode: hybrid
Collections to index: None configured yet. Semantic search: Not configured. ```
Verify search is working:
``` GET /api/search?q=test
Response: { "data": { "results": [], "meta": { "query": "test", "mode": "hybrid", "timing": 2, // 2ms "totalResults": 0 } } } ```
Test with content:
1. Create a product: "Wireless Bluetooth Headphones"
2. Wait for indexing (automatic, same-transaction)
3. Search: GET /api/search?q=wireless
4. Result: product found, highlighted
Configure Collections to Index
Admin → Search → Index Configuration:
Full-text indexing (automatic on all collections):
``` All collections with text fields are auto-indexed for full-text.
Collections detected: ✅ products (title, description, tags) ✅ pages (title, content, metaDescription) ✅ contacts (name, email, company) ✅ companies (name, description, industry) ✅ blog posts (title, content, tags) ✅ helpdesk tickets (subject, body) ... (all collections with text fields)
You don't need to configure anything. Full-text works automatically. ```
Column weights (optional):
javascript
// Admin → Search → Index Config → Column Weights
{
"products": {
"title": 3, // A weight — most important
"description": 2, // B weight — secondary
"tags": 1, // C weight — tertiary
"specs": 1,
},
"pages": {
"title": 3,
"metaDescription": 2,
"content": 1,
},
"contacts": {
"name": 3,
"email": 2,
"company": 1,
}
}
Fields to exclude:
```javascript // Some fields should not be searched: // • Large text blocks (body, content) — only index first 1000 chars // • System fields (id, timestamps, internal notes) // • Sensitive data (SSN, credit card numbers)
{ "orders": { "excludeFromIndex": ["notes", "internalComments", "creditCardLast4"], "maxCharsPerField": 1000 } } ```
Enable Semantic Search (Optional)
Semantic search uses AI embeddings to understand meaning, not just keywords.
Prerequisite: OpenAI or Anthropic API key
``` Admin → Search → Semantic Search → Configure
AI Provider: OpenAI API Key: sk-... (your key) Model: text-embedding-3-small (1536 dims, $0.02/1M tokens) ```
Any embedding-capable provider configured on your tenant's AI settings can back semantic search (OpenAI, Voyage, Cohere, Mistral, Google, and others) — pick the one your tenant already uses for embeddings.
Enable semantic on collections:
``` Admin → Search → Semantic Search → Collections
Select collections to vectorize: ✅ products (product name, description, tags) ✅ blog posts (title, content, tags) ❌ pages (use full-text only — changing content) ❌ contacts (privacy concerns with embeddings)
Click "Generate Embeddings" → Background job starts → Progress: 0% → 100% → Embeddings stored in pgvector ```
Index status:
``` Admin → Search → Vector Index
Collections indexed: products: Status: ✅ Complete Documents: 10,000 Avg tokens/doc: 127 Total cost: $0.13 Last updated: 2024-01-15 14:32
blog posts: Status: ✅ Complete Documents: 500 Avg tokens/doc: 89 Total cost: $0.01 Last updated: 2024-01-15 14:30 ```
Incremental updates:
``` When you create/update a product: 1. Save product to PostgreSQL (normal) 2. Search module detects change 3. Generate embedding for changed fields 4. Upsert to embeddings table 5. Product is semantically searchable immediately
No manual re-indexing needed. ```
Test Queries and Monitor Analytics
Test search modes:
Full-text:
``` GET /api/search
{ "query": "wireless headphones noise cancelling", "mode": "fulltext", "collections": ["products"], "limit": 10 }
Response: { "data": { "results": [ { "collection": "products", "id": "p_123", "title": "Wireless Bluetooth Headphones Pro", "highlight": "<em>Wireless</em> <em>Bluetooth</em> <em>Headphones</em> <em>Pro</em> with active <em>noise</em> <em>cancelling</em>", "score": 0.95, "route": "/admin/commerce/products/p_123" } ], "meta": { "mode": "fulltext", "timing": 8, "totalResults": 15 } } } ```
Semantic (concept-based):
``` GET /api/search
{ "query": "something to listen to music while exercising", "mode": "semantic", "collections": ["products"], "limit": 10 }
Response: { "data": { "results": [ { "collection": "products", "id": "p_123", "title": "Sport Wireless Earbuds", "score": 0.91, "route": "/admin/commerce/products/p_123" } // ← "Sport Wireless Earbuds" wasn't in the query // but semantically similar → returned by meaning, not keyword ] } } ```
Hybrid (RRF fusion):
``` GET /api/search
{ "query": "best laptop for video editing", "mode": "hybrid", "collections": ["products"], "limit": 10 }
Response: { "data": { "results": [ { "collection": "products", "id": "p_456", "title": "MacBook Pro 16 — Video Editing Powerhouse", "score": 0.94, "sources": { "fulltext": 1, "semantic": 1 }, // ↑ Rank 1 in BOTH engines → RRF fusion boosted it "route": "/admin/commerce/products/p_456" } ], "meta": { "mode": "hybrid", "fulltextResults": 20, "semanticResults": 18, "fusionTiming": 3, "totalTiming": 28 } } } ```
Monitor search analytics:
``` Admin → Search → Analytics
Top queries today: 1. "wireless headphones" — 1,247 searches 2. "iphone case" — 892 searches 3. "laptop stand" — 654 searches
Queries with no results (opportunities): 1. "hdmi cable 6ft" — 234 searches, 0 results 2. "water bottle metal" — 156 searches, 0 results → Action: Create products for these searches ```
Each query's timing (db, embedding, total) is recorded per-search, so you can spot slow queries or modes from the raw search_queries log.
Setup FAQ
No. The moment the module is enabled, every collection with text fields is auto-indexed — products, pages, contacts, tickets, and the rest work immediately. Column weights and field exclusions are optional refinements, not prerequisites.
No. Full-text and hybrid both work out of the box. Semantic search is opt-in — it needs an embedding-capable provider key on your tenant's AI settings, and you enable it per collection when you want meaning-based matching rather than keyword matching.
hybrid. It runs the full-text and semantic queries in parallel and blends them with Reciprocal Rank Fusion, so a result can win on keywords, on meaning, or both. You can override it per query with `mode: "fulltext"` or `mode: "semantic"`.
No. Full-text updates in the same transaction as the write. For semantic collections, the changed fields are re-embedded and upserted automatically in the background — there's no manual re-indexing job to run.
You bring your own embedding provider and pay their rate with zero platform markup. For example, `text-embedding-3-small` is $0.02 per 1M tokens, so tens of thousands of typical documents index for only cents.