Skip to main content

dot-search

PyPI Python Version Licence: AGPL v3 Pipeline

Add vector, keyword and exact search to the database tables you already have.

from dot_search import SearchEngine, TableConfig, EmbeddingConfig

engine = SearchEngine(db_url="postgresql+psycopg2://user:pass@localhost/mydb")

engine.index(
    TableConfig(table="articles", embeddings=[EmbeddingConfig(key="body", source_column="body", dimension=1536)]),
    batch_size=500,
)

for hit in engine.search("neural networks", "articles"):
    print(hit.id, hit.score)

dimension is the vector size your embedding model returns — 1536 for OpenAI's text-embedding-3-small, 1024 for Mistral's mistral-embed. It has to match, or indexing fails on the first batch.

Adding semantic search to an existing application usually means standing up a separate vector database, then keeping it in sync with the tables that hold the real data — a second source of truth, a sync job, and a new failure mode.

dot-search takes the other route: it adds embedding columns and search indexes to your existing tables, in your existing database. Your rows stay where they are, joins keep working, and a search result is a row you already own. Vector, BM25 and exact matching can run together, with results merged through Reciprocal Rank Fusion.

Features

  • Vector search on any text column, through pgvector
  • BM25 keyword search, through ParadeDB pg_search
  • Exact substring matching
  • Hybrid search fusing several strategies via Reciprocal Rank Fusion
  • Several independent indexes on the same table
  • Row filtering with a parameterised Filter API — filter values are bound, so they can safely carry end-user input
  • Declarative configuration, persisted so an index can be reopened later
  • Batch or caller-driven indexing
  • Pluggable embedders, defaulting to dot-inference
  • SQLite backend for local development and testing

Requirements

PostgreSQL (production)

Requires pgvector and ParadeDB pg_search:

CREATE EXTENSION vector;
CREATE EXTENSION pg_search;

SQLite (development and testing)

Uses sqlite-vec and FTS5. Convenient for tests, but not intended for production.

Installation

pip install dot-search

Configuration

Embeddings are produced through dot-inference, which reads its settings from DOTI_-prefixed environment variables:

Variable Example
DOTI_EMBEDDING__PROVIDER openai
DOTI_EMBEDDING__OPENAI__MODEL_NAME text-embedding-3-small
DOTI_EMBEDDING__OPENAI__API_KEY your API key

Works with any OpenAI-compatible API (OpenAI, Mistral, OpenRouter, vLLM, TGI, Ollama, …). See the dot-inference settings for the full list, or pass your own embedding function instead.

Quick start

from dot_search import (
    BM25Config,
    EmbeddingConfig,
    ExactConfig,
    Filter,
    SearchConfig,
    SearchEngine,
    TableConfig,
)

engine = SearchEngine(db_url="postgresql+psycopg2://user:pass@localhost/mydb")

# --- 1. Index a table with vector search ---
engine.index(
    TableConfig(
        table="articles",
        embeddings=[EmbeddingConfig(key="body", source_column="body", dimension=1536)],
    ),
    batch_size=500,
)

# --- 2. Search ---
for hit in engine.search("neural networks", "articles"):
    print(hit.id, hit.score)

# --- 3. Combine several strategies on the same table ---
engine.index(
    TableConfig(
        table="articles",
        embeddings=[
            EmbeddingConfig(key="body", source_column="body", dimension=1536),
            EmbeddingConfig(key="title", source_column="title", dimension=1536),
        ],
        bm25=[
            BM25Config(key="title_bm25", source_column="title"),
            BM25Config(key="body_bm25", source_column="body"),
        ],
        exact=[ExactConfig(key="name_exact", source_column="name")],
    ),
    batch_size=500,
)

# --- 4. Hybrid search with row filters and weight overrides ---
results = engine.search(
    "fermentation and gut health",
    "articles",
    SearchConfig(
        limit=10,
        where=[
            Filter(column="published_year", op="gte", value=2022),
            Filter(column="topic", op="eq", value="health"),
        ],
        weights={"body": 1.0, "title": 0.3, "title_bm25": 0.5, "body_bm25": 2.0},
    ),
)

# --- 5. Single-strategy search ---
results = engine.search("fermentation", "articles", SearchConfig(strategy="bm25"))
results = engine.search("Dupont", "articles", SearchConfig(strategy="exact"))

# --- 6. Several independent indexes on one table ---
engine.index(
    TableConfig(
        table="articles",
        index_id="article_titles",
        embeddings=[EmbeddingConfig(key="title_only", source_column="title", dimension=1536)],
    ),
    batch_size=500,
)
results = engine.search("gut health", "article_titles")

# --- 7. Index specific rows (caller-driven batching) ---
engine.index(
    TableConfig(
        table="articles",
        embeddings=[EmbeddingConfig(key="body", source_column="body", dimension=1536)],
    ),
    row_ids=[1, 5, 10],
)

Search strategies

Strategy What it uses
"hybrid" Vector + BM25 + exact (any configured), fused via RRF (default)
"vector" Vector similarity only
"bm25" BM25 keyword search only
"exact" Substring (LIKE) search only

min_score

SearchConfig.min_score drops results scoring below a threshold. It is only meaningful for single-strategy searches (vector, bm25, exact), where raw scores are preserved and interpretable — a cosine similarity, for instance. It is rejected in hybrid mode, because RRF scores are rank-based and carry no absolute meaning; use limit there instead.

Filtering

Row filters are expressed with Filter objects rather than SQL fragments:

SearchConfig(where=[
    Filter(column="status", op="eq", value="published"),
    Filter(column="lang", op="in", value=["fr", "en"]),
    Filter(column="deleted_at", op="is_null"),
])

Available operators: eq, ne, lt, lte, gt, gte, in, not_in, like, ilike, is_null, is_not_null.

Column names are validated as plain identifiers, and values are passed to the database as bound parameters — never interpolated into SQL. Filter values are therefore safe to build from end-user input.

Batching modes

index() requires exactly one of batch_size or row_ids:

  • batch_size=N — dot-search reads all rows and batches embeddings internally in chunks of N.
  • row_ids=[...] — the caller decides which rows to embed. dot-search only reads, embeds, and writes those specific rows in a single pass. This lets you drive batching from the outside — useful when you want to control concurrency, retry individual batches, or stream IDs from a queue.

Setup steps (creating columns, saving config) run in both modes — they're idempotent.

Stability

dot-search follows semantic versioning: everything exported from the top-level package is covered, anything underscore-prefixed is internal and may change in any release. Public names are never removed without a deprecation period.

dependencies = ["dot-search>=2.0,<3"]

See docs/VERSIONING.md for the full policy.

Roadmap

  • Async engine API
  • Reranking stage after fusion
  • Incremental re-indexing on row updates

Documentation

Document Contents
docs/DEVELOPMENT.md Environment setup, tests, code style
docs/VERSIONING.md Versioning, deprecation policy, how to depend on this package
docs/PUBLISHING.md Cutting a release
docs/ARCHITECTURE.md Architecture and data-flow diagrams
CHANGELOG.md Release history

Contributing

Contributions are welcome. See CONTRIBUTING.md for the DCO sign-off requirement, the licensing terms that apply to contributions, and how to submit a change.

Licence

Copyright (C) 2026 Kannon For Deep Tech (deepika)

This software is distributed under the GNU Affero General Public License, version 3 or later — see LICENSE.md.

A commercial licence is available for use in proprietary environments. Contact: louis.letarnec@deepika.ai

Metadata

Release files for dot-search 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dot-search 2.0.0
File Size Uploaded
dot_search-2.0.0.tar.gz 82.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dot-search 2.0.0
File Interpreter ABI Platform
dot_search-2.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 114.2 kB

Release files / dot_search-2.0.0.tar.gz

Download URL dot_search-2.0.0.tar.gz
Size 82.1 kB
Tags Source
SHA-256 checksum
How to use checksums
e0f3bdb32111903ab54a8eef577fdc821b0627f08dbae8aaa4f74cecd74744fa
BLAKE2b-256 checksum
How to use checksums
e7f893df74ba4de42a84d12db27db47a6a86dac01e45329ebc3403b5a70a9855
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / dot_search-2.0.0-py3-none-any.whl

Download URL dot_search-2.0.0-py3-none-any.whl
Size 32.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e8bec3e58ae788d834d45b24081f1fe4710467d528a2717258ca3e7bf7f61570
BLAKE2b-256 checksum
How to use checksums
e9ea38e22ab9f48cf0c6ff10e8e003596bbf9dff46bde5b3136c5bd1d3d5fd43
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

2.0.0 This release

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page