Elasticsearch, vector and full text search in one engine
Elasticsearch is a distributed search engine that has, over the last several releases, come to treat vectors as first class alongside text. That means one system handles keyword matching and semantic similarity, with no second database to maintain and no manual merging of result sets. The current version is 9.5.0, released on 4 August 2026, and the project ships under a triple licence that includes the open source AGPLv3.
When Elasticsearch is a sensible choice for vectors
I will state a claim that organises the rest of this text: if Elasticsearch already runs in your system, adding a separate vector database rarely pays off. If it does not run, standing it up purely for vectors pays off even less often.
The reason lies in what this engine is by nature. It is an inverted index engine carrying an entire layer that dedicated vector databases lack: language analysers, tokenisation, synonym handling, aggregations, snippet highlighting, document level permissions. If your product needs those things, you get them alongside vectors, and that usually outweighs any difference in raw approximate search performance.
The reverse holds for a purely vector workload. Running an Elasticsearch cluster demands knowledge of Java heap tuning, shards, and replicas. If all you need is nearest neighbour search over two million vectors, Qdrant or Milvus will go up faster and cost less to keep running.
There is a third case where the decision is obvious. When user queries mix both intents, something like "March 2025 invoice from the energy supplier", neither system answers well on its own. You need exact matching on numbers and dates plus semantic matching on the rest. Elasticsearch does that in a single query, and that is its strongest argument.
semantic_text, the shortest path to semantic search
The classic vector path looks like this: pick an embedding model, split text into chunks, compute vectors, store them with metadata, then at query time compute the question's vector and search. Five steps, every one of which can go wrong.
The semantic_text field type collapses that into one. You declare the field type, and the engine picks the vector representation, chunks long text, and computes embeddings at index time on its own.
PUT /documents
{
"mappings": {
"properties": {
"content": { "type": "semantic_text" }
}
}
}The query is equally short and takes plain text rather than a vector:
GET /documents/_search
{
"query": {
"semantic": {
"field": "content",
"query": "how to settle a corrective invoice"
}
}
}In version 9.4 the defaults for this field changed in three places at once, which is worth knowing if you compare results across environments. The default model is now jina-v5, the default precision BFLOAT16, and the default index type disk_bbq. An index created before the upgrade keeps its old settings, so two environments on the same version can return slightly different rankings when one was built earlier.
The convenience has a price and it is fair to name it. You give up control over model choice and over how text is chunked. For documents with unusual structure, tables or code for instance, custom chunking usually beats the automatic kind. Start with semantic_text and move to manual configuration only once measurements say it is worth it.
Quantisation and DiskBBQ
Vectors consume a lot of memory, and that is the main cost of this part of the system. A thousand dimensions in single precision floats is four kilobytes per document, so four gigabytes per million documents before you count the index.
Quantisation means storing those numbers at lower precision. Elastic develops its own variant of the technique under the name BBQ, and vendor material cites compression up to 32 times and memory reduction of around 95 percent with recall preserved. Those are marketing figures, so treat them as an order of magnitude rather than a promise for your data. The mechanism itself is real, and on larger sets it is what determines cluster size.
Version 9.4 brought two concrete changes here. DiskBBQ, the variant keeping the structure on disk, is now the default for newly created indices, and its algorithm was rewritten so that searches under very restrictive filters run three times faster or better. A choice of precision also arrived: 1, 2, 4, or 7 bits per dimension.
That choice is where an hour of measurement pays for itself. One bit gives the largest saving and the lowest fidelity, seven bits the reverse. The rule that holds in practice: start on the defaults, measure recall against your own set of queries with expected results, and raise precision only when the outcome disappoints. Raising it as a precaution means paying for memory you do not need.
Hybrid search and rank fusion
Vector similarity alone fails wherever an exact string matters. An invoice number, a surname, a case reference, a device model name. The embedding model sees them as similar to other numbers and surnames, which produces results that are semantically correct and practically useless.
The answer is combining both approaches. The simpler variant is a plain boolean query where the keyword clause and the semantic clause both sit under should, each with its own boost. The drawback is that the two produce scores on different scales, so tuning boosts becomes guesswork.
Reciprocal rank fusion works better. Instead of comparing scores, it considers only each document's position on each list, which makes the scale problem disappear.
GET /documents/_search
{
"retriever": {
"rrf": {
"retrievers": [
{ "standard": { "query": { "match": { "content": "corrective invoice" } } } },
{ "standard": { "query": { "semantic": { "field": "content", "query": "how to settle a corrective invoice" } } } }
],
"rank_window_size": 50,
"rank_constant": 20
}
}
}Two parameters in that query are worth understanding. rank_window_size says how many results to take from each list before merging, and raising it improves the odds of surfacing a document visible on only one of them, at the cost of latency. rank_constant dampens the advantage of top positions, so raising it lets a document ranked tenth on both lists beat one ranked first on a single list and absent from the other.
Licensing, the largest source of confusion
This topic generates more confusion than everything else combined and resurfaces in every discussion about choosing a search engine, so it is worth taking apart.
In January 2021 the project stopped shipping under Apache 2.0 and moved to two licences not approved by the Open Source Initiative. That is when Amazon created the OpenSearch fork. In August 2024 Elastic added a third option, AGPLv3, which is an OSI approved open source licence, so today's arrangement is triple licensing: AGPLv3, Elastic License 2.0, and SSPL, with the choice left to the user.
The practical meaning of those differences reduces to one question: do you intend to offer Elasticsearch as a managed service to other companies. If not, which describes nearly every product team, all three licences let you do what you want, including commercial use and self hosting. Elastic License 2.0 forbids precisely the resale as a service, SSPL requires open sourcing the whole stack of such a service, and AGPLv3 imposes an obligation to publish modifications.
Feature tiering is a separate layer. The free Basic tier covers vector search, hybrid search, the semantic_text field, and all the core functionality. Paid tiers add security and management capabilities, field and document level permissions, single sign on, machine learning for anomaly detection. When planning a deployment it is this split, rather than the source licence, that more often turns out to be decisive.
Elasticsearch against dedicated vector databases
| Feature | Elasticsearch | Milvus | Qdrant | pgvector |
|---|---|---|---|---|
| Full text search | full, with analysers | basic, sparse vectors | basic | through an extension |
| Vector search | yes, with quantisation | yes, its main purpose | yes, its main purpose | yes, simpler |
| Aggregations and reporting | strong | limited | limited | full SQL |
| Operational entry barrier | high | high | low | none on an existing database |
| Licence | AGPLv3, ELv2, SSPL | Apache 2.0 | Apache 2.0 | PostgreSQL |
| Memory footprint | high, JVM based | medium | low | depends on the database |
Read that table through the lens of what you already run. With an existing PostgreSQL database and a set below a million vectors, the pgvector extension settles the matter in an evening and adds nothing new to maintain. Where you need genuine text search with stemming, synonyms, and highlighting, none of the dedicated vector databases will match Elasticsearch, because that is not what they set out to do.
Costs and resources
An Elasticsearch bill rarely follows document count and almost always follows memory. Three kinds of it are worth distinguishing, because confusing them produces clusters mis-sized in both directions.
The Java heap serves query structures and caches. The guidance is to stay under half the machine's memory and below roughly thirty gigabytes, because past that threshold the virtual machine stops using compressed pointers and effectively loses memory instead of gaining it.
The other half of memory belongs to the operating system, and it is what keeps index files in the page cache. For vector search that is critical: if the graph structure does not fit there, every query goes to disk and latency rises by an order of magnitude. Disk based variants, DiskBBQ among them, exist precisely for that situation and let you knowingly accept slower access in exchange for a smaller machine.
The third element is disk space, the cheapest of the three and the one most often over-provisioned while memory gets rationed. When sizing a cluster, compute the post quantisation vector size first, add the inverted index and metadata, and only then think about disks.
Common mistakes
The first is changing a field mapping on an existing index. Field types cannot be altered in place, so every correction means creating a new index and reindexing. Projects that did not plan for it discover this at the worst possible moment, during their first serious data model change. Index aliases exist exactly so that such a swap stays invisible to the application, and they are worth using from day one.
The second is mixing embedding models. Vectors from two different models are not comparable, even at identical dimensionality. Changing models requires recomputing the whole set rather than only new documents, otherwise old and new records stop competing meaningfully.
The third is too many shards. The default belief that more shards means better performance is the reverse of the truth for small datasets. Each shard is a separate Lucene index with its own overhead, so ten shards for a hundred thousand documents slow queries down rather than speeding them up.
The fourth concerns refresh. A written document is not immediately visible in results, because the index refreshes roughly once a second. An integration test that writes and immediately searches will flake. Tests should force a refresh; production should be patient, since forcing it on every write can bring a cluster down.
The fifth is deep pagination. Jumping to page one thousand through an offset parameter forces every node to sort an enormous set. Long lists call for cursoring on sort values, not for a larger offset.
FAQ
Is Elasticsearch free?
The source is available under a triple licence including the open source AGPLv3, and you may use it commercially at no cost. The free Basic tier covers vector and hybrid search. Paid subscriptions mainly add security and management features, field level permissions for instance.
How does Elasticsearch differ from OpenSearch?
OpenSearch is a fork Amazon created in 2021 after the Elasticsearch licence change, and the two projects have diverged functionally since. The newer vector machinery, the semantic_text field and the DiskBBQ variant among it, arrived after the split and has no direct counterpart.
Do I need a separate vector database alongside Elasticsearch?
Usually not. If a cluster already runs and holds your data, adding a second system means two write paths and the burden of keeping them consistent. A separate database only earns its place at a scale where cluster memory costs more than maintaining an extra component.
Which hybrid search method should I use?
Reciprocal rank fusion, unless you have a reason to steer the weights by hand. It needs no boost tuning because it ignores scores and looks only at positions, which removes the problem of incomparable scales between keyword and semantic matching.
How much memory does a cluster with vectors need?
It depends on storage precision. Without quantisation, budget four kilobytes per thousand dimensional vector, roughly four gigabytes per million documents, plus index overhead. Quantisation cuts that several times over, and the disk based variant goes lower still at the cost of latency.
Release details are covered in the Elasticsearch release notes, and the licensing position is explained in the official licensing FAQ.