Skip to main content

Hybrid Search

A hybrid search ranks the same records two ways, by vector similarity and by BM25 keywords, and fuses the two rankings into one. Meaning finds the paraphrases; keywords keep exact names, codes and rare terms from being drowned out. Add a text leg to vectorSearch and the engine does the rest in one request.

It needs an embedding index for the vector leg and a text index for the text leg. It works on RushDB Cloud and on a self-hosted engine; on a self-hosted engine, pass a queryVector from your own model.


How it runs​

  1. Filter once. labels and where select one set of candidate records.
  2. Rank inside the candidates. The vector leg and the text leg each rank only those records, so a record outside the filter never takes a top slot.
  3. Fuse. Each leg contributes to every record it returned; a record both legs found gets both contributions.
  4. Page. Results are sorted by fused score, then skip and limit apply.

result = db.records.vector_search({
"index": "Article.body.text-embedding-3-small.1536.cosine",
"query": "how do graph databases store relationships", # Cloud; use queryVector on a self-hosted engine
"text": {"query": "index-free adjacency"},
"where": {"status": "published"},
"limit": 10,
})

print(result.meta["fusion"], result.meta["vectorSearch"], result.meta["textSearch"])

The text leg's query defaults to the vector query, and its index to the one ready text index of the first label. text also takes index, property, allTerms and phrase, as in keyword search.


Fusion​

fusionA leg's contribution to a recordUse when
rrf (default)weight / (rrfK + rank + 1), rank counted from 0The legs' scores aren't comparable; the robust default.
dbsfweight × normalized score: each leg's scores mapped by that leg's own mean ± 3σ to [0, 1]How far ahead a record is within a leg should count, not only its place.
ParameterWhereDefaultDescription
fusiontop levelrrfHow the legs combine.
rrfKtop level60RRF smoothing: smaller lets top-ranked hits dominate, larger favours agreement between legs.
weighttop level1The vector leg's share of the fused score.
prefetchLimittop level4 × (skip + limit)Hits the vector leg ranks before fusion (1–10,000).
text.weighttext1The text leg's share; 0 leaves the vector order.
text.prefetchLimittext4 × (skip + limit)Hits the text leg ranks before fusion (1–10,000).

At least one weight must be above 0. The fused score is __score on each record; the result reports which fusion ran.

// Trust keywords half as much, and let a strong lead within a leg count
await db.records.vectorSearch({
index: 'Article.body.text-embedding-3-small.1536.cosine',
queryVector,
text: { query: 'index-free adjacency', weight: 0.5 },
fusion: 'dbsf',
limit: 10
})

See also​