What Is k-Nearest Neighbor (kNN) Search in Elasticsearch?

What Is k-Nearest Neighbor (kNN) Search in Elasticsearch?

A complete, beginner-friendly guide to searching by meaning instead of by exact words: how text and images become lists of numbers, how Elasticsearch finds the closest matches quickly, and how to tune, secure and operate it in production.

Imagine you walk into an enormous library and ask the librarian for “a book about a lonely robot who learns to love gardening.” A very literal librarian would hunt for books whose titles contain those exact words and come back empty-handed. A wise librarian, however, understands what you mean. She remembers a story about a rusty machine that tends a rooftop garden and hands it to you at once, even though not one of your words appears in its title. Ordinary keyword search behaves like the literal librarian. k-nearest neighbor search behaves like the wise one.

In this tutorial we start from the very beginning. We will see why matching words is sometimes not enough, how computers turn meaning into numbers, what it means for two things to be “near” each other, and how Elasticsearch finds the closest matches among millions of candidates in a few milliseconds. Then we will learn how to set it up, tune the trade-offs between speed and accuracy, combine it with ordinary search, keep memory under control, secure it, monitor it and avoid the mistakes that catch most beginners.

1The Big Picture: Searching by Words Versus Searching by Meaning

To appreciate kNN search, we first need to see what ordinary search does well and where it runs out of road.

How keyword search works

Traditional Elasticsearch search is built on the inverted index, which lists, for every word, the documents that contain it. When you search for “cheap laptop,” Elasticsearch finds documents containing those words and ranks them with a scoring formula that rewards rare words, frequent occurrences and short fields. This approach, often called lexical search, is fast, precise and easy to explain. If a document contains your words, it can be found.

The weakness is that lexical search only understands words, not ideas. A document about an “inexpensive notebook computer” describes exactly what the user wants, yet it shares no important words with the query. Synonyms, paraphrases, spelling variations, questions phrased in everyday language, and images all fall outside what an inverted index can handle without heavy manual effort.

Simple Analogy

Think of two ways to find a friend in a crowded school hall. Keyword search is shouting their exact name and hoping they hear it. Meaning-based search is looking for someone who wears the same kind of jacket, walks the same way and stands with the same group. You may find them even if you forgot their name.

The idea behind searching by meaning

The trick is to describe every document, and every question, as a point in a kind of map where similar meanings sit close together. A sentence about laptops lands near other sentences about laptops. A photo of a beach lands near other beach photos. Once everything is a point on this map, finding relevant results becomes a geometry problem: given the point for the question, find the closest points. Those closest points are the nearest neighbors.

graph LR
  subgraph Lexical["Keyword search"]
    Q1["Query words"] --> II["Inverted index: word to documents"]
    II --> R1["Documents containing the words"]
  end
  subgraph Semantic["kNN search"]
    Q2["Query turned into a vector"] --> VI["Vector index: points on a map"]
    VI --> R2["Documents whose meaning is closest"]
  end
        
FIG 1 — Two ways of finding results: by matching words and by measuring closeness of meaning
Term 1

Lexical search

Finding documents by the words they contain, using the inverted index and relevance scoring.

Term 2

Semantic search

Finding documents by meaning, so a query can match text that uses completely different words.

Term 3

Vector

A list of numbers that describes something, such as a sentence or an image, as a point in space.

Term 4

Nearest neighbor

The stored item whose vector is closest to the vector of the query.

i
Good to Know

kNN search does not replace keyword search. Each is strong where the other is weak, and the best production systems often use both together, as we will see in the chapter on hybrid search.

2Vectors and Embeddings: Turning Meaning into Numbers

Everything in kNN search depends on one idea: describing things with lists of numbers.

What is a vector?

A vector is simply an ordered list of numbers. You already know a tiny one: a location on a map can be described by two numbers, how far east and how far north. A point in a room needs three numbers. In machine learning, vectors are much longer, often hundreds or even thousands of numbers, and each number captures some faint aspect of the thing being described. Nobody labels the aspects by hand; the model discovers them during training.

What is an embedding?

An embedding is a vector produced by a machine learning model for a piece of data such as a sentence, a paragraph, a product photo or an audio clip. The model has been trained on huge amounts of examples, so that things with similar meaning receive similar lists of numbers. The word “car” and the word “automobile” get vectors that are very close, while “car” and “banana” get vectors that are far apart.

Simple Analogy

Imagine describing every fruit using a few sliders: how sweet, how sour, how crunchy, how juicy, how big. An apple might be sweet 6, sour 4, crunchy 9, juicy 6. A pear would have similar slider settings, so it sits near the apple. A lemon has very different settings, so it sits far away. An embedding is like this, but with hundreds of sliders chosen automatically by a model.

Dimensions

The number of values in a vector is called its number of dimensions. Common text embedding models produce vectors with a few hundred to a couple of thousand dimensions. More dimensions can capture finer detail but cost more memory and more computing effort. Every vector in the same field must have exactly the same number of dimensions, and the query vector must match too.

3
numbers to describe a point in a room
384-1536
typical numbers in a text embedding
1 model
must create both stored and query vectors
flowchart LR
  T["Text: cheap laptop for students"] --> M["Embedding model"]
  M --> V["Vector: 0.12, -0.53, 0.88, ... hundreds more"]
  P["Photo of a beach"] --> M2["Image embedding model"]
  M2 --> V2["Vector: 0.71, 0.02, -0.34, ..."]
        
FIG 2 — Models convert text, images and other data into vectors
!
Critical Rule

Vectors are only comparable when they come from the same model. A vector from one model and a vector from another model live on entirely different maps, and measuring the distance between them is meaningless. Always create stored vectors and query vectors with the same model and the same settings.

3Measuring “Near”: Similarity and Distance

To find the nearest neighbors, we need a clear rule for how near two vectors are.

Three popular ways to measure

There are several ways to measure closeness, and Elasticsearch supports the main ones. Each is a small formula that takes two vectors and returns one number. Choosing the wrong one for your model gives poor results, so it is important to know what each means and which your embedding model expects.

Metric 1

Cosine similarity

Compares the direction two vectors point in, ignoring their length. Values near 1 mean very similar direction.

Metric 2

Dot product

Multiplies matching numbers and adds them up. For vectors scaled to unit length it behaves like cosine similarity and is faster.

Metric 3

L2 (Euclidean) distance

The straight-line distance between two points. Smaller means closer, like measuring with a ruler.

Metric 4

Maximum inner product

A dot product variant for vectors that are not scaled to unit length, used by some recommendation models.

Simple Analogy

Imagine two friends pointing at things from the middle of a field. Cosine similarity asks, “Are you both pointing in the same direction?” and does not care how far each arm reaches. Euclidean distance asks, “How far apart are your fingertips?” A long arm and a short arm pointing the same way are close under cosine, but not necessarily close under distance.

Which one should I use?

Use the metric recommended by the documentation of your embedding model. Many text models are trained with cosine similarity or with dot product on normalized vectors. Normalized means each vector has been scaled to a length of exactly one. When vectors are normalized, cosine similarity and dot product give the same ranking, and dot product is cheaper to compute, so it is a favorite for performance.

MetricBigger value meansBest whenNotes
CosineMore similarModel recommends cosine; vectors may vary in lengthSafe default for most text models
Dot productMore similarVectors are normalized to unit lengthFast; requires unit-length vectors in Elasticsearch
L2 distanceFarther apart (smaller is better)Model trained with Euclidean distanceElasticsearch converts it into a score where closer is higher
Maximum inner productMore similarUnnormalized vectors from recommendation modelsScores can be unbounded in raw form
!
Common Mistake

Choosing dot product for vectors that were not normalized gives results that favour vectors with large lengths rather than similar meaning. If in doubt, use cosine, which handles the scaling for you.

4So, What Exactly Is kNN Search?

Now we can put the pieces together into a precise definition.

A precise definition

k-nearest neighbor search, written kNN, is the task of finding the k stored items whose vectors are closest to a given query vector, according to a chosen similarity measure. The letter k is simply how many results you want, such as the ten nearest neighbors. In Elasticsearch, the stored items are documents with a vector field, the query vector represents the user’s question, and the result is a ranked list of the k most similar documents.

The name comes from an old and simple idea in data science: to decide something about a new item, look at the items nearest to it. If you want to guess what kind of film someone will like, look at the films closest to the one they just enjoyed. That basic idea powers modern semantic search, recommendations and question answering.

Simple Analogy

You arrive at a new school and want to find classmates with similar hobbies. You stand in the playground and look at the ten students standing closest to you. If the playground is arranged so that people with similar interests stand together, those ten nearest students are your best matches. The playground arrangement is the embedding, and looking at the ten nearest is kNN.

graph TD
  Q["Query point"] --> N1["Neighbor 1: distance 0.10"]
  Q --> N2["Neighbor 2: distance 0.14"]
  Q --> N3["Neighbor 3: distance 0.19"]
  Q -.-> F1["Far point: distance 0.90"]
  Q -.-> F2["Far point: distance 1.10"]
        
FIG 3 — With k equal to 3, the three closest points are returned and the far ones are ignored

What kNN search is used for

Semantic text search

Finding documents that answer a question even when they share no words with it.

Recommendations

Suggesting products, articles or songs similar to something a user liked.

Image and multimedia similarity

Finding photos that look like a sample photo, or matching an image to a text description.

Retrieval for language model applications

Finding the most relevant passages to hand to a language model so it can answer with facts from your own data.

i
Remember This

kNN always returns k results, however poor the best matches are, unless you also set a similarity threshold. It answers “what is closest?” and not “what is truly relevant?” That difference matters when your data has no good answer for a query.

5Exact kNN and Why Brute Force Does Not Scale

The simplest way to find nearest neighbors is to check everything. It is accurate, and it is also slow at scale.

The brute-force method

Exact kNN, sometimes called brute force or flat search, compares the query vector with every stored vector, computes the similarity for each, and keeps the best k. There is no cleverness involved, which is its strength: the answer is always perfectly correct, and there is no index to build or tune. For small collections, or when a filter has already reduced the candidates to a few thousand documents, it is an excellent choice.

Why it breaks down

The work grows in direct proportion to the number of documents and the number of dimensions. Comparing one query against ten thousand vectors of 768 dimensions is quick. Comparing it against fifty million vectors means tens of billions of arithmetic operations for a single query, and that is far too slow if hundreds of users search at once. Reading all those vectors from memory or disk adds delay as well.

Number of vectorsDimensionsApproximate comparisons per queryPractical feel
10,000768About 7.7 millionFast, fine for exact search
1,000,000768About 770 millionNoticeably slow for interactive use
50,000,000768About 38 billionImpractical without an index
Simple Analogy

Finding the ten shirts most like your favourite by walking through a warehouse and holding every single shirt against yours is perfectly accurate. It is fine for a wardrobe of twenty shirts. It is hopeless for a warehouse of ten million.

When exact search is the right tool

Small collections, filters that leave only a modest set of candidates, evaluation runs that need ground truth, and workloads where perfect accuracy matters more than speed.

When you need something smarter

Collections of hundreds of thousands to billions of vectors, interactive latency targets of tens of milliseconds, and many simultaneous users.

i
A Useful Habit

Even if you use approximate search in production, keep an exact search around for testing. Comparing the two on sample queries tells you how much accuracy the approximation costs.

6Approximate Nearest Neighbors and the HNSW Graph

To go fast, we accept a tiny chance of missing the perfect answer in exchange for looking at only a small part of the data.

The bargain: approximate search

Approximate nearest neighbor search, or ANN, finds neighbors that are very likely, though not guaranteed, to be the true nearest ones. By building a special index structure in advance, it can find good answers while examining only a tiny fraction of the vectors. The measure of how often it finds the true nearest neighbors is called recall. A recall of ninety-five percent means that, on average, ninety-five of the true top hundred neighbors are found.

How HNSW works

Elasticsearch uses an algorithm called HNSW, short for Hierarchical Navigable Small World. It organises vectors into a graph, a network in which each vector is connected to a handful of its nearby friends. Searching the graph means starting somewhere, looking at the friends of the current point, stepping to whichever friend is closer to the query, and repeating until no closer friend can be found.

The hierarchical part adds layers. The top layer contains only a few vectors with long-distance links, like motorways. Lower layers contain more vectors with shorter links, like main roads, and the bottom layer contains every vector with local links, like streets. A search starts at the top, takes big strides toward the right neighborhood, then drops down a layer at a time, taking smaller and smaller steps until it reaches the streets around the target.

Simple Analogy

Suppose you want to reach a specific house in a foreign city. You do not check every house. You take a plane to the right region, then a motorway to the right town, then a main road to the right district, and finally walk the streets to the door. HNSW does the same thing with vectors, using fewer and fewer steps at each level.

graph TD
  L2["Layer 2 (few points, long links)"] --> L1["Layer 1 (more points, medium links)"]
  L1 --> L0["Layer 0 (all points, short links)"]
  L0 --> R["Closest neighbors found"]
  Q["Query vector"] --> L2
        
FIG 4 — HNSW searches from sparse upper layers down to the dense bottom layer

The two build-time knobs

Knob 1

m

How many neighbors each point is linked to. Higher values build a richer graph with better recall but more memory and slower indexing.

Knob 2

ef_construction

How many candidates are examined when linking each new point during indexing. Higher values improve graph quality but slow down indexing.

i
Good Defaults

The defaults for these knobs are chosen to work well for many datasets. Start with them, measure recall on your own data, and adjust only if you see a clear need.

7How Elasticsearch Stores and Searches Vector Graphs

The details of storage explain nearly every performance and memory question you will meet later.

One graph per segment

Elasticsearch stores each index in shards, and each shard is made of immutable pieces called segments. The HNSW graph is built separately for each segment. When new documents are written and a new segment is created, a new graph is built for it. When segments are merged in the background, the graphs of the merging segments are combined into a new graph for the merged segment, which is real work for the node.

This has two consequences. First, a search must visit the graph in every segment of every shard involved, so having many small segments makes searches slower. Second, indexing vectors is more expensive than indexing plain fields, because building and merging graphs takes processing time.

Vectors live outside the Java heap

The vectors and the graph structure are stored in files on disk and are read through the operating system’s file cache, not held in the Java heap. For fast searches, the parts of the graph that queries touch should be present in that cache, because reading them from disk for every hop would be slow. This is why memory planning for kNN is about leaving enough free memory outside the heap for the file cache, a point we will return to in the capacity planning chapters.

Simple Analogy

Each segment is like a small map booklet with its own road network. To find a place, you must look in every booklet. Merging booklets means drawing a fresh road network for the combined book, which takes effort. And the booklets should be open on your desk, the file cache, not locked in a distant cupboard, the disk.

graph LR
  subgraph Shard["One shard"]
    S1["Segment 1: vectors and HNSW graph"]
    S2["Segment 2: vectors and HNSW graph"]
    S3["Segment 3: vectors and HNSW graph"]
  end
  Q["kNN query"] --> S1
  Q --> S2
  Q --> S3
  S1 --> M["Merge best candidates"]
  S2 --> M
  S3 --> M
        
FIG 5 — A shard searches the graph in every segment, then merges the best candidates
Fact 1

Graphs are per segment

More segments mean more graphs to search and more merge work in the background.

Fact 2

Off-heap storage

Vectors and graphs are read through the operating system’s file cache rather than the Java heap.

Fact 3

Deletes are lazy

Updated or deleted documents leave stale entries until segments merge, which can slow searches slightly.

Fact 4

Indexing costs more

Building the graph makes vector indexing slower and more processor-hungry than ordinary fields.

!
Common Mistake

Giving almost all of a machine’s memory to the Java heap leaves nothing for the file cache, so vector searches keep reading from disk and become slow. For vector workloads, a moderate heap and a large free memory area for the cache is the healthier arrangement.

8Mapping a Vector Field: The dense_vector Type

Before you can search vectors, you must tell Elasticsearch which field holds them and how to treat them.

The field type

Vectors are stored in a field of type dense_vector. The word dense means that every position in the list holds a value, as opposed to sparse representations in which most positions are empty. You declare this in the index mapping, along with a few properties that control how the field behaves.

Setting 1

dims

The number of values in each vector. It must match your model exactly, and every document in the field must use it.

Setting 2

similarity

The metric used to compare vectors: cosine, dot product, L2 distance or maximum inner product.

Setting 3

index

Whether vectors are indexed into an HNSW graph for fast approximate search. Without it, only exact search is possible.

Setting 4

index_options

Choice of index structure and its parameters, such as graph type, the m and ef_construction knobs, and quantization.

Setting 5

element_type

How each number is stored, for example as a decimal number, a byte, or a single bit, trading precision for space.

Decisions you cannot easily undo

Several choices in the mapping are effectively permanent for an index. The number of dimensions cannot be changed once documents exist. The similarity metric is part of how the graph is built, so changing it means reindexing. Because of this, it is wise to test with a small sample of data and your chosen model before creating a large production index.

Simple Analogy

Choosing dims and similarity is like deciding the size and layout of a filing cabinet before putting any files in. If the drawers are the wrong size or organised by the wrong rule, you must move every file into a new cabinet.

DecisionCan it be changed later?How to change it
Number of dimensionsNo, not on an existing field with dataCreate a new index and reindex with new vectors
Similarity metricGenerally noCreate a new index and reindex
Index structure and quantizationLimited, depending on versionUsually requires reindexing into a new mapping
Embedding modelNot without regenerating all vectorsRe-embed all data and reindex
i
Practical Advice

Keep the name of the embedding model and its version in your index metadata or in a field of each document. Months later, when somebody asks why results look odd, this note is invaluable.

9Getting Embeddings into Elasticsearch

Elasticsearch stores and searches vectors, but the vectors have to come from somewhere. There are several routes.

Route one: create vectors outside and send them in

You can run an embedding model in your own application, or call an external service, and then index each document together with its vector. This gives you complete freedom over the model, and it is the approach used when you have a specialised or fine-tuned model. The responsibility for keeping the model, its version and its dimensions consistent lies with you.

Route two: let Elasticsearch call a model

Elasticsearch has an inference feature that connects to models, either hosted inside the cluster on machine learning nodes or provided by external services. An ingest pipeline can call the model for each incoming document and store the resulting vector automatically. At query time, the same model can convert the user’s text into a vector on the fly, so the application sends plain text and never handles vectors at all.

Route three: semantic fields

Newer versions offer a semantic text field type that handles much of the work automatically. It splits long text into chunks, creates embeddings for each chunk using a configured inference endpoint, and stores them for searching. This reduces the amount of code needed for the common case of semantic search over text.

RouteWho creates vectorsStrengthTrade-off
External applicationYour code or an outside serviceFull control over the modelYou maintain consistency and the pipeline
Ingest pipeline with inferenceElasticsearch calling a modelAutomatic embedding at index time and query timeModel load and latency on the cluster or a service
Semantic text fieldElasticsearch, with chunking handled for youLeast code for text searchLess low-level control; depends on version
Simple Analogy

Think of translating documents into a secret code so they can be filed on a special map. You can do the translation yourself, hire a translator who works in the office, or install a machine at the front door that translates everything that comes in. Whichever you choose, the same translator must handle both the documents and the visitors’ questions.

sequenceDiagram
  participant App as Application
  participant ES as Elasticsearch
  participant Model as Embedding model
  App->>ES: Index document text
  ES->>Model: Create embedding for the text
  Model-->>ES: Vector
  ES-->>App: Stored with vector
  App->>ES: Search with question text
  ES->>Model: Create embedding for the question
  Model-->>ES: Query vector
  ES-->>App: Nearest documents
        
FIG 6 — Inference lets the cluster embed both documents and questions with the same model
!
Chunk Long Text

Embedding models handle a limited amount of text at once. A whole book squeezed into one vector loses detail. Split long documents into sensible passages, embed each, and store them so that the best passage, not just the best document, can be found.

10Running a kNN Search: The Parts of the Request

A kNN search request has only a handful of parts. Understand each and you can read any example.

Part 1

field

The name of the dense_vector field to search.

Part 2

query_vector

The vector for the question, created by the same model as the stored vectors.

Part 3

query_vector_builder

An alternative to a ready-made vector: give text and a model, and let Elasticsearch create the vector.

Part 4

k

How many nearest neighbors to return overall.

Part 5

num_candidates

How many candidates each shard examines before the best k are chosen. Larger values raise accuracy and cost.

Part 6

filter

An optional condition that documents must meet, applied while the graph is searched.

Part 7

similarity

An optional minimum similarity a result must reach to count as a match, in the units of the chosen metric.

Reading a request in words

In plain language, a typical request says: “In the field called description_vector, find the ten documents closest to this query vector, considering fifty candidates on each shard, and only among documents whose category is shoes.” The answer comes back in the usual search response format, with the hits ordered by score, and each hit carrying its document source. You can request only some fields, add highlighting to other parts of a combined request, and use all the familiar options for paging and sorting.

Simple Analogy

It is like asking a shop assistant: “From the shoes department only, show me the ten items most like this photo. Look through at least fifty of the closest-looking ones on each shelf before you decide.” Each part of the request matches a part of that sentence.

A knn option and a knn query

Elasticsearch offers kNN as a top-level option of the search request and also as a query type that can be placed inside the query section. The top-level option is the classic route and is well suited to standalone kNN or to combining kNN with ordinary queries. The query form makes it possible to use kNN inside compound queries and other places where queries are accepted. Both use the same underlying graph search.

i
Version Note

Names and availability of some request forms vary between versions. When following an example from the web, check that the version matches yours, and consult the documentation for the exact syntax supported by your cluster.

11How a kNN Search Runs Across the Cluster

Following one request through the cluster shows where time and memory are spent.

1

The request arrives

A coordinating node receives the request. If a model is configured to embed the text, it first creates the query vector.

2

The query goes to every relevant shard

Each shard searches its own data, so the request is sent to one copy of each shard of the target indices.

3

Each shard searches its segments

Inside a shard, the graph of each segment is searched, applying any filter as it goes, and the best candidates from all segments are combined.

4

Each shard returns its top candidates

A shard returns up to the requested number of candidates, along with their similarity scores.

5

The coordinating node picks the global best

Candidates from all shards are merged and the best k overall are selected and ordered by score.

6

Documents are fetched and returned

The winners’ documents are fetched from their shards and sent back to the client in the usual response format.

sequenceDiagram
  participant C as Client
  participant N as Coordinating node
  participant S1 as Shard 1
  participant S2 as Shard 2
  C->>N: kNN request (k=10, num_candidates=50)
  N->>S1: Search graphs, return top candidates
  N->>S2: Search graphs, return top candidates
  S1-->>N: 50 candidates with scores
  S2-->>N: 50 candidates with scores
  N->>N: Merge 100 candidates, choose best 10
  N->>S1: Fetch documents
  N->>S2: Fetch documents
  N-->>C: Top 10 hits
        
FIG 7 — Each shard contributes candidates and the coordinating node chooses the global winners
Simple Analogy

Imagine a national talent contest with regional heats. Each region sends its fifty best performers to the final round, where judges pick the overall top ten. The more regions there are, the more performers reach the final, and the more the final round costs.

i
Why Shard Count Matters

Because every shard is searched and each returns its own candidates, the total work grows with the number of shards. Vector indices often perform best with fewer, larger shards, within the limits of your resilience and recovery needs.

12k Versus num_candidates: The Speed and Accuracy Dial

Two numbers in every request quietly control the balance between fast answers and accurate answers.

What each number does

The number k is the size of the final answer: how many results the user will receive. The number num_candidates is how many nearest neighbor candidates each shard considers internally before it hands its best ones to the coordinating node. Candidates are the pool from which the answer is picked. A larger pool means the search explores more of the graph and is more likely to include the true nearest neighbors, but it also visits more vectors and therefore takes longer.

The rule is that num_candidates must be at least as large as k, and there is an upper limit on how large it can be. In practice, a value several times larger than k is common. Raising it is the simplest lever for improving recall when results look poor, and lowering it is the simplest lever for improving speed when latency is too high.

Simple Analogy

Imagine picking the three best cakes at a fair. If you only taste the first three you see, you might miss the best one. If you taste twenty cakes, you are far more likely to find the true winners, but tasting takes longer. k is how many prizes you give. num_candidates is how many cakes you taste.

ChangeEffect on accuracyEffect on speedWhen to use
Raise num_candidatesHigher recallSlower, more vectors visitedResults miss obviously relevant items
Lower num_candidatesLower recallFasterLatency is too high and recall is comfortably good
Raise kMore results returnedSlightly slowerNeed more results for paging or reranking
Raise the number of shardsMore candidates overallMore work per queryOnly for capacity, not for accuracy tuning

A tuning recipe

1

Collect representative queries

Gather a few hundred real or realistic queries that reflect how users will search.

2

Compute ground truth

Run exact search for each query to find the true nearest neighbors.

3

Sweep values

Run approximate search with several values of num_candidates, measuring recall against the ground truth and latency at each value.

4

Pick the knee of the curve

Choose the smallest value that gives recall you can accept. Beyond that point, extra cost buys very little.

i
Diminishing Returns

Recall usually rises quickly at first, then flattens. Doubling num_candidates from a low value may help a lot, while doubling it from an already high value may change almost nothing but the bill.

13Filtering: Combining Similarity with Conditions

Real applications almost never want “the nearest thing overall.” They want the nearest thing that also meets some condition.

Why filtering is tricky in vector search

Suppose a shopper wants shoes similar to a photo, but only in their size and only in stock. There are two obvious ways to combine similarity with conditions. One is post-filtering: find the nearest neighbors first, then throw away those that fail the condition. The trouble is that you may throw away most or all of them and end up with too few results, or none.

The other is pre-filtering: apply the condition first, and find the nearest neighbors only among the documents that pass. This guarantees that the requested number of matching results are returned, as long as enough documents match. Elasticsearch supports this by letting you place a filter inside the kNN search, so the graph search itself respects the condition as it walks.

Simple Analogy

Post-filtering is like asking for the ten prettiest flowers in a garden and only afterwards noticing that nine of them are the wrong colour. Pre-filtering is like first walking only along the row of red flowers and then choosing the ten prettiest ones there.

flowchart LR
  subgraph Post["Post-filtering: risky"]
    A1["Find 10 nearest"] --> A2["Remove non-matching"] --> A3["Maybe only 2 remain"]
  end
  subgraph Pre["Filter inside the kNN search: safer"]
    B1["Search graph among matching documents only"] --> B2["Return 10 matching neighbors"]
  end
        
FIG 8 — Filtering during the search returns the full number of matching results

What happens behind the scenes

While walking the graph, the search simply ignores documents that do not pass the filter when collecting results, though it may still pass through them on the way. If the filter is extremely restrictive and matches only a small number of documents, walking the graph can become less efficient than simply checking those few documents directly, and the engine may fall back to an exact comparison over them. This is often a pleasant surprise: highly selective filters can make searches both faster and perfectly accurate.

Pattern 1

Filter inside kNN

Use for conditions that must hold, such as stock status, language, tenant or access rights.

Pattern 2

Post filter

Use only when you deliberately want filtering to affect other parts of a combined request, such as facet counts, without changing which neighbors are found.

Pattern 3

Separate indices per tenant

For strong isolation and very small filtered sets, separate indices or routing can be simpler than a filter.

!
Security Filters Belong Inside

If a condition protects information, such as which tenant may see which document, do not rely on post-filtering it away. Use proper access controls, and apply tenant conditions inside the search so that documents of other tenants never enter the candidate pool.

14Scores and Similarity Thresholds

Elasticsearch ranks all results by a relevance score, so raw similarity values are converted into scores that are always positive.

From similarity to score

Elasticsearch requires scores to be non-negative, and higher scores mean better matches. For cosine similarity and dot product, the raw value can be negative, so it is shifted and scaled: one is added to the similarity and the result is halved, mapping the range from minus one to plus one into the range from zero to one. For L2 distance, the score is one divided by one plus the squared distance, so a distance of zero gives a perfect score of one and greater distances give smaller scores.

These conversions keep the ordering the same as the raw metric. The closest vector always has the highest score. What changes is the scale, which matters when you combine kNN scores with scores from ordinary queries.

MetricRaw value rangeHow it becomes a score
CosineMinus 1 to 1One plus the similarity, divided by two
Dot product (unit vectors)Minus 1 to 1One plus the similarity, divided by two
L2 distance0 upwardOne divided by one plus the squared distance
Maximum inner productUnboundedRescaled to stay non-negative

The similarity threshold

Because kNN always returns the k closest items, you might receive poor matches when nothing good exists. The similarity option lets you set a minimum quality: documents less similar than the threshold are not counted as matches, even if they are among the k nearest. Note that the threshold is expressed in the raw units of the metric, not in the converted score. For cosine similarity it is a cosine value between minus one and one; for L2 distance it is a maximum distance.

Simple Analogy

If you ask a friend for the three restaurants nearest to you, they will always name three, even if the nearest is a two-hour drive away. Adding “but only if it is within ten minutes” is the similarity threshold. Sometimes the honest answer is “nothing close enough.”

0 to 1
typical range of a converted kNN score
Raw
units used by the similarity threshold
k
results always returned unless a threshold cuts them
i
Calibrate on Your Data

Similarity values are not universal. A cosine of 0.8 may mean “very related” for one model and “loosely related” for another. Look at real examples from your data and choose thresholds by inspection, not by guesswork.

15Hybrid Search: Combining kNN with Keyword Search

Meaning and words each catch things the other misses, so the best systems use both.

Why hybrid?

Semantic search is excellent at finding paraphrases and related ideas, but it can stumble on exact terms such as product codes, names, error numbers and rare jargon, which an embedding model may blur. Lexical search nails exact terms but misses paraphrases. Hybrid search runs both and combines the results, so that a document that ranks well on either measure has a chance to appear, and documents strong on both rise to the top.

Ways to combine

The simplest method is to include both a kNN section and a normal query in one request. Elasticsearch adds the scores together, and each part can be given a boost to control how much it matters. Because the two scores are on different scales, tuning boosts takes some experimentation. A more robust method is reciprocal rank fusion, which ignores raw scores and combines results using only their positions in each ranked list: an item near the top of either list receives a good combined rank. Retrievers, a newer request style, let you describe such combinations clearly and even chain reranking steps.

Simple Analogy

Two judges score a talent show. One judge is strict about technical skill, the other about creativity. Adding their scores, or combining their rankings, gives a fairer winner than trusting either alone. A performer who impresses both judges beats one who impresses only one.

graph TD
  Q["User question"] --> L["Keyword query on text fields"]
  Q --> E["Embedding of the question"]
  E --> K["kNN search on vector field"]
  L --> F["Combine: weighted scores or rank fusion"]
  K --> F
  F --> R["Final ranked results"]
        
FIG 9 — Hybrid search runs keyword and vector retrieval, then fuses the rankings
Combination methodHow it worksStrengthWatch out for
Weighted score sumAdds the keyword score and the kNN score, each with a boostSimple and directScores differ in scale, so boosts need tuning
Reciprocal rank fusionCombines based on rank positions from each listNo score calibration neededIgnores how big the score gaps are; availability depends on version and subscription
Rerank after retrievalA second model reorders the merged candidatesCan greatly improve top resultsAdds latency and cost per query

When keywords must win

Searching for an order number or an error code should rely mainly on exact matches, so give the keyword part more weight or a strict filter.

When meaning should lead

Natural-language questions such as “how do I reset my password when I lost my phone” benefit from semantic retrieval, with keywords as a supporting signal.

i
Start Hybrid

If you already have a good keyword search, adding kNN as a second retriever is usually a smaller risk than replacing your search entirely. Measure the change on real queries and increase the weight of the vector part as evidence supports it.

16Exact kNN with Scripted Scoring

Sometimes brute force is exactly what you want, and Elasticsearch supports it directly.

How it works

Besides the graph-based search, Elasticsearch can compute vector similarity inside a scripted scoring query. A normal query first selects a set of documents, and then a script computes the similarity between the query vector and each selected document’s vector, using built-in functions for cosine similarity, dot product and distances. Because every selected document is scored, the result is exact.

This approach does not need the graph index at all, so it works with vector fields that are not indexed for approximate search. Its cost is proportional to the number of documents scored, which means it is only sensible when the pre-selected set is modest, such as a few thousand documents narrowed down by a strong filter.

Simple Analogy

Choosing the best-fitting shoe from the five pairs on your shelf, by trying each on, is perfectly reasonable. Trying on every shoe in the city is not. The strong filter is what reduces the whole city to five pairs.

AspectApproximate kNN (graph)Exact kNN (scripted scoring)
AccuracyHigh but not perfectPerfect for the documents scored
Speed on large dataFastSlow, grows with documents scored
Needs a graph indexYesNo
Best forLarge collectionsSmall or heavily filtered sets, and ground-truth testing
Flexibility of scoringStandard similarityCustom formulas mixing several signals
!
Do Not Score Everything

A scripted similarity applied to a whole large index, with no filter, is one of the quickest ways to make a cluster crawl. Always confirm that the query narrows the documents before the script runs.

17Chunking and Multiple Vectors per Document

Real documents are long, and one vector rarely captures a whole long document well.

Why chunking helps

An embedding model compresses text into a fixed-size vector. For a short sentence, the vector can be rich and precise. For a long report, the single vector is a blurry average of many topics, and a question about one detail may not match it well. The remedy is chunking: split the document into passages of a sensible size, embed each passage, and search over passages.

Good chunks respect natural boundaries such as paragraphs and sections, are small enough for the model to represent faithfully, and often overlap slightly so that an idea split across a boundary is still captured whole in at least one chunk. Each chunk should keep a reference to its parent document, so that results can be grouped and displayed properly.

Ways to store chunks

Option 1

One document per chunk

Each chunk is its own Elasticsearch document with a parent identifier. Simple, and it scales well, but results must be grouped by parent.

Option 2

Nested chunks

Chunks are stored as nested objects inside the parent document, each with its own vector. The best chunk decides the parent’s rank, and matching chunks can be returned.

Option 3

Semantic field handling

A semantic text field chunks and embeds for you, hiding much of the bookkeeping, where your version supports it.

Simple Analogy

A single sentence summarising an entire textbook is too vague to help you find a formula. Indexing every page separately, and remembering which book each page came from, lets you jump straight to the right page. Chunking is indexing by page instead of by book.

Question answering over manuals

Each manual is split into short sections. The user’s question retrieves the best three sections, which are shown with a link back to the whole manual.

Products with several photos

Each product has several image vectors. A search by photo matches the closest image of any product and ranks the product by its best match.

i
Experiment with Sizes

There is no single best chunk size. Try a few, measure retrieval quality on real questions, and choose the size that finds the right passage most often without splitting ideas.

18Shrinking Vectors: Quantization and Memory Savings

Vectors are big. Quantization is the main tool for making them smaller without ruining search quality.

The size of a vector

A typical vector stores each number as a four-byte decimal value. A vector with 768 dimensions therefore occupies about three thousand bytes, roughly three kilobytes. A collection of ten million such vectors holds around thirty gigabytes of raw vector data before counting the graph. To search quickly, the hot parts of that data should sit in the file cache, so large collections demand large amounts of memory.

What quantization does

Quantization stores each number with less precision, using fewer bits. Instead of a four-byte decimal, a number might be squeezed into a single byte, a half byte or even a single bit. Comparing the compressed vectors is much faster and needs far less memory, at the cost of slightly less precise similarity values. Elasticsearch typically keeps the original full-precision vectors on disk too, so that a final rescoring step can correct the small errors for the top candidates.

Simple Analogy

A photo taken at full resolution looks perfect but takes lots of space. A smaller version is nearly as recognisable and loads instantly. You could use the small copies to find likely matches quickly and only open the full-size originals for the final few. Quantization does the same with vectors.

Storage styleBytes per number (approximate)Memory saving for hot dataAccuracy impact
Full-precision float4NoneHighest
8-bit integer quantization1About four times smallerSmall, often negligible with rescoring
4-bit integer quantization0.5About eight times smallerNoticeable on some data, often acceptable
Binary style quantization0.125Up to about thirty-two times smallerLarger, best with rescoring and suitable models
~4x
memory saving from 8-bit quantization
~8x
memory saving from 4-bit quantization
~32x
saving from binary style compression
!
Disk Versus Memory

Quantization reduces the memory needed to keep the hot search data cached, but the full-precision originals are usually still stored on disk, so total disk use can even grow slightly. Judge the saving by memory footprint and query speed, not by index size alone.

i
Test Before Committing

The accuracy cost of quantization depends on your model and data. Compare recall with and without it on your own queries. Availability and defaults of the various quantization options depend on your Elasticsearch version.

19Capacity Planning: A Worked Memory Example

A little arithmetic tells you whether a plan is realistic before you buy any machines.

The scenario

A company wants semantic search over ten million product descriptions. Their embedding model produces 768-dimensional vectors, and each vector is stored as four-byte decimals. They want fast searches, meaning the vectors should live in the file cache. Let us estimate what that requires.

1

Raw vector size

Ten million vectors, times 768 numbers, times four bytes, is about thirty-one billion bytes, roughly thirty gigabytes.

2

Add the graph

Each point stores links to its neighbors, adding a modest overhead that grows with the m setting. As a rough guide, allow a small extra percentage on top of the raw size.

3

Account for replicas

With one replica, the index exists twice across the cluster, so total memory across nodes roughly doubles, although each node only needs to cache its own share.

4

Apply quantization

With 8-bit quantization, the hot data to cache falls to roughly a quarter, about eight gigabytes for the compressed vectors, plus the graph, with the full-precision copies on disk.

5

Spread across nodes

Divide the hot data among the data nodes, leaving room for the heap, the operating system and everything else the nodes do. Confirm each node’s free memory exceeds its share.

~30 GB
raw full-precision vectors for ten million documents
~8 GB
approximate hot data with 8-bit quantization
x2
total footprint with one replica across the cluster
Simple Analogy

Planning a school trip, you count the students, add the teachers, and think about how many buses are needed. Planning kNN memory is the same: count the vectors, multiply by their size, add overheads, and check that your machines have enough seats for the popular data.

i
Treat Numbers as Estimates

These figures are estimates. Real sizes depend on your settings and version. The reliable method is to index a representative sample, read the actual disk usage of the vector field with the disk usage analysis tool, and scale up carefully with a safety margin.

20Indexing Performance: Building Graphs Efficiently

Loading millions of vectors takes planning. A few habits make it much smoother.

Why indexing is slower

Each new vector must be linked into the graph of its segment by searching for its best neighbors, which is real computation. Later, when segments merge, graphs must be rebuilt or combined. Creating embeddings, if you do it during ingestion, adds further time because a model must run for every document. On big loads, these costs add up to hours rather than minutes.

Practical tips

Tip 1

Bulk in reasonable batches

Send documents in bulk requests of moderate size, and keep several requests in flight, watching for rejections.

Tip 2

Slow down refreshes during loads

A longer refresh interval means fewer tiny segments and fewer graphs to build and merge. Restore it afterward.

Tip 3

Use replicas later

For a big initial load, indexing with no replicas and adding them afterwards avoids doing the graph work twice.

Tip 4

Embed ahead of time

Creating vectors in a separate step, perhaps on machines with graphics accelerators, keeps model work off your search cluster.

Tip 5

Merge when the data is settled

After loading a stable index, merging segments down improves search speed, since fewer graphs need visiting.

Tip 6

Mind the build knobs

Higher graph quality settings improve recall but slow indexing. Choose the lowest quality that meets your recall target.

Simple Analogy

Building a road network takes longer than piling houses in a field. If you keep opening new tiny villages every second, each needing its own roads, and then redo the roads whenever villages merge, you spend all your time building. Fewer, larger settlements built in bulk are cheaper.

ANTI-PATTERN-01 Avoid
Problem

Updating vectors frequently, such as re-embedding and rewriting every document daily, on a very large index.

Why It’s Harmful

Each update is a delete plus a new insert, leaving stale entries and forcing continual graph building and merging. Indexing load and search latency both suffer.

Correct Approach

Update only what changed, batch updates into off-peak windows, and consider building a fresh index and switching an alias when a whole collection must be re-embedded.

21Query Performance: Keeping Searches Fast

Fast vector search comes from a few well-understood conditions. Check them one by one when latency disappoints.

1

Is the data cached?

If the vector and graph files are not in the file cache, searches read from disk and slow down dramatically. Check free memory and consider quantization or more nodes.

2

How many segments are there?

Many small segments mean many graphs to visit. Merge when data is stable and avoid needlessly frequent refreshes.

3

How many shards are involved?

Each shard adds work and candidates. Prefer fewer, larger shards where resilience allows.

4

Is num_candidates larger than needed?

Reduce it until recall starts to fall, then step back up a little.

5

Are filters helping?

Selective filters shrink the search space. Check that filter fields are indexed appropriately and that filters are inside the kNN search.

6

Is embedding at query time slow?

If the model call is the bottleneck, the graph search is innocent. Cache frequent query embeddings and size the model service properly.

flowchart TD
  A["Search is slow"] --> B{"Is the time spent creating the query vector?"}
  B -->|"Yes"| C["Cache embeddings, scale the model service"]
  B -->|"No"| D{"Are vector files in the file cache?"}
  D -->|"No"| E["Add memory, use quantization, reduce data per node"]
  D -->|"Yes"| F{"Many segments or shards?"}
  F -->|"Yes"| G["Merge segments, reduce shard count"]
  F -->|"No"| H["Lower num_candidates or tighten filters"]
        
FIG 10 — A quick triage path for slow kNN searches
Simple Analogy

If a library assistant is slow, ask why. Is she waiting for the translator, walking to a distant storeroom for every book, visiting too many small rooms, or checking too many books before answering? Each cause has a different cure, so find the real one first.

i
Measure the Whole Path

Users experience total time, including creating the query vector, network hops and fetching documents, not only the graph search. Measure each stage separately so that you improve the stage that actually dominates.

22Measuring Quality: Recall and Relevance

A fast search that returns the wrong things is worthless. Quality needs to be measured, not assumed.

Two kinds of quality

The first kind is recall of the approximation: does the fast approximate search find the same neighbors that an exact search would? This measures the cost of the shortcut and can be computed automatically by comparing the two on sample queries. The second kind is relevance: are the nearest neighbors actually useful to the user? This depends on the embedding model, the chunking, the filters and the hybrid combination, and it must be judged by people or by labelled examples.

Quality 1

Recall at k

The share of the true top k neighbors that the approximate search returns.

Quality 2

Relevance judgments

Labelled pairs of queries and good answers, created by people, against which results are scored.

Quality 3

Ranking measures

Numbers that reward putting good answers near the top, such as how high the first correct result appears.

Quality 4

Online signals

Clicks, purchases, follow-up searches and feedback from real users after launch.

Simple Analogy

A treasure hunter with a fast metal detector might miss a few coins compared with digging up the whole beach. That is recall. But even if the detector finds every coin, they still need to check whether the coins are valuable. That is relevance. You need both.

A simple evaluation routine

1

Build a query set

Collect real user questions, or write realistic ones, covering easy, hard and unusual cases.

2

Label good answers

Have people mark which documents should be found for each query, even if only for a sample.

3

Score each configuration

Run the query set through each candidate setup, varying model, chunk size, hybrid weights and quantization, and record the scores.

4

Choose and keep testing

Select the best trade-off between quality, cost and speed, and re-run the tests whenever the model, data or settings change.

!
Do Not Judge by Three Examples

A handful of impressive demo queries proves little. Semantic search often shines on the queries a team tries first and struggles on the odd ones real users type. Evaluate on a broad, honest sample.

23Dense Vectors Versus Sparse Vectors

Elasticsearch supports two families of vector-based search, and it helps to know how they differ.

Dense vectors

Everything so far has used dense vectors, in which every position holds a number and the whole vector describes the overall meaning of the text. kNN search operates on dense vectors. They are compact and excellent at capturing broad semantic similarity, but individual numbers have no human-readable meaning, so it is hard to explain why two items matched.

Sparse vectors

A sparse vector has a huge number of possible positions, but only a few are filled. Each position usually corresponds to a word or word-like term, and the value says how important that term is to the text. Learned sparse models expand a text with related terms, so a document about “laptops” might also carry weights for “notebook” and “computer.” Sparse vectors are searched using the inverted index machinery rather than graph-based kNN, and they retain some of the explainability of keyword search, since you can see which terms contributed.

Simple Analogy

A dense vector is like describing a person with a paragraph of general impressions: friendly, energetic, artistic. A sparse vector is like a name tag listing a few key words: chess, violin, Tuesday. The paragraph captures the overall feel, while the tag is easy to read and check.

AspectDense vectors with kNNSparse vectors
ShapeFixed length, all positions filledVery long, mostly empty
Search structureHNSW graph or exact comparisonInverted index with term weights
ExplainabilityLow, numbers are opaqueHigher, contributing terms can be seen
StrengthBroad semantic similarity, images and other mediaText relevance with term expansion
Typical memory patternLarge vectors held in the file cacheSimilar to a normal inverted index
i
Use Them Together

Because the two approaches make different kinds of mistakes, combining dense retrieval, sparse retrieval and plain keyword matching in one hybrid search often gives the best results for text. Images and other non-text data rely on dense vectors.

24Security and Privacy Considerations

Vectors are derived from your data, so the usual rules of data protection apply, with a few extra points.

Access control still applies

Role-based access, document-level security and field-level security work with vector searches just as they do with ordinary searches. A user restricted to certain documents can only receive neighbors among those documents. Make sure that any restriction is enforced by the security features or by filters inside the kNN search, not by hiding results in the user interface.

Vectors are not anonymous

It is tempting to think that a list of numbers cannot reveal the original text. Research has shown that, in some circumstances, information about the source text can be partially recovered from embeddings. Treat vectors that come from sensitive content as sensitive too, and protect them with the same access rules, encryption and retention policies as the source documents.

Model and service credentials

When Elasticsearch calls an external model service, it needs credentials such as keys. Store them using the secure mechanisms provided, restrict who can create or change inference endpoints, and rotate keys regularly. Remember also that text sent to an external service leaves your cluster, which may matter for privacy and compliance rules.

Control 1

Document and field security

Restrict which documents and fields each role can search and retrieve.

Control 2

Filters for tenants

Apply tenant conditions inside the kNN search so other tenants’ data never enters the candidate pool.

Control 3

Encryption

Encrypt data in transit and at rest, since vectors and source text are stored on disk.

Control 4

Secrets management

Keep model service keys in secure storage and limit who can manage inference endpoints.

Control 5

Data-sharing review

Check what text leaves your environment when calling external embedding services.

Control 6

Deletion and retention

When a source document must be deleted, delete its vectors and chunks too, so nothing derived from it lingers.

Simple Analogy

A photocopy of a secret letter, even if the handwriting is smudged, is still a secret. Vectors are a kind of smudged copy of your documents. They deserve the same lock as the originals.

ANTI-PATTERN-02 Avoid
Problem

Applying access checks only after retrieval, in application code, to results that were gathered from all tenants or all confidentiality levels.

Why It’s Harmful

A bug or a missed check leaks other people’s documents. It also causes too few results, since many of the k neighbors are discarded.

Correct Approach

Enforce access at the search layer with document-level security or mandatory filters inside the kNN search.

25Monitoring and Metrics

Vector workloads have their own warning signs. Watch them and you will catch trouble early.

Metric 1

Search latency percentiles

Track the median and the slowest few percent. Tail latency often reveals cache misses first.

Metric 2

File cache health

Disk read rates and free memory show whether vector files are being served from memory or repeatedly read from disk.

Metric 3

Segment counts and merge activity

Many segments or constant merging point to indexing settings that need attention.

Metric 4

Vector storage size

Per-field disk usage shows how much the vectors and graphs cost, and how it grows with data.

Metric 5

Indexing throughput and rejections

Rejected bulk requests mean graph building is saturating the nodes.

Metric 6

Embedding service latency

Time spent creating vectors, at index time and at query time, and any errors from the model service.

Metric 7

Recall and relevance scores

Automated periodic checks against a labelled query set, to catch quality drops after changes.

Where to look

Node and index statistics give search and indexing rates and times, segment counts and merge activity. The disk usage analysis tool reports how much space each field, including vector fields, consumes. Operating system metrics reveal file cache behaviour and disk reads. Slow logs highlight the slowest searches. Your application should also log the time of each stage: creating the query vector, running the search and post-processing.

Cache
disk reads rising means vectors fell out of memory
Merges
constant merging suggests too many small segments
Recall
regular quality checks catch silent degradation
i
Alert on Causes

Alert on rising disk reads, growing segment counts and slow embedding calls, which are causes you can act on, in addition to alerting on slow user-facing searches, which are the symptoms.

26A Worked Example: Semantic Help-Centre Search

Let us follow a realistic project from idea to launch to see how the pieces fit together.

The scenario

A software company has a help centre with fifty thousand articles. Customers search with questions such as “why can’t I log in after changing my phone,” but the articles use titles like “Two-factor authentication device reset.” Keyword search returns poor results because few words overlap. The team decides to add semantic search with kNN and combine it with their existing keyword search.

1

Split the articles into chunks

Each article is divided into passages of a few paragraphs, with slight overlap. Each passage keeps its article identifier, title and language.

2

Choose the model and mapping

They select a text embedding model with a moderate number of dimensions, set the similarity metric recommended for it, and record the model name in the index metadata.

3

Index text and vectors

An ingest pipeline creates embeddings through an inference endpoint. The initial load runs with no replicas and a long refresh interval, then settings are restored.

4

Build a hybrid search

Each query runs a keyword search and a kNN search with a language filter, then merges the rankings. The kNN part uses a moderate number of candidates.

5

Evaluate on real questions

They collect two hundred real customer questions, label the right articles, and compare keyword-only, kNN-only and hybrid setups. Hybrid wins clearly.

6

Tune and launch

They raise the number of candidates until recall plateaus, merge segments after the load, set up dashboards and alerts, and launch to ten percent of users before rolling out fully.

graph TD
  A["Help articles"] --> B["Split into overlapping chunks"]
  B --> C["Ingest pipeline creates embeddings"]
  C --> D["Index: text fields and vector field"]
  Q["Customer question"] --> E["Embed the question"]
  E --> F["kNN search with language filter"]
  Q --> G["Keyword search"]
  D --> F
  D --> G
  F --> H["Fuse rankings"]
  G --> H
  H --> I["Show best passages and article links"]
        
FIG 11 — A complete semantic help-centre search pipeline
50,000
articles split into many passages
3 modes
compared: keyword, kNN and hybrid
10%
of users receive the first rollout
i
What Made It Work

The team did not choose kNN blindly. They chunked sensibly, kept keyword search as a partner, measured on real questions, and rolled out gradually. The technology mattered, but the discipline around it mattered more.

27Design Patterns That Work Well

These habits have proved themselves across many production systems.

Hybrid by default for text

Run keyword and vector retrieval together and fuse the results. This protects you from both the blind spots of embeddings and the blind spots of exact matching.

Filter inside the search

Put must-hold conditions such as language, tenant, availability and permissions inside the kNN search so that results are complete and safe.

Chunk with care and keep parent links

Store passages with references to their parent documents so that results can be grouped, displayed and cited properly.

Version the embedding model

Record model name and version, and when changing models, build a new index and switch an alias rather than mixing vectors.

Quantize when memory is the constraint

Use compressed vectors with rescoring to fit large collections in memory, after verifying recall on your data.

Keep an exact-search yardstick

Maintain a small exact-search test harness to measure recall whenever settings, versions or data change.

Separate embedding from serving

Run heavy model inference on dedicated resources so that vector creation does not compete with search on your data nodes.

Rerank only the best few

When a more accurate but slower reranking model is available, apply it only to the top candidates from retrieval, keeping latency in check.

“Retrieve widely with cheap methods, then judge narrowly with expensive ones.”

28Anti-Patterns to Avoid

Recognise these mistakes early, and you will avoid the most common kNN disappointments.

ANTI-PATTERN-03 Avoid
Problem

Mixing vectors from different models, or from different versions of a model, in the same field.

Why It’s Harmful

Vectors from different models are not comparable. Distances between them are meaningless, and results look random in ways that are very hard to diagnose.

Correct Approach

Use one model per field. When switching models, create a new index and reindex everything with the new vectors.

ANTI-PATTERN-04 Avoid
Problem

Dropping vector search into production after trying a few demo queries.

Why It’s Harmful

Demos flatter semantic search. Real queries include product codes, typos, short fragments and odd phrasing, where embeddings can underperform keyword matching.

Correct Approach

Evaluate on a broad, honest query set, and keep keyword search as a partner in a hybrid design.

ANTI-PATTERN-05 Avoid
Problem

Starving the file cache by giving nearly all memory to the Java heap.

Why It’s Harmful

Vectors and graphs are read through the file cache. Without room there, every search reads from disk, and latency rises sharply.

Correct Approach

Use a moderate heap, leave plenty of memory for the operating system, and consider quantization to shrink the hot data.

ANTI-PATTERN-06 Avoid
Problem

Embedding whole long documents as single vectors.

Why It’s Harmful

One vector becomes a blurry average of many topics, and specific questions fail to match well. Models also have input limits, and text beyond them may be ignored.

Correct Approach

Chunk documents into passages, embed each, and keep a link to the parent.

ANTI-PATTERN-07 Avoid
Problem

Choosing a similarity metric that does not match the model, such as dot product on unnormalized vectors from a cosine-trained model.

Why It’s Harmful

Ranking is skewed toward long vectors rather than related meaning, and quality suffers without any error message.

Correct Approach

Follow the model’s documentation, and if unsure, use cosine similarity.

ANTI-PATTERN-08 Avoid
Problem

Post-filtering results after retrieving only the top k neighbors.

Why It’s Harmful

Most of the k may be discarded, leaving few or no results, and unfiltered data has already entered the pipeline, which is dangerous for access control.

Correct Approach

Place the filter inside the kNN search so that the neighbors are chosen among matching documents only.

ANTI-PATTERN-09 Avoid
Problem

Setting num_candidates extremely high everywhere to be “safe.”

Why It’s Harmful

It multiplies work on every shard for very small gains in recall, raising latency and cost for all users.

Correct Approach

Measure recall against exact search and choose the smallest value that meets your target.

29Advantages, Disadvantages and Trade-offs

Every technology is a bargain. Here is what kNN search gives and what it costs.

Advantages

  • Finds results by meaning, matching paraphrases and related ideas that share no words
  • Works for images, audio and other data once they are embedded
  • Integrates with the rest of Elasticsearch: filters, security, aggregations and hybrid ranking
  • Approximate search is fast even on very large collections
  • Quantization options make large collections affordable
  • Powers retrieval for language model applications

Disadvantages / Trade-offs

  • Needs an embedding model, adding cost, latency and a component to maintain
  • Approximate search can miss the true nearest neighbors
  • Vectors are memory-hungry and need a well-provisioned file cache
  • Results are hard to explain, since vector numbers have no readable meaning
  • Changing the model means re-embedding and reindexing everything
  • Weaker than keywords on exact codes, names and rare terms

Keyword search and kNN search side by side

AspectKeyword (lexical) searchkNN (semantic) search
What it matchesWords in the textMeaning represented by vectors
Exact codes and namesExcellentOften weak
Paraphrases and synonymsNeeds manual effortHandled naturally
ExplainabilityHigh, matched terms are visibleLow
Extra componentsNone beyond analyzersEmbedding model and vector storage
Memory patternModestLarge, off-heap file cache
Best usedAs the backbone of most searchAlongside keywords, for semantic reach
i
How to Think About It

kNN is not a better keyword search. It is a different lens. Use the lens that fits the question, or combine both, and be honest about the costs of extra machinery.

30Real-World and Industry Examples

Here is how organisations in different fields put nearest neighbor search to work.

E-commerce discovery

Shoppers describe what they want in natural language or upload a photo, and the store finds visually or conceptually similar products. Filters for size, price and stock keep results purchasable.

Customer support and knowledge bases

Support portals retrieve the best answer passages for a customer’s question, even when it uses different words from the articles, reducing tickets and speeding up agents.

Retrieval for language model assistants

An assistant retrieves the most relevant passages from company documents and hands them to a language model, so answers are grounded in current, private information instead of guesses.

Media and content recommendation

Streaming and publishing platforms suggest films, songs or articles similar to what a person just enjoyed, using embeddings of content or of listening behaviour.

Duplicate and near-duplicate detection

Teams find documents, images or support tickets that are almost the same, to merge duplicates, detect plagiarism or group similar incidents.

Security and anomaly analysis

Analysts search for events or files whose embeddings are near a known suspicious example, or far from anything normal, to spot unusual behaviour.

Legal and research discovery

Researchers and lawyers find passages on a concept across huge collections of papers and contracts without guessing the exact phrases used.

The lesson across all these examples

In every case, the value came from letting users express what they mean, rather than forcing them to guess the words a document uses. And in every case, the successful teams paired the technology with filters, keyword matching, careful evaluation and sensible capacity planning, instead of relying on the vectors alone.

31Common Myths About kNN Search

A few misunderstandings appear again and again. Let us set them straight.

Myth: “kNN search replaces keyword search.”

Reality: it complements it. Exact terms, codes and names are often better served by keyword matching, and hybrid designs usually win.

Myth: “Approximate means inaccurate.”

Reality: well-tuned approximate search reaches very high recall. The small loss is measurable, controllable, and usually far outweighed by the speed gain.

Myth: “More dimensions always mean better results.”

Reality: quality depends on the model. Higher dimensions cost memory and time, and a good smaller model can beat a poorly matched larger one.

Myth: “The nearest neighbors are always relevant.”

Reality: kNN returns the closest items even when none is close. Use similarity thresholds and quality checks to avoid presenting weak matches as good ones.

Myth: “Vectors live in the Java heap.”

Reality: they are stored in files and read through the operating system’s file cache, so leaving free memory outside the heap is essential.

Myth: “I can swap the embedding model any time.”

Reality: a new model means new vectors for every document and query. Plan a reindex into a new index and switch over with an alias.

Myth: “Embeddings are anonymous.”

Reality: vectors can leak information about their source text. Protect them like the originals.

“Nearness is a measurement, not a verdict: the closest answer is not always a good answer.”

32How Vector Search Evolved in Elasticsearch

A short history helps you read older articles and understand why the feature looks the way it does.

1

Early vector support

The 7.x series introduced the dense vector field and scripted scoring, allowing exact similarity over selected documents, but with no graph index for large-scale search.

2

Approximate kNN arrives

The 8.x series brought approximate kNN search based on HNSW graphs built by the underlying Lucene library, with filtering support and a dedicated kNN section in the search request.

3

Hybrid and fusion features

Later 8.x releases added easier ways to combine kNN with keyword queries, including rank fusion and the retriever style of describing searches.

4

Compression and scale

Quantization options arrived and expanded, along with support for byte and bit vectors and larger dimension limits, making very large collections affordable.

5

Simpler semantic workflows

Inference endpoints and semantic text fields reduced the code needed to embed, chunk and search text, so that semantic search became accessible without hand-built pipelines.

!
Check Your Version

This area of Elasticsearch has changed quickly. Default settings, available index options, request syntax and limits differ between releases. Confirm details in the documentation for your exact version and test on a non-production cluster first.

33Frequently Asked Questions

Short answers to the questions beginners ask most often about kNN search.

Q1What is kNN search in one sentence?

It finds the k stored items whose vectors are closest to the vector of a query, so results are chosen by similarity of meaning rather than by matching exact words.

Q2What is an embedding?

An embedding is a list of numbers produced by a machine learning model that represents the meaning of a piece of data, so that similar items have similar lists.

Q3What is the difference between exact and approximate kNN?

Exact kNN compares the query with every candidate vector and is perfectly accurate but slow at scale. Approximate kNN uses an HNSW graph to examine only a small part of the data, trading a tiny chance of missing a true neighbor for much greater speed.

Q4What does num_candidates do?

It sets how many candidates each shard considers before the best results are chosen. Higher values improve accuracy and slow the search; lower values do the opposite. It must be at least as large as k.

Q5Which similarity metric should I use?

Use the one recommended for your embedding model. Cosine is a safe default. Dot product is fast when vectors are normalized to unit length. L2 distance suits models trained with Euclidean distance.

Q6Can I change the number of dimensions later?

Not on an existing field that already holds data. Create a new index with the new mapping and reindex, generating vectors with the corresponding model.

Q7Why do I get fewer results when I add a condition?

If the condition is applied after retrieving the k neighbors, many may be discarded. Put the filter inside the kNN search so that the neighbors are chosen among matching documents only.

Q8Do vectors use the Java heap?

No. They are stored in files and read through the operating system’s file cache. Leave enough free memory outside the heap for the cache to hold the hot vector data.

Q9What is quantization?

It is storing vector numbers with fewer bits, which shrinks the memory needed and speeds comparisons at a small cost in precision. Full-precision originals are usually kept for rescoring.

Q10Should I combine kNN with keyword search?

For text, usually yes. Hybrid search handles paraphrases through vectors and exact terms through keywords, and typically beats either alone.

Q11Why does kNN return results even when nothing is relevant?

Because it returns the closest items whatever their distance. Use a similarity threshold, and check quality on real queries, so that weak matches are not presented as good ones.

Q12How do I handle long documents?

Split them into passages, embed each passage, and keep a reference to the parent document. Search over passages and group or display results by parent.

Q13How do I measure whether my kNN setup is good?

Measure recall against exact search to check the approximation, and measure relevance against labelled queries to check usefulness. Repeat whenever the model, data or settings change.

Q14Can I switch embedding models later?

Yes, but it requires re-embedding all documents and creating a new index. Vectors from different models must never be mixed. Use an alias to switch over smoothly.

Q15Are vectors safe to store without extra protection?

No. Vectors can reveal information about their source text, so protect them with the same access controls, encryption and retention rules as the documents they came from.

34Summary and Key Takeaways

k-nearest neighbor search finds the items whose vectors lie closest to a query vector, letting Elasticsearch match by meaning instead of by words. Embedding models turn text, images and other data into vectors, similarity metrics define what near means, and an HNSW graph in each segment makes the search fast by examining only a small part of the data. The controls that matter most are the choice of model and metric, the values of k and num_candidates, filters applied inside the search, hybrid combination with keyword retrieval, and quantization for memory. Success depends as much on discipline as on technology: chunk documents sensibly, never mix models, leave memory for the file cache, protect vectors like the data they came from, and measure recall and relevance on honest queries before and after every change.

Key Takeaways

  • kNN finds the closest vectors — the k items nearest to the query vector become the results, so matches are based on meaning rather than shared words.
  • Embeddings must come from one consistent model — stored and query vectors need the same model, dimensions and similarity metric, or distances mean nothing.
  • Approximate search trades a little accuracy for a lot of speed — HNSW graphs per segment let Elasticsearch skip most vectors, and recall can be measured against exact search.
  • k and num_candidates form the speed and accuracy dial — raise num_candidates for better recall, lower it for lower latency, and choose the smallest value that meets your target.
  • Filter inside the search and combine with keywords — filters keep results complete and safe, and hybrid retrieval covers both paraphrases and exact terms.
  • Memory lives outside the heap — vectors and graphs rely on the file cache, so plan free memory, consider quantization, and keep segment and shard counts sensible.
  • Evaluate, secure and version everything — test on real queries, protect vectors as sensitive data, record model versions, and reindex into a new index when models or key settings change.