Search Optimization
A schema, facet, and indexing playbook for making search both relevant and usable. The biggest wins are upstream of scoring — better schemas and tighter scoping beat any boost-tuning round.
For the per-change tuning loop see Ranking tuning. For evaluation methodology see Relevance evaluation.
Start with user intent
Before indexing anything, answer:
| Question | Why it matters |
|---|---|
| What entity types do users actually search for? | These are the only types that should appear as default search hits. |
| What facets do they refine by? | Drive your edge model; facets you can't compute are useless. |
| What's the top "zero result" query? | Reveals missing content or missing aliases. |
| Do users click the first result or scroll? | Click-on-first is a strong signal that ranking is working. |
Pull these from search analytics, not from product-manager intuition.
Index only what matters
The default temptation is to index every field. Resist it.
- Index: titles, summaries, names, descriptions, identifiers, body text of long-form content.
- Don't index: status codes, timestamps, boolean flags, internal IDs that nobody types, deprecated fields.
Status and category fields are better as facets than as searchable text — they're discoverable and filterable without polluting score signal.
Re-audit indexed fields every six months. Drop anything that contributes < 1% of high-rank hits.
Schema for searchability
Search quality is a property of the schema as much as the indexer.
| Pattern | Why |
|---|---|
| Materialize the entity users search for. | "Search for cases by device" needs an explicit ForDevice edge. |
| Split body vs summary. | Different boost levels; embed only the body, search both. |
| Normalize aliases at ingestion. | "MBA" → MacBook Air once, in the connector. Don't expect the retriever to do it at query time. |
| Carry source provenance as an edge. | Facet by source; show "from Confluence" vs "from email." |
| Permission as graph, not as field. | ReBAC scales; tenant_id == ? filters do not. |
See Graph design patterns for the deeper schema patterns.
Facets — the highest leverage
Facets are how users go from "1000 results" to "the right 3." They're more valuable than ranking tweaks for any non-trivial corpus.
| Facet type | Source | Best for |
|---|---|---|
| Property facet | A node property (Status, Priority). |
Categorical fields with low cardinality (< 50 values). |
| Related facet | An outbound edge (Owner, Customer). |
Graph-relationship-driven filters. |
| Time facet | A [Timestamp] property. |
"Last 7 days", "Last quarter", custom ranges. |
| Numeric facet | A numeric property (Price, Severity). |
Ranges. |
| Value facet | A property with discrete known values. | Multi-select tag-style filters. |
Ship the 3–5 facets users actually use, not the 20 that could exist. Cluttered facet rails are worse than no facets.
Use graph context for scope
The single biggest precision win is computing TargetUIDs from a graph traversal before search runs.
request.TargetUIDs = Q().StartAt("Customer", customerId)
.Out("Account")
.Out("SupportCase")
.AsUIDEnumerable()
.ToArray();
This collapses the candidate set the search engine considers. It also removes a whole class of "wrong tenant" leaks at the architectural level. Any constraint expressible in the graph should be in the graph, not in post-hoc filtering.
Hybrid as default
Keyword-only search struggles with paraphrases. Vector-only search struggles with identifiers. Hybrid retrieves both branches and merges; for most corpora, it's the right baseline.
See Hybrid search for the algorithm and tuning.
When to disable hybrid: pure identifier/code corpora (parts catalogs, SKU directories), where vector retrieval adds latency without lifting relevance.
Boost discipline
Boosts compose multiplicatively. Two field boosts at 5.0 and 3.0 is a 15× ratio against an un-boosted field — way too aggressive in practice.
- Default everything to
1.0. - Boost titles / summaries to
2.0. - Boost identifiers (exact-match IDs) to
3.0–5.0only if your golden set says so. - Never boost a field to make up for missing facets — fix the schema instead.
Relevance evaluation checklist
Treat search relevance like any other engineering metric — measured, tracked, and reviewed.
| ✓ | Item |
|---|---|
| □ | A golden set with ≥ 80 queries and their correct top-3 UIDs, version-controlled. |
| □ | A weekly job that runs the golden set and emits precision@1, precision@3, NDCG@10, recall@50. |
| □ | Per-query regression alerts (any query that drops from precision@3 = 1 to 0). |
| □ | A staging environment where ranking changes ship first, with shadow-mode runs. |
| □ | A revert plan documented for every ranking change. |
| □ | Quarterly review of the golden set — products move, "correct" labels drift. |
| □ | Click-through, refinement, and zero-result rates plotted in dashboards. |
| □ | A documented threshold for "good enough to ship" (e.g. NDCG@10 ≥ baseline + 0.02). |
See Relevance evaluation for the full methodology.
Instrumentation
Track in your analytics:
- Top queries by volume. Where to focus tuning.
- Top zero-result queries. Where the corpus or aliases are missing.
- Click-through-rate by rank position. A CTR drop at rank 1 suggests the top result is wrong.
- Refinement rate. % of queries that triggered a facet selection. High = facets are useful. Zero = facets are wrong.
- Time-to-first-click. Long means users are scanning; short means rank 1 is right.
Common pitfalls
- Tuning by feel. Always against a golden set.
- Indexing every field. Noise drowns signal.
- Over-boosting. A
5.0boost on every field is a1.0boost on every field. - Forgetting facets. Ranking can't substitute for a "filter by customer" button.
- Permission as a post-hoc filter. Use the graph's ACL; it's faster and correct.
- No re-evaluation after re-embed. Model swaps silently shift ranking. Re-run the golden set.
Where to go next
- Ranking tuning — the per-change loop.
- Hybrid search — algorithm and blend.
- Graph design patterns — schema patterns that lift search.
- Relevance evaluation — golden sets and metrics.
- Performance tuning — latency-side optimization.