Skip to main content
Let’s get Infino running in a few minutes. We’ll build a small knowledge base (a handful of help-center notes) and search it three ways: by keyword, by meaning, and with SQL. Pick your language; each step builds on the one before it.
1

Install

pip install infino
# Or with uv (https://docs.astral.sh/uv/):
uv pip install infino
The Rust examples build Arrow data, and update / delete take DataFusion predicates (col, lit), so add these alongside infino, matching the Arrow and DataFusion versions Infino uses (Arrow 53 and DataFusion 53 for infino 0.1.x):
[dependencies]
infino = "0.1"
arrow-array = "53"
arrow-schema = "53"
datafusion-expr = "53"  # only needed for update / delete
Run cargo tree -p infino to check the versions if you are on a newer Infino.Infino also installs the mimalloc global allocator by default. If you embed it in a process that already sets a global allocator, turn it off: infino = { version = "0.1", default-features = false }.
2

Connect

First, open a connection. "memory://" keeps everything in memory for this walkthrough; point it at "./data" or an "s3://bucket/prefix" URI when you want your data to stick around.
import infino
import pyarrow as pa

db = infino.connect("memory://")
3

Create a table

Now create a table. You give it a schema and say which columns to index: a full-text index on the text, and a vector index on the embedding.
schema = pa.schema([
    pa.field("source", pa.large_utf8(), nullable=False),
    pa.field("body", pa.large_utf8(), nullable=False),
    pa.field("embedding", pa.list_(pa.float32(), 16), nullable=False),
])
docs = db.create_table(
    "docs", schema,
    infino.IndexSpec().fts("body").vector("embedding", 16, 1, "cosine"),
)
4

Add data

Let’s add a few notes. The embed helper stands in for a real embedding model so the example runs on its own. It’s a 16-dim one-hot by topic (0 = billing, 1 = appearance). Your own embeddings will be dense and higher-dimensional; see Embeddings.
def embed(topic):
    v = [0.0] * 16
    v[topic] = 1.0
    return v

docs.append([
    {"source": "help-center", "body": "To cancel a subscription, open Settings then Billing.", "embedding": embed(0)},
    {"source": "help-center", "body": "Refunds return to the original payment method.",         "embedding": embed(0)},
    {"source": "blog",        "body": "Enable dark mode under Settings then Appearance.",        "embedding": embed(1)},
])
5

Search it

Now the part you came for. The same table answers all three kinds of query: keyword, meaning, and SQL.
keyword  = docs.bm25_search("body", "cancel subscription", 5)               # BM25
semantic = docs.vector_search("embedding", embed(0), 5)                     # vector kNN
# vector kNN, restricted to rows whose body matches a keyword (pushdown filter):
filtered = docs.vector_search("embedding", embed(0), 5, filter_column="body", filter_query="billing")
billing  = db.query_sql("SELECT body FROM docs WHERE source = 'help-center'")  # SQL
Each search returns Arrow rows. With this tiny corpus, the body of the rows you get back looks like this:
Expected output
keyword   "To cancel a subscription, open Settings then Billing."

semantic  "Refunds return to the original payment method."
          "To cancel a subscription, open Settings then Billing."
          "Enable dark mode under Settings then Appearance."

filtered  "To cancel a subscription, open Settings then Billing."

sql       "To cancel a subscription, open Settings then Billing."
          "Refunds return to the original payment method."
keyword matched the BM25 terms; semantic ranks the billing notes first; filtered keeps only the note whose text matches billing; sql returns the two help-center rows. From here, feed the retrieved passages to your model as grounding context.

Next steps

Core concepts

The mental model: one Parquet copy, indexed and queried four ways.

Guides

Tables, embeddings, indexing, search, and storage.

Integrations

LangChain, Vercel AI SDK, CrewAI, and MCP.

SQL Reference

Query and compose search with SQL.
Last modified on June 29, 2026