Search nomadLab

pgvector vs Pinecone vs Qdrant vs Weaviate: The WHERE Clause Decides

Nobody picks a vector store on unfiltered QPS, because nobody runs unfiltered queries. What separates these four is what happens when you add a tenant filter, and which version you are pinned to.

Updated

Almost every vector database comparison ranks these four by queries per second on a public benchmark, which measures a query nobody ships: top-k over the whole collection, no filter, one tenant, static data. Real retrieval queries carry a WHERE. Filter by tenant, by conversation, by document set, by whether the thing has been deleted.

That is the part where an approximate index stops being a solved problem, and it is the part where these four differ most.

Filtering an approximate index is a choice, not a feature

pgvector’s own README states the problem without hedging: “With approximate indexes, queries with filtering can return less results since filtering is applied after the index is scanned.”

So an HNSW scan walks the graph, hands back roughly hnsw.ef_search candidates, and then Postgres applies your WHERE. Ask for ten results with a filter that matches one row in a thousand and you can get two, or none. The index did its job. The filter ate the answer.

Two ways a filtered top-k query resolves. In pgvector the HNSW scan returns ef_search candidates, the WHERE clause is applied afterward, and fewer than k results can come back unless iterative scan is enabled. In Qdrant a payload index narrows the candidate set first and the HNSW traversal runs inside it, returning k results provided the payload index was created. What a WHERE clause does to an approximate index pgvector: scan, then filter Qdrant: filter inside the scan HNSW returns ef_search candidates WHERE applied to those rows fewer than k results, unless hnsw.iterative_scan is set payload index narrows candidates HNSW traverses only those k results, as long as the payload index was created first Either way the decision happens before the query: a setting on one side, an index on the other.
Sourced from pgvector's README section on iterative index scans and Qdrant's filtering documentation, read 22 August 2026.

pgvector 0.8.0 added a fix for this, and it is off by default:

SET hnsw.iterative_scan = strict_order;   -- exact distance order
SET hnsw.iterative_scan = relaxed_order;  -- better recall, slight reordering

Iterative scans keep walking the index until they have enough rows or hit hnsw.max_scan_tuples. If you are running pgvector with tenant filters and have never set this, your recall is worse than you think and no error tells you.

Qdrant went the other way. Filter conditions run against payload indexes you create explicitly, and the docs are blunt about the prerequisite: “For performant filtering, create payload indexes for the fields you plan to filter on. For best results, create payload indexes before ingesting data.” Skip that and you get the same class of surprise from the other direction, just as a latency problem instead of a recall problem.

pgvector: the version you are on matters more than the benchmark

First, a correction to something repeated everywhere, including in the earlier version of this post: there is no pgvector 0.9. The current release is 0.8.6, dated 29 July 2026. Sparse vector support arrived in 0.7.0, not in some 2026 release.

More useful than the version number is what the recent releases were for. Reading the changelog in order:

VersionDateWhat it fixed
0.8.317 Jun 2026possible index corruption with HNSW vacuuming
0.8.430 Jun 2026hnsw graph not repaired error, and errors on insert during HNSW vacuuming
0.8.58 Jul 2026IVFFlat build memory on small tables
0.8.629 Jul 2026IVFFlat buffer overflow on 32-bit, sparsevec cast limits

Three consecutive releases in six weeks on HNSW vacuuming. Vacuum is what runs after deletes and updates, which is exactly the code path an agent-memory workload hammers and a static RAG corpus never touches. If you are storing conversation memory in pgvector and you are on 0.8.2 or earlier, that is the upgrade to schedule, and it has nothing to do with throughput.

0.8.3 also fixed a performance regression on Hamming and Jaccard distance specific to Postgres 18, which is worth knowing since Postgres 18 has been the sensible target since it went GA on 25 September 2025 and is supported through November 2030.

The benchmark everyone quotes is not measuring pgvector

The “Postgres does 471 QPS at 50M vectors while Qdrant does 41” numbers that circulate come from Timescale’s comparison, and they measure pgvectorscale, a separate extension implementing StreamingDiskANN and statistical binary quantization. That is a different index than the one you get from CREATE EXTENSION vector.

Which matters, because pgvectorscale has not cut a release since 0.9.0 on 4 November 2025. The cadence before that was roughly quarterly: 0.6.0 in February 2025, 0.7.0 in March, 0.8.0 in July, 0.9.0 in November. Then nine months of nothing tagged, on a repo that is still receiving commits. I would not read that as abandoned. I would read it as a reason to check whether the version you can install supports the Postgres you are running before you build a capacity plan on its benchmark.

Pinecone: read the meter, and check which tier is cheaper

Pinecone’s pricing as of 22 August 2026 has four tiers, and one detail in the table is easy to read past.

StorageWrite unitsRead units
Standard, $50/mo minimum$0.33/GB/mo$4.00 to $4.50 per million$16 to $18 per million
Enterprise, $500/mo minimum$0.33/GB/mo$6.00 to $6.75 per million$24 to $27 per million

Enterprise costs 50% more per unit than Standard, on top of a minimum that is ten times higher. You are buying the SLA and the support, not volume pricing. There is also a newer Builder tier at a flat $20 per month with capped usage, which is a reasonable place to find out what your real read-unit consumption looks like before committing.

The forecasting problem is genuine and structural. Read-unit consumption depends on how much of the index a query has to touch, so filter complexity moves the bill in ways you cannot model from row counts alone. Egress is $0.10 per GB after 100 GB, backups are $0.10 per GB-month, and restore is $0.15 per GB.

Qdrant and Weaviate: both alive, both permissive, different pitches

Both ship on a monthly-ish cadence and both are genuinely open source, which is not a given in this category. Qdrant is Apache-2.0, at v1.19.0 released 5 August 2026, with commits landing this week. Weaviate is BSD-3-Clause, at v1.39.0 released 4 August 2026, same picture.

Qdrant’s argument is the filtering model above plus a single self-contained binary. Weaviate’s is hybrid retrieval, where BM25 and vector scores are fused in one query, plus object references. Hybrid is the real differentiator and it is also the thing most RAG pipelines do not need; if your recall problem is that semantic search misses exact product codes, that is a hybrid problem and Weaviate is built for it. If it is not, you are learning a schema model for nothing.

Neither publishes cloud pricing I could pull off the page without an account, so I am not going to invent a monthly range for them. Both self-host on one box for a long time.

What I would actually decide on

If you already run Postgres, start there, on 0.8.4 or later, with hnsw.iterative_scan set and an honest look at whether your filters are selective enough to need it. The operational argument is the strongest one in this whole comparison and it has not changed: no second backup story, no second failover story, no second thing that pages someone.

Reach for Qdrant when the query pattern is heavily filtered and latency-bound, because the filter model is the thing you are buying. Reach for Weaviate when you need BM25 and vectors scored together. Reach for Pinecone when nobody on the team is going to operate a database, and price the read units against a month of real traffic rather than a row count.

The experiment that settles it costs an afternoon. Load a hundred thousand of your own embeddings, run your ten most common filtered queries, and count how many results come back versus how many you asked for. That number tells you more than any benchmark chart.

Keep reading