When should you use approximate vs exact vector search?

Short answer: Exact search checks every eligible vector; approximate indexes trade a small miss risk for speed at large scale.

At small scale, brute-force can be fine and even preferable because it guarantees the true nearest neighbors. As collections grow, HNSW-style approximate search becomes necessary. A tight filter can shrink the eligible set enough that exact search on that subset is faster than navigating a full-collection approximate index. Engram’s stack can choose adaptively rather than locking one mode forever.

The approximate nearest-neighbor search covered throughout most of this Part earns its speed by deliberately not guaranteeing the mathematically perfect answer. That deliberate imprecision is worth examining directly, since it isn’t always the right choice, and understanding exactly when exact search remains genuinely competitive, or even preferable, clarifies a decision this Part has mostly assumed rather than spelled out.

What Does Exact Search Actually Guarantee That Approximate Search Doesn’t?

Exact search, sometimes called brute-force or flat search, compares a query against every single stored vector directly, guaranteeing that whatever comes back is genuinely the true closest match, with no possibility of missing a better result that happened to be structurally hard for an index to reach. Approximate search, the HNSW-based approach covered earlier in this Part, trades this guarantee away deliberately, accepting some chance of missing a small fraction of the true best matches in exchange for dramatically faster search at scale. Exact search never makes that particular kind of mistake, because it never skips anything, it simply costs more to run as the underlying collection grows.

Why Does This Tradeoff Actually Favor Exact Search at Small Scale?

Comparing a query against every stored vector is computationally trivial when there are only a few hundred or a few thousand vectors to check, cheap enough that the entire exercise finishes in a genuinely negligible amount of time regardless of how careful or thorough that comparison actually is. Building and maintaining an approximate index carries its own overhead, memory for the graph structure, complexity in tuning its parameters, that only pays for itself once the underlying collection has grown large enough that skipping most of it during a search actually saves meaningful time. Below that scale, the overhead of maintaining an approximate index can genuinely cost more than it saves, making a straightforward exact comparison the simpler, faster, and more accurate choice for exactly this reason.

At What Point Does This Balance Actually Flip in Favor of Approximate Search?

As a collection grows, the cost of exact search grows directly and linearly along with it, since every additional stored item means one more comparison every single search has to perform. Approximate search’s cost grows far more slowly, since its whole design is built around skipping the vast majority of a collection rather than touching all of it. At some collection size, exact search’s linear cost overtakes whatever fixed overhead an approximate index carries, and beyond that point approximate search becomes decisively faster, often by a very large margin, precisely the crossover this Part’s earlier discussion of vector indexes was building toward without stating the exact threshold explicitly. Well-designed systems recognize this crossover and can switch approaches automatically as a specific collection actually grows past it, rather than forcing a single, fixed choice made once and never revisited regardless of how much the underlying data has grown since.

Does a Restrictive Filter Change Which Approach Is Actually Faster, Even for an Otherwise Large Collection?

It does, in a way directly connected to the filtered-search discussion covered earlier in this Part. A sufficiently restrictive filter can narrow an otherwise enormous collection down to a genuinely small, eligible subset, and once that subset is small enough, running an exact comparison directly against just that narrowed set can actually be faster than forcing an approximate index, built and optimized for the full, much larger collection, to navigate its way toward a small, scattered pocket of eligible candidates buried within it. This is exactly why the choice between exact and approximate search isn’t purely a function of how large the overall collection is, it depends on how large the collection actually eligible for a specific query turns out to be, once any relevant filtering has already been applied.

Is This Choice Something a System Has to Make Once and Commit to Permanently?

It doesn’t have to be. A system genuinely benefits from treating this as a decision made per query, or per meaningfully distinct scope, rather than one fixed, global setting applied uniformly regardless of how much any specific portion of the data has actually grown. A newly onboarded user with only a handful of stored memories is well served by exact search’s simplicity and guaranteed accuracy, while a heavily active user with years of accumulated history genuinely benefits from approximate search’s speed advantage at that larger scale. Treating this as a static, one-time decision misses exactly the kind of variation that a real, growing memory system actually exhibits across its different scopes.

How Does Weaviate Engram’s Underlying Infrastructure Handle This Crossover Without Requiring Manual Intervention?

Weaviate Engram runs on top of Weaviate’s index infrastructure, which supports exactly this kind of adaptive behavior, capable of favoring exact comparison for smaller, more restrictive searches and shifting toward approximate search once a collection or a filtered scope genuinely grows large enough to benefit from it. Consider a specialty auto-parts distributor’s inventory-matching assistant, where some searches are scoped narrowly to one specific warehouse’s modest inventory while others span the distributor’s entire, much larger national catalog:

from engram import EngramClient

client = EngramClient(api_key=os.environ["ENGRAM_API_KEY"])

client.memories.add(
    "This warehouse currently stocks the aftermarket brake caliper compatible with the 2019-2021 model range, three units on hand as of the last inventory count.",
    properties={"warehouse_id": "warehouse-regional-9"},
)

A search scoped to just this one, comparatively modest regional warehouse benefits from the simplicity and guaranteed accuracy exact comparison naturally offers at that smaller scale:

regional_results = client.memories.search(
    query="Do we have the aftermarket brake caliper for this model range in stock here?",
    properties={"warehouse_id": "warehouse-regional-9"},
)

A different search spanning the distributor’s entire national inventory across every warehouse combined benefits instead from approximate search’s speed advantage at that much larger, genuinely accumulated scale, where exact comparison across the full national catalog would meaningfully slow the search down. Neither approach is universally correct, the right choice for a given query depends entirely on how much data that specific query actually has to search through once any relevant scoping has already narrowed things down, exactly the judgment this chapter has been building toward, and exactly the kind of decision Engram’s underlying infrastructure is built to make adaptively rather than forcing a single fixed choice on every search regardless of its actual scale.

Approximate and exact search both operate against a collection that’s assumed to be sitting still while a query runs. Real collections keep changing as new memories get added and old ones get modified, and keeping a search index actually current with those changes is its own distinct challenge. Our next chapter, What is index rebuilding and vector freshness?, takes up exactly that challenge.