Use case: hybrid retrieval, filters & facets
Hybrid search is little big brain’s headline capability. This guide covers how to get the most out of it: choosing sources and targets, narrowing with filters, bucketing with facets, and fusing several queries with reciprocal-rank fusion.
The signals being fused
Section titled “The signals being fused”A single search.hybrid call combines, over one snapshot:
- lexical matching (exact and near-exact terms)
- ontology resolution and concept expansion (canonicalize + widen the query)
- BM25 full-text scoring
- vector / ANN semantic similarity
- graph-neighborhood signal (entities near strong matches score higher)
You don’t wire these together — the engine does. Add explain: true to see each
leg’s contribution.
Sources, consistency & targets
Section titled “Sources, consistency & targets”const res = await lbb.search.hybrid("customer identity storage", { topK: 10, source: "persisted", // "persisted" (index runs) or "ephemeral" (built on the fly) consistency: "strong", // overlay WAL committed after the index run targets: ["entities", "assertions", "observations"],});source—persisteduses the durable index runs (fast, scalable);ephemeralbuilds an index on the fly (handy right after a write with no persisted run yet). On the hosted product, search never 404s: it falls back to ephemeral while a base run is built.consistency—strong(default) overlays facts committed after the last index run so results match the head;eventualskips the overlay for lower cost.targets— which document kinds to return:entities,assertions,observations, relation types, ontology terms/concepts, neighborhoods.
Filters
Section titled “Filters”Filters are structured JSON expressions applied across BM25, vector, and hybrid
paths. Supported ops: equality, set membership (in), existence, numeric
comparison, token containment, glob, regex, and boolean and/or/not.
const res = await lbb.search.hybrid("identity storage", { topK: 10, filter: { and: [ { field: "entity_type", op: "in", value: ["DATABASE", "DATASET"] }, { field: "tier", op: "eq", value: "prod" }, ], },});Filters can prune candidates early, but final results always re-verify the filter against graph-derived metadata — a candidate that slips through approximate generation is still dropped if it doesn’t truly match.
Facets
Section titled “Facets”Facets count metadata buckets after filters — great for building a faceted UI or letting an agent see the shape of the result set before drilling in.
const res = await lbb.search.hybrid("identity storage", { topK: 10, facets: ["target_kind", "entity_type", "relation"],});// res.facets => { entity_type: { DATABASE: 3, DATASET: 1 }, relation: { STORES: 2, ... } }Common facet fields: target_kind, entity_type, relation, source_id,
label, target, text.
Multi-search (RRF)
Section titled “Multi-search (RRF)”When a question decomposes into several angles, run them as subqueries and fuse with reciprocal-rank fusion — this beats a single query for recall on multi-faceted questions.
const res = await lbb.search.multi({ queries: [ "systems that store identity data", "services that read customer records", "databases classified as prod tier", ], topK: 10,});Each subquery runs the full hybrid pipeline; RRF combines their rankings so a result strong across several subqueries rises to the top.
Just BM25 or just vectors
Section titled “Just BM25 or just vectors”When you want a single leg — for evaluation, or because you already know which signal matters — call it directly:
await lbb.search.fullText("customer identity", { topK: 10 }); // BM25 onlyawait lbb.search.vector({ query: "customer identity", topK: 10 }); // ANN onlyFor offline recall/latency comparison, the console Search & eval view runs accuracy and latency checks so you can compare single-leg vs. hybrid configurations side by side.
Bulk-loading a corpus first
Section titled “Bulk-loading a corpus first”To retrieve over a real corpus, ingest it with bulk NDJSON
(POST /v1/graph/import) rather than per-fact commits — it’s tens of times
faster and supports flat properties and external keys. Then build indexes once
and search. See the HTTP API.
Tuning recall
Section titled “Tuning recall”- Configure managed embeddings and pick a model per graph for real semantic recall — see Embeddings & feedback tuning.
- Grade results with
search_feedback(3 = relevant, 1 = partial, 0 = irrelevant) to produce qrels and fine-tune the embedding model to your domain. - Use
explain: trueand checkexplain.vector_model_idto confirm the vector leg is using the model you configured. - Fold in rule-derived and type-closure facts with
reason: truewhen relevant context is entailed rather than written verbatim.
Related
Section titled “Related”- Core concepts: hybrid search, filters, facets, consistency
- Embeddings & feedback tuning
- SPARQL, structured query & SHACL for exact, analytical questions