🔎 Hybrid Search
Three ways to search: the dedicated search() (BM25, faceted, highlighted), the hybrid SQL operator, and the typed namespace handle.
POST /search — BM25 with facets & highlights
res = client.search(
"alice chen", "Person",
limit=10,
facets=["company", "risk"],
highlight=True,
filters={"company": "Acme Corp"},
matching_strategy="all",
)
for hit in res.hits:
print(hit.score, hit.fields["name"])
# 8.42 Alice Chen{
"hits": [{"score": 8.42, "fields": {"name": "Alice Chen", ...}}],
"total": 1,
"estimated_total_hits": 1,
"facets": {"company": {"Acme Corp": 1}, "risk": {"HIGH": 1}},
"processing_time_ms": 2.1
}HYBRID_SEARCH — fused BM25 + vector (via SQL)
client.query(
"HYBRID_SEARCH FROM CaseDoc "
"QUERY 'embezzlement offshore transfers' LIMIT 3"
){"rows": 3, "data": [
{"title": "Whistleblower Complaint by Carla Nunez", "_score": 12.84},
{"title": "Acme Corp CFO Under Scrutiny", "_score": 9.21},
{"title": "Suspicious Activity Report", "_score": 7.55}
]}All three documents found, ranked by relevance — no Elasticsearch, no external service.
Weighted fusion — [graph, bm25, vector]
# Pure BM25 (keyword precision, no semantic fuzziness):
client.query(
"HYBRID_SEARCH FROM CaseDoc QUERY 'ShellCo shell company' "
"LIMIT 3 WEIGHTS 0.0 1.0 0.0"
)
# Balanced BM25 + vector via the search() door (set metric or weights to
# trigger the hybrid channel — #2672):
client.search(
"embezzlement offshore", "CaseDoc",
metric="cosine", weights=[0.0, 0.5, 0.5],
)The
WEIGHTStriple is[graph, bm25, vector]. Setting any one to1.0and the others to0.0gives you single-channel mode.
Next: Identity Resolution — query the IdentityIndex that SmartIngest built during ingest.