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")
const hits = docs.bm25Search("body", "cancel subscription", 5, { mode: "or" });
use infino::BoolMode;
let hits = docs.bm25_search("body", "cancel subscription", 5, BoolMode::Or, None)?;
| 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 |
Counting matches
count returns how many rows match a BM25 keyword query, without fetching or ranking
them — cheaper than a search when you only need the tally.
n = docs.count("body", "cancel subscription") # or mode="and"
const n = docs.count("body", "cancel subscription"); // or { mode: "and" }
use infino::BoolMode;
let n = docs.count("body", "cancel subscription", BoolMode::Or)?;
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")
const a = docs.exactMatch("doc_id", "1");
const b = docs.tokenMatch("body", "billing");
use infino::BoolMode;
let a = docs.exact_match("doc_id", "1", None)?;
let b = docs.token_match("body", "billing", BoolMode::Or, None)?;
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)
const hits = docs.vectorSearch("embedding", embed("cancel subscription"), 5);
// higher recall: probe more IVF partitions
const more = docs.vectorSearch("embedding", embed("..."), 5, { nprobe: 32 });
use infino::VectorSearchOptions;
let hits = docs.vector_search("embedding", &q, 5, VectorSearchOptions::new(), None, None)?;
// higher recall: probe more IVF partitions
let more = docs.vector_search("embedding", &q, 5,
VectorSearchOptions::new().with_nprobe(32), None, None)?;
| 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")
const hits = docs.vectorSearch("embedding", embed("..."), 5,
{ filter: { column: "body", query: "billing" } });
use infino::{VectorFilter, VectorSearchOptions, BoolMode};
let hits = docs.vector_search("embedding", &q, 5, VectorSearchOptions::new(),
Some(VectorFilter { column: "body", query: "billing", mode: BoolMode::Or }), None)?;
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 over the same table and fuses their rankings with
reciprocal-rank fusion (RRF), which is strong when a query has both keyword and
semantic intent. It’s a first-class method — pass the text query and the query vector,
and it returns ranked Arrow rows like the other searches:
hits = docs.hybrid_search("body", "cancel subscription", "embedding",
embed("cancel subscription"), 5)
# mode (BM25 boolean mode) and nprobe (vector recall) are optional:
hits = docs.hybrid_search("body", "cancel subscription", "embedding",
embed("..."), 5, mode="and", nprobe=32)
const hits = docs.hybridSearch("body", "cancel subscription", "embedding",
embed("cancel subscription"), 5);
// mode (BM25 boolean mode) and nprobe (vector recall) are optional:
const tuned = docs.hybridSearch("body", "cancel subscription", "embedding",
embed("..."), 5, { mode: "and", nprobe: 32 });
use infino::{BoolMode, VectorSearchOptions};
let hits = docs.hybrid_search(
"body", "cancel subscription", BoolMode::Or,
"embedding", &q, VectorSearchOptions::new(), 5, None,
)?;
| Argument | Type | Default | Description |
|---|
text_column | string | required | the FTS-indexed text column |
text_query | string | required | query terms for the BM25 side |
vector_column | string | required | the vector-indexed column |
vector_query | float[] | required | the query vector (same dim as the index) |
k | int | required | number of top results |
mode | or / and | or | BM25 boolean mode |
nprobe | int | engine default | IVF partitions to probe on the vector side |
projection | string[] | _id + score | columns to return |
Hybrid search needs both indexes on the table: an fts index on the text column and a
vector index on the embedding column. It’s also available in SQL via
the hybrid_search table function, where you can 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