Purpose and Functionality

Intermediate4 min

A B-tree cannot find nearest neighbors, exact search stops scaling, and approximate search trades a guarantee for speed. Sometimes you need none of it.

#vector-search

The operation a vector store exists for

You have a million passages, each embedded as a vector of several hundred numbers. A user asks a question, you embed it, and you need the passages closest to it.

That is nearest-neighbor search in a high-dimensional space, and it is the one operation these stores are built around. Everything else they offer exists to make that operation usable.

Why a B-tree index cannot find nearest neighbors

A B-tree answers two kinds of question: is this value equal to that one, and does it fall in this range. Both depend on the values having an order.

Vectors have no useful order. "Closest in 768 dimensions" cannot be decomposed into a range scan on any single dimension, because two vectors can agree closely on four hundred dimensions and still be far apart overall. Sorting by the first coordinate tells you nothing about proximity.

So the index that makes your WHERE created_at > ... query fast does nothing for similarity, and a store that only had B-trees would have to compare against every row.

Where exact search stops working

Comparing against every vector is not wrong. It returns exactly the right answer, and for a small corpus it is instant.

Qdrant puts the limit plainly: the naive approach "might work with dozens or even hundreds of examples but may become a bottleneck" past that. Every query touches every vector, so cost grows with the corpus and never stops growing.

This is worth stating because the opposite claim gets made a lot. Exact search is not a beginner's mistake. It is correct, and it is what you should use until your corpus makes it slow. A few thousand chunks in a Postgres table with no vector index will answer fast enough, and you will have skipped an entire system.

What approximate nearest neighbor trades

Past that point, the fix is to stop looking at everything.

Graph-based indexes such as HNSW organize vectors so a search can walk toward the query's neighborhood, and, in Qdrant's words, "compare the distance to some of the objects from the database, not to all of them."

The word doing the work is approximate. You are no longer guaranteed the true nearest neighbors. Most of the time you get them; sometimes a genuinely close vector is missed because the walk did not reach it.

That trade is tunable. Searching more of the graph raises recall and costs time and memory; searching less is faster and misses more. There is no universally right setting, and the question to ask is what a miss costs you. In a recommendation feed, nothing. In a compliance search meant to surface every matching document, a miss is a defect and approximate search may be the wrong tool.

I have not measured recall figures for any particular configuration, and any number quoted without your corpus and your settings would be decoration.

What else comes with the category

The search is the reason these stores exist. The rest is what makes them usable.

Filtering during the search, not after it. Filtering afterward means taking a global top-k and then deleting most of it, which returns far fewer results than you asked for. Performing similarity search covers why the order matters.

Partitioning, through namespaces or collections, so one index serves many tenants with enforced separation.

Upsert semantics and metadata, so a changing corpus can be corrected rather than rebuilt, which storing in a vector database covers.

When you do not need a vector database

Often you do not.

If your data already lives in Postgres, pgvector adds a vector column to the table beside it, and your filters stay ordinary SQL against columns you already have. Supabase Vector makes that case in full, including the transactional argument: a row and its embedding land together or not at all.

Reach for a dedicated store when the search workload deserves to scale independently of your transactional database, when the corpus is large enough that index memory is its own budget, or when you want features built for this job rather than added to a general-purpose database.

Those are real reasons. "It is what you use for RAG" is not one.

Further reading

Knowledge check

Question 1 of 3

Why can a B-tree index not answer 'which of these million vectors is nearest to mine'?

Sign in to save your progress and pick up where you left off.