Skip to main content

pghybrid

Hybrid search on the Postgres you already have.

Vector similarity + full-text search, combined by Reciprocal Rank Fusion, on plain pgvector. No pg_search, no VectorChord, no Elasticsearch, no vector database, no extension you need superuser to install.

pip install pghybrid

Why

Hybrid search on Postgres is a solved problem if you can install a C extension. pg_search, VectorChord and pg_textsearch are all excellent, and all of them are extensions — and on managed Postgres you usually cannot install extensions at all.

pgvector is available almost everywhere. BM25 extensions are available almost nowhere: not on RDS, Aurora, Cloud SQL, Azure Database, Supabase or Heroku, and removed from Neon for new projects in March 2026.

This is not a claim to search better than ParadeDB — pg_search's BM25 genuinely beats ts_rank_cd, and if you can install it you probably should. It is search you can actually install.

The case it exists for

A contract. Someone asks "renewal notice period". The clause that answers it says "sixty days written notice prior to the anniversary date" — it never uses the word renewal. Vector search puts a plausible-but-wrong clause first. Keyword search puts the clause that uses all three words and answers none of them first. The right answer is second on both signals and first on neither, which is exactly what rank fusion is for.

Use it

from pghybrid import Config, HybridSearch

search = HybridSearch(
    Config(table="chunks", text_column="content", vector_column="embedding",
           tsvector_column="fts", extra_columns=["title"], paramstyle="pyformat"),
    execute=lambda sql, params: conn.execute(sql, params).fetchall(),
)

for row in search.search("renewal notice period", embedding=query_vector, limit=10):
    print(row.score, row.matched_by, row.get("title"))

You pass the embedding in. pghybrid never calls a model, so it works with OpenAI, Cohere, Voyage, a local sentence-transformer or anything else, and needs no API key. It never opens a connection either — it generates SQL and hands it to the driver you already use. for_psycopg, for_sqlalchemy, for_asyncpg and for_django set the placeholder style for you.

row.matched_by tells you which signal found each row — both, vector or text — which is usually the first thing you want when a search returns the wrong thing.

Read the statement instead of running it

sql, params = search.build_sql("renewal notice period", query_vector, limit=10)

It is one query with a candidate CTE per signal, fused by RRF. Nothing is hidden, and pghybrid sql prints it without a database at all.

Command line

pghybrid init   --dsn "$DATABASE_URL" --table chunks   # inspects, writes the migration
pghybrid doctor --dsn "$DATABASE_URL" --table chunks   # measured recall, read-only

doctor measures recall@k against exact search, sweeps ef_search/probes, catches queries that silently fall back to a sequential scan, and reports a tsvector that has stopped matching its text — a failure that returns wrong answers without erroring.

explain decomposes a single result set: both ranks, both raw scores, each signal's contribution, and the near-miss band just below your cut-off.

Also in TypeScript

npm install pghybrid generates byte-identical SQL, checked on every commit rather than assumed.


Full documentation, the reasoning, and copy-and-paste SQL: github.com/pavangupta352/pghybrid

MIT © Pavan Gupta

Release files for pghybrid 0.1.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 pghybrid 0.1.0
File Size Uploaded
pghybrid-0.1.0.tar.gz 355.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pghybrid 0.1.0
File Interpreter ABI Platform
pghybrid-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 442.8 kB

Release files / pghybrid-0.1.0.tar.gz

Download URL pghybrid-0.1.0.tar.gz
Size 355.9 kB
Tags Source
SHA-256 checksum
How to use checksums
1033d132983af18895033d406fb02f4b4da6e5464cb8c84ad5f8b674aab1138e
BLAKE2b-256 checksum
How to use checksums
744af5d0534531e7ea94671f64f26c182abc97dabb71bbe0781dd2aacc52e9a3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release files / pghybrid-0.1.0-py3-none-any.whl

Download URL pghybrid-0.1.0-py3-none-any.whl
Size 86.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d501cf0eaa0d7965b6c7a6f8bdadcd314a45870765102318d53f3dbd44700cf4
BLAKE2b-256 checksum
How to use checksums
c3176faafa4b4b3637640d02e18589906606eda574f8699e330a5e3f5795c09f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

This release

0.1.0 This release

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