What Is a Dense Vector Field in Elasticsearch?
A complete, plain-English guide to the field type that lets Elasticsearch search by meaning: what a vector is, where it comes from, how similarity is measured, how nearest neighbors are found among millions of vectors, and what it costs in memory, speed, and care.
Imagine a huge map, not of countries, but of ideas. Every sentence, product, or picture has a spot on it. The sentence “a small furry pet that purrs” sits very close to “kitten” and “cat,” a little farther from “dog,” and very far from “tax return.” Nobody drew this map by hand. A computer program learned it by reading enormous amounts of text, and it places things near each other when they mean similar things, even when they share no words at all.
Now suppose that a user searches for “feline companion.” Ordinary keyword search looks for those exact words and finds nothing about kittens. But if every document has coordinates on the map of meaning, the search can simply ask: which documents are closest to the coordinates of “feline companion”? Kittens and cats come back. The list of coordinates stored with each document is called a vector, and the field type that holds it in Elasticsearch is the dense vector field. This guide starts from zero, opens the machinery step by step, and ends with the numbers, the trade-offs, and the habits that keep vector search accurate, fast, and affordable in production.
1What a Dense Vector Field Actually Is
Three plain ideas, put together: a list of numbers, a fixed length, and closeness. Once each is clear, the rest follows naturally.
A vector is simply an ordered list of numbers, such as 0.12, negative 0.83, 0.05, and so on. Each number says how far along one direction a thing lies. With two numbers you can place a point on a flat sheet of paper. With three, you can place it in a room. Vectors used in search have many more numbers, commonly a few hundred or a thousand, so they place things in a space with that many directions. We cannot picture such a space, but the arithmetic works exactly as it does on paper.
A dense vector is one in which almost every position holds a meaningful number. This is different from a sparse vector, where most positions are zero and only a few carry values. Dense vectors are typically produced by machine-learning models called embedding models, which read a piece of content and return a list of numbers that captures its meaning. The list is called an embedding. Content with similar meaning receives embeddings that lie close together.
The dense vector field is the Elasticsearch field type that stores one such vector per document. You declare it in the mapping, telling Elasticsearch how many numbers each vector has, and what kind of closeness to use. Then you fill it with the embeddings of your content, and search it with a technique called k-nearest-neighbor search, or kNN: given a query vector, find the k stored vectors that lie closest to it.
Think of a taste profile. A restaurant rates every dish on twenty scales: sweetness, saltiness, spiciness, crunchiness, and so on. Each dish becomes a list of twenty numbers. To find dishes similar to one you love, you do not read menus. You look for dishes whose twenty numbers are closest to the ones of your favorite. A dense vector is such a profile, only produced by a model, with hundreds of scales that nobody named.
What it is not
A dense vector field is not a text field. It does not split words, it has no postings lists of terms, and it cannot be searched with ordinary word queries. It is also not the place where the model lives. The embedding model is a separate program, inside or outside Elasticsearch, that produces the numbers. The field just stores them, indexes them for fast nearest-neighbor lookup, and compares them at search time. Finally, it is not a replacement for keyword search. It answers a different question, “what is similar in meaning?”, and works best together with the keyword tools, as Chapter 9 shows.
Elasticsearch has several vector-related things. The dense vector field stores numbers and supports similarity search. The sparse vector field stores a small number of weighted terms and is used by different models. The semantic text field is a convenience that can create and store embeddings for you. This guide is about the dense vector field, and mentions the others only to explain how they relate.
The one-sentence version
If you remember one line from this chapter, make it this: a dense vector field stores, for each document, a fixed-length list of numbers produced by an embedding model, and lets Elasticsearch find the documents whose lists lie closest to a query list, which means finding the documents most similar in meaning.
2Why It Exists: Words Are Not Meaning
Every feature is born from a frustration. The frustration here is that people mean one thing and write many.
Keyword search matches the words you type against the words stored in documents. It is fast, precise, and predictable. But human language is slippery. The same idea is expressed in many ways: “cheap flights,” “budget airfare,” “low-cost tickets.” A search for the first may miss documents that use only the second. The same word can mean different things: “bank” of a river and “bank” for money. And people often do not know the exact words: a shopper types “shoes for running in the rain” while the catalog says “waterproof trail sneakers.”
Search engineers have fought this for decades with synonym lists, stemming, spelling correction, and hand-tuned rules. These help, but they are labor-intensive, brittle, and always incomplete. Embedding models offer a different approach. Instead of listing which words are equivalent, they learn from vast amounts of text what tends to appear in similar contexts, and turn that knowledge into positions on a map. Content about the same thing ends up in the same neighborhood, whichever words it uses, and often whichever language it is written in.
A librarian who matches only exact titles will fail a visitor who asks for “that book about the boy wizard.” A librarian who understands what the visitor means will walk straight to the right shelf. Vector search gives the search engine that second kind of understanding.
A short history
Word-based search only
Elasticsearch began as a keyword search engine built on inverted indexes. Meaning was approximated with analyzers and synonyms.
Vectors as stored numbers
Around version 7, a field type for vectors appeared, and similarity could be computed inside scoring scripts. This worked by comparing the query with every candidate document, which is exact but slow on large collections.
Indexed nearest-neighbor search
Around version 8, vectors could be indexed in a graph structure, and approximate nearest-neighbor search became a first-class feature, fast enough for millions of vectors.
Compression and convenience
Later releases added compact number types, quantization to shrink memory, hybrid search with keywords, filters during graph search, integration with embedding models, and fields that manage embeddings automatically.
Today: a general-purpose retrieval layer
Dense vector fields serve semantic search, recommendations, and the retrieval step of applications that use large language models.
The exact release for each step is worth double-checking in the release notes of your version. The direction of travel is what matters: from “compare everything” to “search a graph,” and from “raw numbers” to “compressed, managed, and combined with keywords.”
Why not just use synonyms?
Synonyms are still useful, and often the best answer for a known, small vocabulary, such as product codes and brand names. But a synonym list cannot cover every way that people phrase a question, it needs constant upkeep, and it cannot handle whole sentences. Vectors handle the long tail of phrasing automatically. The price is that they are approximate, they cost memory, and they depend on the quality of the model that produced them.
| Question | Keyword search | Vector search |
|---|---|---|
| Finds exact words and codes | Excellent | Weak, may blur exact tokens |
| Finds paraphrases and related ideas | Weak without synonym lists | Strong |
| Handles long, natural questions | Moderate | Strong |
| Works across languages | No, unless translated | Yes, with a multilingual model |
| Explains why a document matched | Yes, shared words | Hard, only a closeness score |
| Cost in memory and compute | Low | Much higher |
3Core Concepts You Need First
A dozen words explain nearly everything about vector search. Learn them here, and later chapters will read like common sense.
Vector
An ordered list of numbers. In search, it stands for the position of one piece of content on a map of meaning.
Dimension
One position in the list. A vector with 768 numbers has 768 dimensions. All vectors in a field share the same count.
Embedding
The vector that a model produces for a piece of content. “Embed” means to turn content into such a vector.
Embedding model
The machine-learning program that reads content and returns its embedding. Different models give different vectors.
Similarity and distance
Ways to measure how near two vectors are. High similarity or low distance means the contents are alike.
Normalization
Scaling a vector so that its length is exactly one. It makes some similarity measures faster and simpler.
Nearest neighbor and k
The stored vectors closest to the query. The number k says how many you want back.
Exact and approximate
Exact search compares against every vector. Approximate search uses a shortcut structure and may miss a few true neighbors.
HNSW graph
The layered network of vectors, linking each one to close neighbors, that makes approximate search fast.
Recall
The share of the true nearest neighbors that a search actually returns. Ninety percent recall means nine of the ten true closest.
Quantization
Storing each number with fewer bits, to shrink memory and speed up comparison, at some cost in accuracy.
Segment
A sealed set of files inside a shard. Each segment keeps its own vectors and its own graph.
Where the numbers come from
An embedding model is trained on a huge collection of examples until it learns to place similar things near each other. After training, you give it a sentence, an image, or another kind of content, and it returns a vector of fixed length. The same model always returns vectors of the same length, and only vectors from the same model can be compared. A vector produced by one model means nothing on the map of another, just as coordinates from one atlas are meaningless on a different planet’s map.
Two friends draw maps of the same town, but one measures distances in miles from the church and the other in kilometers from the station. Coordinates from the first map cannot be looked up on the second. In the same way, vectors from different embedding models cannot be mixed in one field.
Distance and similarity are two sides of one coin
When two vectors are close, their distance is small and their similarity is large. Elasticsearch turns each measure into a score, so that a bigger score always means a better match. You choose the measure in the mapping. The three classic ones are the straight-line distance, the angle between the vectors, and the dot product. Chapter 6 explains them.
Why “approximate” is acceptable
Finding the exact nearest neighbors among millions of vectors means comparing the query with every one of them. With 768 numbers each, that is billions of multiplications per query. Approximate methods build a network of links between close vectors and follow it from a starting point toward the query, checking only a tiny fraction of all vectors. They may occasionally miss the true closest one, but they answer in milliseconds. For search applications, where the “best” result is a matter of degree anyway, this trade is almost always worth it.
A high similarity score does not prove that a result is correct or useful. It says only that the model considers the two pieces of content close. The scores depend on the model and the metric, and they are not comparable across different models or fields.
Quick glossary
| Word | Plain meaning |
|---|---|
| kNN | k-nearest-neighbor search: return the k stored vectors closest to a query vector. |
| ANN | Approximate nearest neighbor: a faster search that may miss a few true neighbors. |
| Candidate | A vector examined during search and kept for possible return. |
| Hybrid search | Combining keyword search and vector search in one request. |
| Chunking | Cutting long text into shorter pieces so that each gets its own embedding. |
| Sparse vector | A vector where almost all positions are zero, used by different kinds of models. |
| Page cache | The operating system’s memory for recently used file data, where vectors should stay. |
| Heap | The working memory of the Java program that Elasticsearch runs in. |
4Architecture and Components
Where do vectors come from, where do they live, and which parts work together when a user searches?
Vector search has two halves that mirror each other. On the indexing side, content is turned into vectors and stored. On the search side, the user’s query is turned into a vector with the same model, and Elasticsearch looks for stored vectors near it. The embedding model sits outside the field, and the field itself is the storage and the index.
flowchart TB
T["Text, image, or other content"] --> EM["Embedding model: turns content into a list of numbers"]
EM --> V["Vector with a fixed number of dimensions"]
V --> F["dense vector field in the document"]
F --> S["Segment: raw vectors plus an HNSW graph"]
Q["User query"] --> QE["Same embedding model makes a query vector"]
QE --> K["kNN search: find the nearest vectors"]
S --> K
K --> R["Top results with scores"]
Read the picture from top to bottom. Content passes through an embedding model, producing a vector of fixed length. The vector is stored in the document’s dense vector field. When the document is written into a segment, the vectors are saved in a file and, if the field is indexed, linked into a graph. At search time, the query text goes through the same model, giving a query vector. The kNN search walks the graph of each segment, collects the nearest candidates, and returns the best results with their scores.
The main components
Embedding model
Runs inside Elasticsearch, on machine-learning nodes, or in an outside service. It returns the vector for a piece of content.
Inference service and pipelines
Connects models to indexing and search, so that documents are embedded on the way in, and queries on the way out.
Dense vector field
Holds one vector per document, with its dimension count, element type, similarity, and index options.
Vector files
Per segment, a file with the raw vectors, stored in order, ready to be read from the file cache.
HNSW graph
Per segment, a layered network linking each vector to its close neighbors, used to find candidates quickly.
kNN query
Takes a query vector, the number of results, the number of candidates, and optional filters.
Hybrid ranking
Merges vector results with keyword results into one ranking.
Operating system file cache
Keeps vectors and graphs in memory, outside the Java heap, so that searches read them fast.
Vectors live per segment
Like other indexed data, dense vectors are stored inside segments. Each segment has its own vector file and its own graph, covering only the documents of that segment. A shard is made of several segments, so a kNN search on a shard runs once per segment and combines the findings. When segments merge, the vectors are copied into a new segment and the graph is rebuilt or extended. This makes the number of segments, and the cost of merges, important themes of the chapters that follow.
Imagine a city divided into districts, each with its own street map and its own list of the nearest cafes. To find the ten cafes nearest to you in the whole city, you ask each district for its best candidates and then pick the overall best. If the city were one big district, you would ask only once, but the map would be huge.
Walking one search through the machine
The user types a question
For example, “waterproof shoes for trail running.”
The question becomes a vector
The same embedding model that produced the stored vectors converts the question into a query vector.
The request reaches the shards
The coordinating node sends the query vector to a copy of each shard.
Each segment is searched through its graph
The search starts at an entry point and hops toward the query, collecting close candidates. Filters restrict which documents count.
Each shard returns its best candidates
The nearest vectors found across its segments, with scores.
The coordinating node picks the winners
It merges the shard results and returns the best k to the user.
Because the graph, the vectors, and the file cache all take memory, and because every segment adds a search, the amount of RAM, the number of segments, and the number of shards decide how fast and how cheap vector search will be. The following chapters return to these points.
5Anatomy of the Dense Vector Field
Now we open the mapping and look at each option. A small number of choices control almost everything.
When you declare a dense vector field, you tell Elasticsearch a handful of things. The most important are the number of dimensions, the type of each number, whether the field should be indexed for fast search, which similarity to use, and how the index should be built. The table below lists them in plain words.
| Option | What it means | Typical choice |
|---|---|---|
| Dimensions | How many numbers each vector holds. Must match your model exactly. | The size your embedding model produces, often several hundred to about a thousand |
| Element type | How each number is stored: as a decimal number, as a small whole number, or as a single bit. | Decimal numbers by default, smaller types for compact models |
| Indexed | Whether a graph is built so that nearest-neighbor search is fast. | On, for anything searched by similarity at scale |
| Similarity | How closeness is measured: distance, angle, dot product, or maximum inner product. | The one recommended for your model, often cosine or dot product |
| Index options | How the graph is built and whether numbers are compressed. | The default graph, often with compression in recent versions |
Setting up a photo album, you decide the page size (dimensions), whether photos are kept in color or black and white (element type), whether to keep a quick-reference index at the back (indexed), and how to judge whether two photos look alike (similarity). The choices are made once, before the photos go in.
Dimensions must match the model
The number of dimensions is fixed for the field, and every vector stored in it must have exactly that many numbers. If your model produces vectors of 768 numbers, the field must say 768. A vector with a different length is rejected at indexing time. The maximum allowed number has grown across versions, and now reaches several thousand, but higher numbers mean more memory and slower comparison, so more is not better by itself. Choose a model whose size fits your budget.
Element types
Float
Each number is a four-byte decimal. Most embedding models produce these, and this type keeps full precision.
Byte
Each number is a single byte, a whole number in a small range. It needs a model that outputs such values, or a compression step, and quarters the size.
Bit
Each number is a single bit, used with models or methods that produce binary vectors. It shrinks size dramatically, and is compared by counting differing bits.
One vector per field
In the standard arrangement, a dense vector field holds one vector per document. It cannot hold a list of vectors for the same document. If a long document needs several vectors, for example one per paragraph, the usual answers are to cut the document into chunks and index each chunk as its own document, or to place the chunks in nested objects, each with its own vector. Both designs are discussed in Chapter 11.
The vector and the original source
By default, the document’s original source contains the vector as an array of numbers, and the same numbers are also stored in the special vector files. This means the numbers are kept twice, once in the source and once in the vector storage, and every time the source is fetched or a document is updated or reindexed, hundreds of numbers travel with it. Newer versions let you keep vectors out of the returned source, or handle them more economically, to save space and bandwidth. Check the behavior of your version, because the defaults have changed over time.
Choose the model first
Its output size and recommended similarity decide the mapping.
Declare the field
Set dimensions, element type, and similarity to match the model.
Decide on indexing and compression
Turn on the graph, and pick a compression level appropriate to your size and accuracy needs.
Decide what happens to the source
Keep vectors in it or leave them out, according to your version and needs.
Test on a sample
Index a realistic sample, measure size, speed, and result quality, and adjust before the full load.
Changing the mapping later
Most of these choices are permanent for an index. You cannot change the number of dimensions, or the similarity, of an existing field. Some newer versions allow certain upgrades of the compression setting, but in general, changing the design of a dense vector field means creating a new index with a new mapping and reindexing the data. If you also change the embedding model, all vectors must be recomputed, because the old ones are meaningless to the new model. Plan for this from the start, for example by giving indexes names that include the model version, and by using aliases to switch between them.
The set of options, their defaults, and their limits differ between versions. This guide describes the ideas, which are stable, and names details only in general terms. Confirm the exact options, defaults, and limits in the documentation of the version you run.
6Measuring Closeness: Similarity Metrics
Nearest neighbor search needs a ruler. The choice of ruler changes which results come first, so it deserves care.
Two vectors are “close” when a chosen measure says so. Elasticsearch offers several measures. Three of them are classic, and one is a specialized variant. Each turns a pair of vectors into a score in which a higher number always means a better match.
flowchart TD
A["Choose a similarity"] --> B{"Does the embedding model recommend one?"}
B -- Yes --> C["Use the recommended metric"]
B -- No --> D{"Are the vectors normalized to length one?"}
D -- Yes --> E["dot product: fastest, same ranking as cosine"]
D -- No --> F{"Does vector length carry meaning?"}
F -- Yes --> G["maximum inner product or straight-line distance"]
F -- No --> H["cosine: compares direction only"]
Straight-line distance
This is the everyday idea of distance: the length of the straight line between two points. It takes into account both the direction and the length of the vectors. Points that lie physically close have a small distance. Elasticsearch converts it to a score so that small distances give large scores. It suits models that were trained with distance in mind, and data where the magnitude of a vector is meaningful.
Cosine similarity
Cosine similarity looks only at the angle between two vectors, ignoring their lengths. Two vectors pointing in the same direction are considered identical in meaning, even if one is much longer than the other. Perfectly aligned vectors score at the top, unrelated ones in the middle, and opposite ones at the bottom. Most text embedding models are designed for cosine similarity, which is why it is the most common choice. A vector with all zeros has no direction, so it cannot be used with cosine similarity, and is rejected.
Two people walking in the same direction are heading to the same place, even if one walks faster and has gone farther. Cosine similarity asks only “are they pointing the same way?” and does not care how far each has gone.
Dot product
The dot product multiplies the numbers of two vectors position by position and adds them up. When both vectors have been scaled to length one, the dot product equals the cosine, and gives the same ranking, but is cheaper to compute. Elasticsearch’s dot product option therefore expects unit-length vectors. Normalizing your vectors once, before indexing, and using the dot product option is a common way to save work on every search.
Maximum inner product
This variant uses the dot product without requiring unit length, so that vector length counts. Some recommendation models use it deliberately: a longer vector might mean a more popular item, and the ranking should reflect that. Because the raw values are unbounded, Elasticsearch scales them into a positive score. Use it only when your model documentation says to.
| Metric | What it compares | Vectors must be | Typical use |
|---|---|---|---|
| Straight-line distance | Position in space, direction and length | Any, non-zero not required | Models trained for distance, image features |
| Cosine | Direction only | Non-zero | Most text embeddings |
| Dot product | Alignment and length, equal to cosine for unit vectors | Unit length | Normalized embeddings, fastest option |
| Maximum inner product | Raw alignment and length | Any | Recommenders that encode strength in length |
A tiny example
Let us use a two-number picture. Suppose vector A points along the horizontal axis, vector B points straight up, and vector C points diagonally between them. The angle between A and C is 45 degrees, and the angle between A and B is 90 degrees. By cosine similarity, C is closer to A than B is, because the angle is smaller. If you stretch C to twice its length, its direction does not change, so cosine similarity does not change, but the straight-line distance between C and A grows. This shows why the choice of ruler matters: the same three points give different rankings under different measures.
How scores are shaped
The similarity produces a raw value, which Elasticsearch converts into a positive score. For the angle-based measures, the range is mapped so that identical direction gives a score near one, and opposite direction gives a score near zero. For distance, small distances give scores near one, and large distances give scores approaching zero. These scores are for ranking and for combining with other scores. They are not probabilities, and a score of 0.9 does not mean 90 percent chance of being relevant.
Similarity thresholds
A kNN search can also set a minimum similarity, so that results below the threshold are dropped, even if fewer than k results remain. This is useful when you would rather return nothing than return something irrelevant. The threshold is expressed in the units of the raw measure, not the converted score, so read the documentation for your version before choosing a value.
An embedding model is trained with a specific measure in mind. Using a different one may still work, but can quietly reduce result quality. Read the model’s documentation, choose the recommended metric, and test with real queries before you commit to it.
Normalizing vectors
Normalizing means dividing each number of a vector by its length, so that the new length is exactly one. Many embedding services already return normalized vectors. If yours does not, and you want the fast dot product option, you can normalize in your loading program or in an ingest step. Never normalize some vectors and not others in the same field, and never normalize the stored vectors but forget to normalize the query vector, since mixed lengths would distort the ranking.
7How Vectors Are Stored and Indexed
A vector is only a list of numbers, but storing millions of them so that they can be searched quickly takes careful machinery.
flowchart LR
A["Document with a vector arrives"] --> B["Vector is added to the in-memory segment and its graph"]
B --> C["Refresh writes a new segment with vectors and graph"]
C --> D["Merge combines segments and builds larger graphs"]
D --> E["Search asks every segment for its nearest candidates"]
E --> F["Shard combines candidates, coordinator picks the global best"]
Two structures per segment
Inside each segment, a dense vector field creates two things. The first is a file of raw vectors, stored one after the other, in the order of the documents. This lets the search read the numbers of any document quickly. The second, if the field is indexed, is the graph: a network in which each vector is linked to a small number of its close neighbors. The graph is what makes approximate search fast. Both are stored in files that the operating system can map into memory.
Think of a very large address book. The vectors file is the book itself, listing every person’s coordinates. The graph is a set of notes saying, for each person, “the five people who live closest to me.” Starting from one person and following the notes, you can walk toward any address without reading the whole book.
Building the graph as documents arrive
When a document with a vector is indexed, the vector is inserted into the graph of the segment that is currently being built in memory. The insertion searches the existing graph for the new vector’s closest neighbors, and links the new vector to them. This is real work, involving many distance calculations, so indexing vectors is much slower than indexing plain text or numbers. The cost grows with the number of dimensions and with the quality settings of the graph.
Refresh and new segments
When a refresh happens, the in-memory graph is written out as a new segment with its own vector file and graph files. Each refresh under heavy indexing creates a new small segment. Small segments have small graphs, which are quick to search but add to the number of graphs that every search must visit.
Merging: the expensive part
In the background, segments are merged into larger ones. For vectors, a merge must produce a graph for the combined segment. Depending on the version, Lucene may reuse the graph of the largest input and insert the vectors of the others into it, or build a new one. Either way, merging vectors involves many distance calculations and uses a lot of processor time, far more than merging text segments of the same size. This is one reason that indexing many vectors continuously can keep a cluster’s processors busy long after the documents have stopped arriving.
Vector arrives with its document
The vector must have exactly the declared number of dimensions.
It is placed into the in-memory graph
The system searches for its closest neighbors and links to them.
A refresh or flush writes the segment
Vectors and graph are saved as sealed files.
Background merges combine segments
The graph of the new, larger segment is built, using much processor time.
Old segments are removed
Their files are deleted once the merged segment is ready.
How much memory do vectors need?
The single most important sizing fact is that vector search is fast only when vectors and graphs stay in memory, in the operating system’s file cache, not on the Java heap. Reading a vector from disk for every step of a graph walk would be slow. A rough estimate of the memory needed for unquantized decimal vectors is the number of vectors, times four bytes, times the number of dimensions, plus a smaller amount for the graph links, roughly four bytes times the number of links per vector.
A numbers walkthrough
Let us use invented numbers. Suppose you store 10 million vectors of 768 dimensions each as four-byte decimals. The raw vectors take 10 million times 768 times 4 bytes, which is about 30.7 gigabytes. With sixteen links per vector, the graph adds about 10 million times 4 times 16 bytes, which is about 640 megabytes. So around 31 to 32 gigabytes should ideally fit in the file cache across the nodes that hold this data, for good latency. If the same data were stored with each number in one byte, the vectors would take about 7.7 gigabytes. If in a single bit, about 960 megabytes. Compression, which Chapter 10 discusses, is therefore the main way to fit large collections in modest memory.
| Storage form | Approximate memory for 10 million vectors of 768 dimensions | Comment |
|---|---|---|
| Four-byte decimals | About 31 GB, plus the graph | Full precision, largest |
| One byte per number | About 8 GB, plus the graph | Four times smaller, small accuracy loss |
| Half a byte per number | About 4 GB, plus the graph | Eight times smaller, more accuracy loss |
| One bit per number | About 1 GB, plus the graph | Around thirty times smaller, needs rescoring for quality |
Disk is bigger than memory needs
Even when vectors are compressed for searching, the original decimal vectors are usually kept on disk as well, so that they can be used for rescoring or for rebuilding the compressed copy. This means the disk footprint may be larger than the memory footprint of the searched form. Plan disk and memory separately: memory for what the search touches, disk for everything that must be kept.
Deletes and updates
Deleting a document does not remove its vector from the graph of its segment. The vector stays, marked as deleted, and searches skip it, until a merge rewrites the segment. An update is a delete plus a new document in a new segment. Indexes with constant updates therefore carry dead vectors in their graphs, consuming memory and search effort until merged away.
Vectors and graphs are read through the operating system’s file cache, not from the Java heap. Giving the heap too much memory leaves too little for the cache and makes vector search slow. Keep the heap moderate and leave the rest of the machine’s memory to the operating system.
8The HNSW Graph: How Nearest Neighbors Are Found Fast
The name sounds frightening, but the idea is friendly: use highways to get near, then local streets to arrive.
HNSW stands for Hierarchical Navigable Small World. It is a graph in which each vector is a point, connected by links to a few of its closest neighbors. The graph has several layers. The bottom layer contains every vector, with links to close neighbors. Each higher layer contains only a random sample of the vectors below it, so it is sparser, and its links span longer distances. The top layer contains very few points.
flowchart TD
E["Entry point in the top layer"] --> L2["Top layer: few points, long jumps"]
L2 --> L1["Middle layer: more points, shorter jumps"]
L1 --> L0["Bottom layer: all vectors, fine search"]
L0 --> C["Keep the num_candidates closest vectors found"]
C --> T["Return the best k"]
Imagine driving to a small house in another country. You start on the international highway, which has few exits but covers long distances. You leave it near the right region and take a regional road with more exits. Finally you follow small streets to the exact house. You never drive through every street in the world.
How a search works
A search starts at an entry point in the top layer. From there, it repeatedly moves to whichever linked neighbor is closer to the query vector, until no neighbor is closer. That point is the best found in that layer, and it becomes the starting point in the layer below, where the same greedy walk continues with finer steps. At the bottom layer, the search does not stop at the first best point. It keeps exploring neighbors and maintains a list of the closest candidates seen so far, of a size that you control. When no better candidates can be found, it stops, and the best of the list are returned.
Why it is fast
The walk touches only a tiny part of the graph. Even with tens of millions of vectors, a typical search visits a few thousand of them. The number of steps grows slowly with the size of the collection, roughly with its logarithm, rather than in direct proportion. That is why doubling the data adds only a little to the search time, as long as the graph stays in memory.
The knobs
Links per point
How many neighbors each vector links to. More links improve accuracy and give the search more paths, but enlarge the graph and slow indexing. A common default is sixteen.
Construction candidate list
How many candidates are examined when linking a new vector. Larger values build a better graph, but indexing takes longer. A common default is one hundred.
Number of candidates
How many candidates the bottom-layer walk keeps. Larger values find more of the true neighbors, but take more time. This is the main knob you turn at query time.
The value of k
How many results you finally want. It cannot exceed the number of candidates.
The accuracy and speed trade
Every knob trades accuracy for cost. A graph built with more links and a larger construction list is better at finding true neighbors, but takes more time and memory to build. A search with more candidates finds more of the true neighbors, but takes longer. The measure of accuracy is recall: of the true k nearest neighbors, how many did the search return? A recall of ninety percent means that nine of the ten true nearest neighbors were found. Most applications aim for a recall in the high nineties and accept a small loss for large savings in time.
| Change | Recall | Search speed | Index speed | Memory |
|---|---|---|---|---|
| More candidates at search time | Higher | Slower | No effect | No effect |
| More links per point | Higher | Somewhat slower per step, more paths | Slower | Larger graph |
| Larger construction list | Higher | No direct effect | Slower | No effect |
| Compressed vectors | Slightly lower | Faster | Similar | Much smaller |
| More segments | Similar per segment | Slower, one walk per segment | Cheaper writes | Similar |
One graph per segment
Because each segment has its own graph, a search runs the walk once for every segment and combines the candidates. A shard with twenty small segments does twenty walks. A shard with two large segments does two. Each walk in a small segment is quick, but the overhead adds up. This is why heavy indexing, which creates many small segments, can slow vector searches until merges catch up, and why merging read-only data into few large segments is such a helpful step.
Filters during the walk
Real searches often combine similarity with conditions such as “only in stock” or “only this customer’s documents.” Elasticsearch applies such filters during the graph walk itself: it only counts vectors of documents that pass the filter as candidates, and keeps walking until it has enough. This means the search still returns k results that all satisfy the filter, if enough exist. When a filter is very restrictive, so that very few documents pass, walking the graph becomes wasteful, and the engine switches to an exact comparison against just the passing documents, which is cheap when there are few of them.
A filter given inside the kNN part of a request restricts the candidates during the search. A filter given only in the general query part of a request may be applied after the nearest neighbors are found, which can leave fewer than k results. Know which one you are using, and prefer the filter inside the kNN part when you need k results that all pass.
9Searching Dense Vectors
There are two ways to search a dense vector field, exact and approximate, and a powerful way to combine them with keywords.
Approximate kNN search
The main way to search an indexed dense vector field is the kNN search. You give it the field to search, a query vector, the number of results you want, and the number of candidates to consider on each shard. Optionally, you give a filter, a minimum similarity, and a boost that scales its scores. The query vector can be supplied directly, or Elasticsearch can produce it from text using a deployed embedding model, so that the client never handles vectors at all.
| Parameter | What it does | Guidance |
|---|---|---|
| Field | Which dense vector field to search | Must be indexed for this kind of search |
| Query vector, or a way to build it | The point to search near | Must come from the same model as the stored vectors |
| k | How many nearest neighbors to return | Set to what the application shows or feeds onward |
| Number of candidates | How many candidates each shard considers | At least k, often several times larger for better recall |
| Filter | Conditions that documents must satisfy | Applied during the search, so k results pass it |
| Similarity threshold | Minimum closeness to accept | Use to prefer fewer results over poor ones |
| Boost | A multiplier on the scores | Used when combining with keyword scores |
How shards and candidates interact
Each shard searches its own segments and returns its best candidates. The coordinating node then merges the lists from all shards and keeps the overall best k. Since every shard contributes, an index with many shards does more total work per search, and the candidate count applies per shard. If you raise the number of candidates while the number of shards is large, the total effort rises in proportion. Keep this in mind when tuning.
A national talent contest holds local rounds in many towns. Each town sends its best contestants to the final, and a panel picks the overall winners. If you ask each town for its best fifty instead of its best ten, the panel sees more talent and may find a better winner, but it has more contestants to judge.
Exact search with a scoring script
The older method, still valuable, is to compute similarity exactly for a chosen set of documents inside a scoring script. It compares the query vector with the vector of every document that passes a query, and ranks them. This is exact, since no shortcut structure is used, and it works on fields that are not indexed. But its cost is proportional to the number of documents compared, so it is practical only when a filter narrows the set to thousands, not millions.
| Situation | Better choice |
|---|---|
| Millions of vectors, no strong filter | Approximate kNN with the graph |
| A filter leaves only a few thousand candidates | Exact scoring over the filtered set, or approximate with a filter |
| You need a guaranteed exact ranking | Exact scoring, accepting the cost |
| You need to measure the quality of approximate results | Run exact scoring on a sample, and compare |
| Very small index | Either, exact is simple and cheap |
Hybrid search: words and meaning together
Vector search is strong on meaning and weak on exact terms, such as product codes, names, and rare words. Keyword search is the opposite. Combining them gets the best of both. A hybrid request runs a keyword query and a vector query, and merges their results into one ranking.
flowchart TD
Q["User question"] --> L["Keyword query: words and postings lists"]
Q --> V["Vector query: meaning and nearest neighbors"]
L --> M["Combine rankings: weighted scores or reciprocal rank fusion"]
V --> M
M --> R["Final ranked results"]
Weighted sum of scores
Add the keyword score and the vector score, each multiplied by a weight. Simple, but the two scores live on different scales, so weights need tuning.
Reciprocal rank fusion
Ignore the scores and combine the positions in each ranking, so that documents that rank well in both lists rise to the top. It needs little tuning. It may depend on your subscription level.
Rerank the merged list
Take the combined top candidates and let a more expensive model reorder them. It improves quality at a further cost.
Paging and sizes
A kNN search returns at most k results. It is not designed for deep paging through thousands of results. If you need more, raise k, keeping in mind that the cost rises. Ordinary paging with offset and size works only within the k results. For very deep listing, use a different approach, such as a filtered keyword query or exact scoring over a narrowed set.
Practical query tips
Use the same model for queries and documents
Different models produce incompatible vectors.
Put filters inside the kNN part
That way, all k results satisfy them.
Set candidates above k
Start with several times k and measure recall against an exact search.
Combine with keywords for exact terms
Hybrid search rescues codes, names, and rare words.
Do not return the vector unless needed
Vectors are large. Fetching them with every hit wastes bandwidth.
10Quantization: Shrinking Vectors Without Losing Their Meaning
Vectors are big. Compression is the main way to make large collections affordable. The trick is to shrink them without shrinking the quality of the results.
flowchart LR
A["Decimal: 4 bytes per dimension"] --> B["8-bit: about 1 byte per dimension"]
B --> C["4-bit: about half a byte per dimension"]
C --> D["Binary: about one bit per dimension"]
D --> E["Smaller memory, faster search, some loss of accuracy"]
The idea
Quantization means storing each number of a vector with fewer bits. A number such as 0.3721845 is stored as a small whole number that stands for a range of values, such as “somewhere in this band.” The comparison of two vectors is then done on these small numbers, which is faster and needs much less memory. The original number cannot be recovered exactly, but the comparison of two vectors stays close to the true one, and the ranking is usually nearly the same.
Compressing a photograph makes the file smaller. A little compression is invisible to the eye. Heavy compression makes the image blurry. Quantization is the same trade for vectors: a little is almost free, and a lot needs care.
The common levels
| Level | Storage per number | Memory saving | Effect on accuracy |
|---|---|---|---|
| None | Four bytes | None | Full precision |
| Eight-bit | One byte | About four times | Small, often negligible |
| Four-bit | Half a byte | About eight times | Noticeable on some data, usually acceptable |
| Binary | One bit | About thirty times | Larger, recovered by rescoring with fuller vectors |
Rescoring: getting the accuracy back
The most aggressive levels lose enough detail that the top of the ranking may be slightly wrong. The fix is rescoring. The search first finds a generous number of candidates using the compressed vectors, which is fast and cheap. Then it recomputes the exact similarity, using the original full-precision vectors read from disk, for only those candidates, and reorders them. The final list is much closer to what full precision would give, and the expensive comparison ran on a small fraction of the data. This “coarse first, fine second” pattern is why binary quantization can keep good accuracy with a tiny memory footprint.
A hiring team skims hundreds of résumés quickly to pick a shortlist of thirty, then reads those thirty carefully. Skimming is cheap and a little sloppy, but the careful reading of the shortlist corrects most mistakes.
Quantization and the original vectors
Originals stay on disk
The full-precision vectors are usually kept, for rescoring and for rebuilding the compressed form when segments merge.
The compressed copy adds size
Disk use rises slightly, because a second, smaller copy is stored next to the original.
Only the compressed copy must be cached
The search reads the small copy, so memory needs fall, while the originals may stay on cheaper disk.
Graph or no graph
Compression options exist both with a graph and with a plain exhaustive scan of the compressed vectors, for smaller collections.
Element types are different from quantization
Do not confuse choosing a small element type in the mapping with quantization performed by Elasticsearch. With a small element type, your model or your pipeline produces small numbers to begin with, and Elasticsearch stores exactly what you provide. With quantization, you provide normal vectors, and Elasticsearch compresses a searching copy for you and keeps the originals. The second is easier to adopt and reversible in spirit, and in recent versions it is often the default.
A numbers walkthrough
Suppose you have 100 million vectors of 384 dimensions. Stored as decimals, they need about 154 gigabytes of memory to be searched well, spread over the cluster. With eight-bit compression, this falls to about 38 gigabytes. With binary compression, to about 5 gigabytes. In the last case, a modest cluster can hold a search index that would otherwise need a large one. The price is that the search must rescore a candidate list from disk, and that a small share of results may differ from the full-precision ranking. The figures are invented, but the ratios are real.
How to choose
Start from the memory you can afford
Estimate the size of your vectors at each level and compare with your cache.
Measure recall on your own data
Compare approximate results against exact results for a sample of real queries.
Pick the strongest compression that still meets your quality bar
Lean on rescoring to recover accuracy at the aggressive levels.
Recheck after changes to the model or the data
Different embeddings compress with different losses.
Recent versions choose a compressed form by default for new fields and offer improved binary methods that need less tuning. Check what your version does by default, because it may already be compressing without your having asked.
11Embedding Models: Where the Numbers Come From
The dense vector field is only as good as the vectors placed into it. Here is how vectors are made, chosen, and kept consistent.
Where the model can run
On Elasticsearch machine-learning nodes
A model is loaded into the cluster and runs next to your data. Content never leaves your environment, but the nodes need processors and memory for inference.
Through an inference service
Elasticsearch calls an outside embedding service on your behalf, using a stored connection, and receives vectors back. It is easy to start, and costs money per request.
In your own application
Your loading program calls a model, then sends documents with vectors already attached. It gives full control and keeps Elasticsearch simple.
Through a field that embeds for you
A special field type can call an embedding model automatically, cut long text into chunks, and store the results, so that you index text and search text.
Embedding at indexing time
If the model runs inside or through Elasticsearch, an ingest pipeline can embed each document as it arrives. The pipeline sends the chosen text field to the model and stores the returned vector in the dense vector field. If your application embeds outside, it sends the vector as part of the document. In both cases, the same model, with the same settings, must be used for every document in the field.
Embedding at query time
At search time, the query text must be embedded by the same model. Elasticsearch can do this itself, given the model’s identifier and the text, so that clients never handle vectors. This adds the model’s latency to every search, typically tens of milliseconds for a small model on a good processor, and much more for large models or slow networks. For repeated queries, caching the query vectors saves time and money.
A translator sits at both the entrance and the counter of a foreign shop. Goods are labeled in the shop’s language by the entrance translator, and customers’ questions are translated by the counter translator. If the two translators used different dictionaries, customers would never find what they want.
Choosing a model
| Question | Why it matters |
|---|---|
| What language or languages will your content use? | A model trained on one language does poorly on others. Multilingual models handle many, at some cost. |
| What kind of content: short queries, long passages, code, product data? | Models are specialized. A model for web text may not suit legal documents. |
| How many dimensions does it produce? | More dimensions mean more memory and slower search, for possibly better quality. |
| What is the maximum input length? | Longer text than the limit is cut off, which means part of the content is never embedded. |
| How fast and how costly is it? | Indexing millions of documents means millions of model calls. |
| Where can it run, and what are the data-privacy rules? | Sending content to an outside service may be forbidden for sensitive data. |
| Which similarity does it expect? | The mapping must match. |
| How does it perform on your data? | Public benchmarks are only a guide. Test with your own queries and judgments. |
Dense versus sparse models
Not all semantic models produce dense vectors. Some, such as Elastic’s own learned sparse model, produce sparse vectors: a small set of terms with weights, including related terms that were not in the text. These are stored in a different field type and searched with different queries. They tend to be more interpretable, since you can see which terms carry weight, and use less memory, while dense vectors often capture deeper meaning and work across languages. Many systems use both, together with keywords.
Chunking long content
Embedding models have a maximum input length, and even below it, one vector for a long article averages many ideas into a blur. The remedy is to cut long content into chunks, such as paragraphs or windows of a few hundred words, embed each chunk, and store each with its vector. There are two common designs. In the first, each chunk is its own document, carrying a reference to its parent. In the second, chunks are nested objects inside the parent document, each with its own vector, so that the parent is returned with the best matching chunk highlighted.
Chunk size
Small chunks are precise but lose context. Large chunks keep context but blur meaning. A few hundred words is a common start.
Overlap between chunks
Repeating a little text at the edges keeps sentences from being cut in the middle of an idea, at a cost in extra vectors.
Respect natural boundaries
Cutting at headings and paragraphs gives more meaningful chunks than cutting at fixed lengths.
Carry the context along
Store the parent’s title, source, and permissions with each chunk, so that filtering and display work.
Model-specific details that surprise people
- Prefixes. Some models expect a short marker before the text, one for documents and another for queries. Forgetting it lowers quality quietly.
- Normalization. Some models return unit-length vectors and some do not. Check before choosing dot product.
- Truncation. Text beyond the input limit is usually cut without warning.
- Versions. A new version of a model with the same name may produce different vectors. Record the exact version used.
- Batching. Sending many texts to the model in one call is much faster and cheaper than one at a time.
Vectors from two different models, or two versions of one model, do not share a map. Mixing them in one field gives results that look plausible but are wrong. Give each model its own field or its own index, and record which model made which vectors.
12Data Flow and Lifecycle of a Vector Index
A vector index is a small factory with a start and an end. Following the whole line shows where things can go wrong.
The pipeline from source to result
Source content
Documents, pages, products, or records come from a database, a file store, or a stream.
Preparation
Cleaning, splitting into chunks, and adding metadata such as source, date, and permissions.
Embedding
Each chunk is sent to the model, ideally in batches, and a vector comes back.
Indexing
Documents with vectors are sent to Elasticsearch in bulk, and vectors enter the graphs.
Segments and merges
Refreshes create segments, and merges combine them, rebuilding graphs.
Searching
Queries are embedded, nearest neighbors found, filters applied, and results merged.
Feedback and evaluation
Click data and relevance judgments show where quality is poor, leading to changes in models, chunks, or settings.
A bakery takes flour and produces bread through several stations: mixing, shaping, baking, packing. A problem at an early station, such as the wrong flour, shows up as bad bread at the end. Fixing the last station will not help. The same is true of a vector index: poor chunks or the wrong model cannot be repaired by tuning the search.
When content changes
Vectors describe the content at the time they were made. If a document’s text changes, its vector must be recomputed, or it will describe the old text. A common bug is to update the text field and forget the vector, so that searches return the document for words that are no longer in it. Build the update path to re-embed whenever the embedded text changes, and consider storing a hash of the text alongside the vector, to detect when a vector is stale.
Updates and deletes at the segment level
As Chapter 7 explained, an update creates a new document in a new segment and leaves the old vector in its old graph, marked as deleted. Merges eventually clean up. In an index with frequent updates, dead vectors accumulate and merges work harder. If you can, design the data as append-mostly, with updates being rare.
Changing the model: the big move
Replacing the embedding model is the most disruptive change to a vector index. All vectors must be recomputed with the new model, and the mapping may need new dimensions or a new similarity. The safe way is a parallel build.
Create a new index
With the mapping that matches the new model, and a name that includes the model version.
Re-embed and load everything
Run the full content through the new model, and index it with settings tuned for a large load.
Evaluate offline
Compare results of old and new indexes on a set of representative queries.
Keep the two in sync while you switch
New and changed content must be embedded with both models until the change is complete.
Move an alias
Point the search alias at the new index, and keep the old one for a while, in case of rollback.
Merging and force merging
Merges keep the number of segments in check, and therefore the number of graph walks per search. For an index that is complete and will not receive more writes, a force merge down to a few segments makes searches faster, since fewer graphs are walked, and removes dead vectors. It also costs a lot of processing power, because graphs are rebuilt, so run it in a quiet period, and only on finished data.
Refresh interval and small segments
While loading large volumes, a short refresh interval creates many small segments, each with a small graph, and the merges that follow rebuild graphs again and again. A longer refresh interval during loading reduces this churn. Restore a sensible value when the load is over.
Record, for each index or document, the model name and version, the chunking rule, and the date of embedding. When quality changes, or a new model appears, this record tells you what to rebuild and how.
13Size, Performance, and Tuning
Vector search is expensive in three currencies: memory, processing time, and indexing effort. Here is where each is spent and how to save it.
Where the cost comes from
Vectors in the file cache
Millions of vectors of hundreds of numbers each. This is usually the largest and most decisive cost.
Distance calculations
Each step of a graph walk compares the query with many vectors, each comparison multiplying hundreds of numbers.
Graph construction
Every new vector is linked into the graph by searching for its neighbors, and merges rebuild graphs.
Embedding calls
One model call per chunk at indexing time and one per query at search time.
Originals and rescoring
Full vectors kept on disk, read for rescoring, add to disk use and to disk reads.
Vectors in requests
Each vector is hundreds of numbers, and bulk requests full of them are large.
A numbers walkthrough for search cost
Let us use invented numbers. Suppose an exact search compares the query with 10 million vectors of 768 numbers. Each comparison needs about 768 multiplications and additions, so one query needs about 7.7 billion operations. Even on a fast processor, that takes seconds. An approximate search through a graph might compare the query with only a few thousand vectors, perhaps five thousand, which is about 3.8 million operations, roughly two thousand times fewer. That is the difference between a few seconds and a few milliseconds, and it is why the graph exists.
Finding the most similar face in a crowd of ten million people by comparing with every person would take days. Following a chain of “this person looks similar to that person” from a starting point brings you to the closest match after a few thousand glances.
Tuning levers
| Lever | Effect on memory | Effect on speed | Trade-off |
|---|---|---|---|
| Fewer dimensions, from a smaller model | Large saving | Faster search and indexing | Possibly less nuanced meaning |
| Quantization | Large saving | Faster comparison | Small loss, recovered by rescoring |
| Fewer, larger segments | None | Faster search, fewer walks | Costlier merges |
| Longer refresh interval while loading | None | Faster loading, fewer merges | Data visible later |
| Zero replicas for a bulk load | None | Faster loading, since each copy builds graphs | No safety copy during load |
| Lower construction effort | Slightly smaller graph | Faster indexing | Lower recall |
| Fewer candidates at query time | None | Faster search | Lower recall |
| Strong filters inside the kNN part | None | Can switch to a cheap exact check | Requires good filter design |
| Keep vectors out of the returned source | Saves network and disk | Smaller responses | Vectors not returned unless requested |
| Dedicated nodes for model inference | Frees data nodes’ memory | Steadier search latency | More machines |
Indexing speed
Vector indexing is slower than text indexing, because of graph construction and, if embedding happens on the way in, model calls. To speed up a large load, embed in batches and outside the critical path if you can, use bulk requests of moderate size, lengthen the refresh interval, use zero replicas for a first load, and let the graph quality settings stay at moderate levels. Because each replica builds its own graph, the cost of replicas is real: one replica doubles the graph-building work.
Search latency
Search latency depends on whether the vectors and graphs are in the file cache. When they are, latency is set by the number of comparisons. When they are not, every step of the walk may need a disk read, and latency rises by orders of magnitude. The most common cause of slow vector search is not the query, but too little memory for the cache. After that come too many segments, too many candidates, and overly large shards.
Shard layout
Every shard runs its own walks, so more shards mean more total work per search. Choose a modest number of shards, of moderate size, and avoid spreading a small vector index over many shards. On the other hand, a single huge shard has a huge graph and long merges, and does not use several nodes’ memory. The right balance depends on the size of the index and the number of nodes.
Hardware
- Memory first. Size the cluster so that the searched form of the vectors fits in the cache of the data nodes.
- Modern processors. Distance calculations benefit from vector instructions of modern processors, and recent Java versions used by Elasticsearch make use of them. Old hardware wastes this.
- Fast local disks. Rescoring and cold reads need fast random access, so solid-state drives are strongly preferred.
- Separate roles. Keep model inference on machine-learning nodes, so that it does not compete with search for memory and processors.
Speed measured without quality is meaningless, since the fastest search returns nothing useful. Always report recall next to latency, for the same settings, on realistic queries.
14Advantages, Disadvantages, and Trade-offs
Every design carries a price. Here is the honest balance sheet of searching by meaning.
Advantages
- Finds content by meaning, not only by shared words.
- Handles paraphrases, long natural questions, and, with the right model, many languages.
- Works for more than text: images, audio, and products can be embedded too.
- Approximate search stays fast on millions of vectors.
- Combines with keywords and filters in one system.
- Compression makes large collections affordable.
- Provides the retrieval step for applications that use large language models.
Disadvantages
- Needs an embedding model, which costs money, time, and care.
- Memory-hungry, since vectors and graphs should live in cache.
- Indexing and merging are processor-heavy.
- Results are approximate and hard to explain.
- Weak on exact codes, names, and rare terms without keywords.
- Quality depends on the model, and changing the model means rebuilding everything.
- Scores are not comparable across models and are not probabilities.
| Trade-off | Leaning toward quality | Leaning toward cost |
|---|---|---|
| Model size and dimensions | Large model, many dimensions: nuanced, heavy | Small model, few dimensions: light, less nuance |
| Precision of stored numbers | Full precision: best accuracy, most memory | Quantized: much smaller, some loss |
| Candidates per search | Many: higher recall, slower | Few: faster, lower recall |
| Graph quality | Many links and large construction effort: better recall, slower indexing | Modest: faster indexing, lower recall |
| Chunk size | Small chunks: precise, many vectors | Large chunks: fewer vectors, blurrier meaning |
| Exact versus approximate | Exact: perfect ranking, slow at scale | Approximate: fast, may miss a few |
| Replicas | More: read capacity and safety, each builds graphs | Fewer: cheaper indexing and memory |
| Hybrid versus vector only | Hybrid: robust, more complex | Vector only: simple, weak on exact terms |
A detailed map with fine contour lines helps hikers find their way, but it takes longer to print and is heavier to carry. A rough sketch is light and quick, but may miss a cliff. The right map depends on the trail.
How ambitious should you be?
| Situation | Recommended stance |
|---|---|
| Small collection, thousands of documents | Simple: default settings, exact search or default graph, no compression needed |
| Medium collection, millions of documents | Graph with modest compression, hybrid search, measured recall |
| Large collection, hundreds of millions of vectors | Strong compression with rescoring, dedicated hardware, careful shard and segment planning |
| Exact terms matter, such as part numbers | Hybrid search, with keyword fields for codes |
| Sensitive or regulated data | Models that run inside your environment, strict access control, clear retention |
15High Availability and Reliability
Vector indexes are ordinary indexes in the way they are replicated and recovered, with a few special sensitivities.
Replicas and graphs
In the standard model, each replica repeats the indexing work, so each replica builds its own graph, in its own segments. The graphs on two copies of a shard therefore differ in shape, although they hold the same vectors. Two searches served by different copies may return slightly different approximate results, because the walks follow different graphs. The differences are small, and the ranking of the best results is usually the same, but they mean that identical queries can occasionally give slightly different lists.
Two people each draw a map of the same neighborhood from the same list of addresses. Their maps show the same houses, but the small connecting paths may be drawn differently. Two travelers following them may take slightly different routes and reach almost the same destination.
Recovery and memory
When a replica is rebuilt by copying files, vector files and graph files are copied like any other segment file. After a node restart, the file cache is cold: vectors and graphs must be read from disk again, and searches are slow until the cache warms. For large vector indexes, this warm-up can take minutes or longer, and search latency in that period may be many times higher. Plan restarts accordingly: restart one node at a time, and let each node warm up before moving on.
| Failure | Effect on vector search | Response |
|---|---|---|
| A data node crashes | Replicas take over, cold caches on the nodes that pick up traffic | Expect a latency bump, let caches warm |
| A node restarts | Its cache is empty, searches on its shards are slow at first | Rolling restarts, one node at a time, and warm-up time |
| Memory pressure | The operating system evicts vector pages, and latency spikes | More memory, compression, or fewer vectors per node |
| Embedding service is unavailable | New documents cannot be embedded, and text queries cannot be embedded | Retry with backoff, queue documents, fall back to keyword search |
| Model changed by mistake | Query vectors no longer match stored vectors, results degrade quietly | Pin model versions, monitor quality, keep a rollback index |
| Segment corruption | The affected copy is failed and rebuilt from another copy | Keep at least one replica, take snapshots |
| Upgrade of Elasticsearch | Graph and file formats may change with the underlying library | Read release notes, test on a copy, reindex if required |
Designing for resilience
Keep at least one replica for important indexes
It protects against node loss and serves searches while another copy recovers.
Size memory with headroom
If a node fails, the remaining nodes must hold more vectors in their caches.
Have a keyword fallback
If embedding is unavailable, a plain keyword search keeps the application usable.
Snapshot regularly
Re-embedding millions of documents is slow and costly, so protect the vectors themselves.
Version everything
Pin the model, the mapping, and the chunking rule, and record them with the index.
Losing the vectors means paying again for every model call. For large collections, that can cost days of time and real money. Snapshots of vector indexes are cheap insurance compared with recomputing them.
16Security and Privacy Considerations
A vector looks like harmless numbers. It is not. It carries the meaning of your content, and the path to it has its own risks.
Vectors leak meaning
An embedding is a compressed description of the content. Research has shown that, with enough effort, parts of the original text can be reconstructed from its embedding, or at least that sensitive attributes can be inferred. You should therefore treat vectors with the same sensitivity as the text they came from. If the text is confidential, so are its vectors, in the index, in snapshots, and in backups.
A detailed description of a secret document, even without the document itself, still reveals a lot. The description needs the same lock as the document.
Access control still applies
Restrict who can search
Use roles to limit which users can read which indexes. Similarity search does not bypass permissions.
Permissions per document
Where your subscription supports it, restrict which documents a user may see. Test that kNN results respect it. In search designs with filters, put the permission in the filter, inside the kNN part.
Hide the vector field
Most users need results, not the raw vectors. Do not grant access to the vector field, and do not return it.
Carry permissions in chunks
When documents are chunked, each chunk must inherit the permissions of its parent, or a chunk may leak.
The embedding path is a data path
When embedding happens in an outside service, your content leaves your environment. Check the provider’s terms: whether data is stored, used for training, or logged, where it is processed, and how it is protected. For regulated or confidential data, prefer models that run inside your own cluster or network. Protect the credentials used to call the service, keep them in secure storage, rotate them, and give each application its own.
Risks specific to retrieval for language models
When vector search feeds a large language model, a further set of risks appears. The retrieved text becomes part of the model’s input. If a retrieved document contains hidden instructions, such as text that tells the model to ignore its rules or reveal secrets, the model may obey them. This is called prompt injection through retrieved content. Reduce the risk by controlling what enters the index, marking retrieved text clearly as data and not as instructions, limiting what the model is allowed to do, and reviewing outputs where the stakes are high.
Poisoning and quality attacks
Someone who can add documents to the index can also try to influence what is retrieved, by writing content designed to be close to popular queries. If your index accepts content from the public, treat it as untrusted: moderate it, limit who can write, and separate it from trusted content.
Deletion and retention
When a document is deleted, its vector remains in the segment’s files until a merge removes it, and older history may be kept for recovery. Snapshots also keep copies. For privacy requests to erase personal data, know how long these copies last, and plan deletion and merging accordingly. Remember that vectors of derived chunks must be deleted as well.
Resource abuse
Vector search is costly. A user, or an attacker, who can send unlimited queries with huge candidate counts or large k can burn processors and memory. Set limits on k and candidates, add rate limits and timeouts in front of search endpoints, and separate heavy workloads from interactive ones.
Turn on authentication and encryption, even for clusters that seem internal. Encrypt data at rest on the nodes that hold vectors, and secure the snapshot storage. Many exposures of indexed data start with a cluster that was described as “only internal.”
17Monitoring, Logging, and Metrics
Vector search can fail in a way that keyword search rarely does: it can keep answering quickly while quietly getting worse. Watch both speed and quality.
Windows onto the index
| Window | What it shows | Use it to |
|---|---|---|
| Index disk usage analysis | Bytes used by vectors and graphs, by field | See what vectors cost on disk |
| Segment listing | Number and size of segments | Spot too many small segments |
| Index and node statistics | Search and indexing times, merge activity, and in newer versions the number of vectors | Track trends |
| Profile option on a search | Time in the vector search, and in newer versions how many vectors were compared | See what one query costs |
| Slow logs | Searches and indexing operations over a time threshold | Find expensive queries |
| Machine-learning statistics | Model deployments, inference times, queues | Watch embedding latency and throughput |
| Operating system metrics | Memory, cache, disk reads, processor | See whether the cache is holding the vectors |
Metrics to chart
| Metric | What it tells you | Warning sign |
|---|---|---|
| Search latency percentiles | How long queries take, including the slow tail | A rising slow tail |
| Recall on a fixed test set | How many true neighbors are found | Any drop after a change |
| Disk reads during searches | Whether vectors are served from cache | High reads on vector nodes |
| Segments per shard | Number of graph walks per search | Growth without merges catching up |
| Merge time and throttling | Whether merges keep up with writes | Merges constantly running |
| Indexing throughput | Speed of loading, including embedding | Sudden drops |
| Embedding latency and errors | Health of the model path | Timeouts, rate limits, rising error counts |
| Share of deleted documents | Dead vectors in graphs | A large share for a long time |
| Free memory for the file cache | Headroom for vectors | Shrinking headroom |
| Result quality signals | Clicks, ratings, and reviewed samples | A decline in useful results |
Measuring quality: recall and relevance
There are two different questions. Recall asks whether the approximate search finds what the exact search would find. You can measure it by taking a sample of real queries, running both the approximate search and an exact scoring search, and counting how many of the exact top results appear in the approximate top results. Relevance asks whether the results are actually useful to people. It needs human judgments or behavior data, and depends on the model and the chunks more than on the search settings. Both matter, and they are affected by different changes.
Build a test set
Collect a few hundred real queries, with judgments of which documents are good answers where possible.
Measure recall against exact search
This tells you whether the search settings and compression are losing true neighbors.
Measure relevance against judgments
This tells you whether the model, chunks, and hybrid mix are helping people.
Repeat after every change
Model, chunking, compression, and settings changes can each move both numbers.
Reading the patterns
Steady latency, high recall
Vectors are in the cache, segments are few, and results are judged useful.
Slow tail, high disk reads
Vectors do not fit in memory. Compress, add memory, or reduce the data on the node.
Many segments, slower searches
Heavy writes create many graphs. Lengthen refresh and let merges catch up.
Speed fine, quality declining
Check whether the model or its input changed, whether vectors are stale, and whether recall dropped after a setting change.
Embedding queue growing
Model inference cannot keep up. Add capacity, batch requests, or slow the load.
Many deleted documents
Update-heavy data leaves dead vectors. Consider the data model, and let merges clean up.
Alert on a rising slow tail of search latency, on rising disk reads on vector nodes, on falling recall in a scheduled test, on embedding errors and timeouts, on segment counts that keep growing, and on the free memory left for the file cache.
18Design Patterns and Anti-patterns
Good habits make vector search accurate and affordable. Bad habits are behind most disappointing results and most surprising bills.
Patterns that work
Hybrid by default
Combine keyword and vector search, so exact terms and meaning both count. Pure vector search is the exception, not the rule.
Chunk with care and carry metadata
Cut long content along natural boundaries, and store title, source, date, and permissions with every chunk.
Filters inside the kNN part
Apply permissions and business conditions during the search, so that every returned result satisfies them.
Compress, then rescore
Use quantization to fit memory, and let rescoring with full vectors recover accuracy.
Alias-based model switching
Build a new index for each model version and move an alias when the new one is proven.
A fixed evaluation set
Keep a set of queries and judgments, and run it after every change to model, chunking, compression, or settings.
Batch and isolate embedding
Embed in batches, on dedicated machines or services, with retries and queues, so search stays smooth.
A good research librarian uses both the card catalog for exact titles and her understanding of topics for vague requests. She knows which shelf each section is on, keeps notes on who may read restricted books, and tests her methods by asking whether readers found what they wanted.
Anti-patterns to avoid
What people do
They mix vectors from different models, or different versions of one model, in the same field, for example after a quiet upgrade of the embedding service.
Why it hurts
Vectors from different models live on different maps. Results look plausible but are wrong, and the problem is hard to notice.
Better approach
Pin model versions, give each model its own field or index, and rebuild fully when the model changes.
What people do
They embed whole long documents as a single vector, or let the model silently cut the text at its input limit.
Why it hurts
The vector blurs many ideas together, or ignores the end of the document. Searches miss specific passages.
Better approach
Chunk documents into meaningful passages within the model’s limit, and embed each one.
What people do
They use pure vector search for queries that are mostly exact codes, names, or identifiers.
Why it hurts
Embeddings blur exact strings. A search for one part number may return similar-looking ones, but not the exact one.
Better approach
Use keyword fields for exact values, and combine with vector search for descriptive text.
What people do
They give the Java heap almost all of the machine’s memory, leaving little for the operating system’s file cache.
Why it hurts
Vectors and graphs cannot stay in memory, so searches read the disk at every step of the graph walk, and latency explodes.
Better approach
Keep the heap moderate, size memory so the searched form of the vectors fits in the cache, and compress if needed.
What people do
They tune settings by trying a few queries and judging the results by eye.
Why it hurts
Small samples hide problems. A change that helps three queries may hurt hundreds.
Better approach
Keep a fixed test set, measure recall and relevance, and compare before and after each change.
What people do
They update the text of documents but do not recompute their vectors.
Why it hurts
Vectors describe the old text. Documents are returned for meanings that they no longer have.
Better approach
Re-embed whenever the embedded text changes, and store a hash of the text to detect stale vectors.
What people do
They return the vector with every search hit, and fetch source fields that include vectors in every request.
Why it hurts
Each vector is hundreds of numbers. Responses become huge, and network and processing time are wasted.
Better approach
Keep vectors out of the returned source and request them only when needed.
What people do
They spread a small vector index over many shards, or request enormous k and candidate values “to be safe.”
Why it hurts
Every shard runs its own graph walks, and large candidate counts multiply the work. Latency and cost rise for little gain.
Better approach
Use few, moderately sized shards, and set candidates by measuring the recall you actually need.
19Best Practices and Common Mistakes
A short list you could hand to a new teammate on their first day.
Best practices
Choose and test the model on your data
Benchmarks are a guide. Your own queries decide.
Match dimensions and similarity
Follow the model’s documentation exactly.
Chunk and label
Meaningful chunks with metadata for filtering and display.
Go hybrid
Keywords for exact terms, vectors for meaning.
Plan for the cache
Moderate heap, compression, and enough RAM for the searched vectors.
Measure recall and relevance
Repeat after every change.
Version and switch by alias
New model, new index, then move the alias.
Protect vectors like text
Access control, encryption, and retention apply to vectors too.
Common mistakes
- Treating vector search as magic. It is only as good as the model, the chunks, and the data.
- Forgetting that scores are relative. A high score is not proof of relevance, and scores differ between models.
- Setting the wrong number of dimensions. The field must match the model exactly, or indexing fails.
- Skipping normalization when using dot product. The ranking is then distorted.
- Ignoring model input limits. Text beyond the limit is not embedded.
- Forgetting the prefixes some models need. Quality drops quietly.
- Using a post-filter and expecting k results. Filters given outside the kNN part can leave fewer results.
- Neglecting the cold cache after restarts. Latency is high until vectors are read back into memory.
- Assuming the mapping can be changed in place. Dimensions and similarity generally cannot.
Myths versus facts
| Myth | Fact |
|---|---|
| Vector search replaces keyword search. | Each is strong where the other is weak, and hybrid search combines them. |
| A dense vector field creates the embeddings. | It stores them. A separate model creates them. |
| More dimensions always means better results. | More dimensions cost more, and may help only modestly. The model matters more. |
| Approximate search returns wrong answers. | It returns almost all of the true neighbors, and recall can be measured and tuned. |
| Vectors are anonymous numbers. | They carry the meaning of the content and can leak information. |
| Compression ruins quality. | Moderate compression is nearly free, and rescoring recovers most of the rest. |
| Similarity scores are probabilities. | They are model-dependent closeness values used for ranking. |
| I can swap the model and keep the old vectors. | Vectors from different models are incompatible. Everything must be recomputed. |
The quality of vector search is decided upstream: by the model, by how content is chunked, and by what is kept in the index. Tuning the search settings can only recover what was already there. Spend your effort on the inputs first.
20Real-World and Industry Examples
Where does the dense vector field quietly change what users can do?
Many well-known organizations have said publicly that Elasticsearch is part of their systems, including Wikipedia’s search, Netflix, Uber, and Stack Overflow. They rarely publish how they use vectors, so the scenarios below are typical patterns for such workloads, not descriptions of anyone’s private configuration.
Retrieval for question answering with a language model
One of the most common uses today is to give a large language model the right information to answer a question. The pattern is often called retrieval-augmented generation. The documents of a knowledge base are chunked and embedded into a dense vector field. When a user asks a question, the question is embedded, the nearest chunks are retrieved, and both the question and the chunks are handed to the language model, which writes an answer based on them.
flowchart LR
U["User question"] --> E["Embed the question"]
E --> K["kNN search in Elasticsearch"]
K --> C["Top chunks of text"]
C --> P["Prompt with the question and the chunks"]
P --> L["Language model writes the answer"]
L --> A["Answer with sources"]
A student answering an exam question with an open book first finds the right pages, and then writes the answer from them. The search finds the pages, and the language model is the student. If the wrong pages are found, even a very good student writes a wrong answer.
Online store: search by intent
A retailer of the size of Amazon or eBay lets shoppers type “something warm for a winter camping trip.” Product titles and descriptions are embedded, and the search combines vector similarity with keyword matching and filters for size, price, and stock. Exact brand names and model numbers are handled by the keyword side, while descriptive queries are handled by the vector side.
Support center: answers from past tickets and manuals
A software company indexes help articles and resolved tickets in chunks. Agents and customers ask questions in their own words, and the system retrieves relevant passages. Permissions are stored with every chunk, and applied as filters inside the vector search, so that customers only see public articles.
Media catalog: “more like this”
A streaming service represents each title by a vector built from its description, genres, and viewer behavior. Finding similar titles means finding the nearest vectors to the one a viewer just watched, with filters for language, rating, and availability in the viewer’s country.
Image and product similarity
A marketplace embeds product photos with an image model. A shopper uploads a picture, its vector is computed, and the nearest product vectors are returned. Compression makes it practical to hold hundreds of millions of image vectors in memory.
Duplicate and near-duplicate detection
A content platform finds articles that are nearly the same by searching each new article’s vector against existing ones, with a similarity threshold. Only very close neighbors are treated as duplicates, and humans review the borderline ones.
Multilingual site search
A travel company serves visitors in many languages. A multilingual embedding model lets a question in one language find pages written in another, without translation. The team combines it with keyword search in each language for names of places and hotels.
In every example, the winning ideas are the same: a model matched to the content, chunks with useful metadata, filters applied inside the vector search, hybrid ranking for exact terms, compression to fit memory, and constant measurement of both recall and relevance.
21Frequently Asked Questions
Short answers to the questions that come up most often.
22Summary and Key Takeaways
If you skim only one part of this guide, make it this one.
The dense vector field lets Elasticsearch search by meaning. It stores, for each document, a fixed-length list of numbers made by an embedding model, indexes those lists in a layered graph, and finds the nearest ones to a query in milliseconds. It costs memory, indexing effort, and care, and it pays back with search that understands paraphrases, works across languages, and feeds language-model applications.
Key Takeaways
- What it is: a field holding one fixed-length vector per document, produced by an embedding model, searched by nearest-neighbor similarity.
- How closeness is measured: choose the metric your model recommends, typically cosine or dot product for normalized vectors, and remember that scores are for ranking, not probabilities.
- How search stays fast: an HNSW graph per segment lets a search visit a few thousand vectors instead of millions, at the price of approximate results whose recall you can measure and tune.
- How it fits in memory: vectors and graphs must live in the operating system’s file cache, so keep the heap moderate, and use quantization with rescoring to shrink memory many times over.
- How to combine it: put filters inside the kNN search, and combine vectors with keywords in hybrid search, so both meaning and exact terms count.
- How to keep it healthy: keep segments few, plan for cold caches after restarts, embed in batches, re-embed when text changes, and switch models by building a new index and moving an alias.
- How to keep it safe and good: treat vectors as sensitive as the text, guard the embedding path, and measure both recall and relevance on a fixed test set after every change.