Infino retrieves from the same table several ways. Pick the mode that matches the
question; you can also compose any of them in SQL. Every search returns
Arrow rows, and you can pass a projection to choose which columns come back.
Choose a search mode
| You want to… | Use | Why |
|---|
| Match exact keywords or terms | Full-text (BM25) | Lexical ranking over tokenized text |
| Find by meaning or paraphrase | Vector kNN | Semantic similarity over embeddings |
| Cover both keyword and semantic intent | Hybrid | RRF fuses BM25 and vector rankings |
| Look up an exact id or value | exact_match | Unranked, precise |
| Filter, aggregate, or join | SQL | Compose over the same rows |
| Mode | Ranked | Tunable recall | Text pre-filter | Compose in SQL |
|---|
| BM25 | ✓ | n/a | n/a | ✓ (bm25_search) |
| Vector kNN | ✓ | ✓ (nprobe) | ✓ (pushdown) | ✓ (vector_search) |
| Hybrid | ✓ (RRF) | ✓ | n/a | ✓ (hybrid_search) |
token_match / exact_match | n/a | n/a | n/a | ✓ |
| SQL | n/a | n/a | n/a | native |
Always pass a sensible top-k on production queries: it bounds both the work and the
result size.
Full-text (BM25)
Ranked keyword search over an FTS-indexed column.
hits = docs.bm25_search("body", "cancel subscription", 5, mode="or")
| Argument | Type | Default | Description |
|---|
column | string | required | the FTS-indexed text column |
query | string | required | query terms, tokenized by the index |
k | int | required | number of top results |
mode | or / and | or | match any term (or) or require all terms (and) |
projection | string[] | _id + score | columns to return |
Unranked lookups
When you don’t need scoring, token_match (rows containing a token) and exact_match
(rows whose column equals a value) return every matching row, unranked.
rows = docs.exact_match("doc_id", "1")
rows = docs.token_match("body", "billing")
exact_match and token_match run over an FTS-indexed column. Index the column
you want to match, for example IndexSpec().fts("doc_id"), to look it up this way.
Vector search
Semantic search over a vector-indexed column. Embed the query with the same model you
used to index (see Embeddings).
hits = docs.vector_search("embedding", embed("cancel subscription"), 5)
# higher recall: probe more IVF partitions
hits = docs.vector_search("embedding", embed("..."), 5, nprobe=32)
| Argument | Type | Default | Description |
|---|
column | string | required | the vector-indexed column |
query | float[] | required | the query vector (same dim as the index) |
k | int | required | number of top results |
nprobe | int | engine default | IVF partitions to probe; higher = better recall, more work |
rerank_mult | int | engine default | over-fetch multiplier for the exact-rerank stage |
filter | text predicate | none | pushdown pre-filter (below) |
projection | string[] | _id + score | columns to return |
Pushdown filter
Restrict the kNN to rows whose FTS-indexed column matches a text predicate. This is a
pre-filter, where the kNN ranks only among matching rows, not a post-filter on the
top-k.
hits = docs.vector_search("embedding", embed("..."), 5,
filter_column="body", filter_query="billing")
For scalar filtering (such as WHERE source = '...') or filtering the results of a
search, query with SQL.
Hybrid search
Hybrid search runs BM25 and vector kNN and fuses their rankings with reciprocal-rank
fusion (RRF), which is strong when a query has both keyword and semantic intent. It runs
through query_sql via the hybrid_search table function. Pass the query text and the
query vector as a comma-separated literal:
hybrid_search(table, text_column, query_text, vector_column, query_vector, k)
vec = ",".join(map(str, embed("cancel subscription")))
rows = db.query_sql(
f"SELECT doc_id, body, score FROM "
f"hybrid_search('docs', 'body', 'cancel subscription', 'embedding', '{vec}', 5) "
f"ORDER BY score DESC"
)
Hybrid search needs both indexes on the table: an fts index on the text column and a
vector index on the embedding column. Build the vector literal from your own embedding,
and escape any user-supplied query text before putting it in SQL. See the
SQL Reference for the full hybrid_search signature and how to compose
it with joins and filters.
Limitations
- Vector search is approximate (IVF). It trades exactness for speed; raise
nprobe
(and rerank_mult) to recover recall at some cost in work.
- Bring your own embeddings. Embed queries with the same model and dimension you
indexed with.
- Filters in vector search are text predicates over an FTS-indexed column. For scalar
filters, use SQL.
exact_match and token_match need an FTS-indexed column.
See also