Dense vector search
|
This section documents the current Solr line (10.0; 9.10.x the maintained 9.x branch), written and verified against the Apache Solr Reference Guide. No specific patch version is pinned. Some capabilities (the Solr Operator on Kubernetes, the package-manager ecosystem, Learning To Rank model training, and expert plugin development) are linked, not documented in depth. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Solr searches embeddings with DenseVectorField — a fixed-length float vector indexed into an
HNSW graph — and the knn query parser, Solr’s counterpart to
Elasticsearch’s dense_vector and kNN.
This page covers declaring the field type, the knn query parser’s topK and filtering behaviour,
using knn as a second-pass re-ranker over a first-pass lexical query, combining lexical and vector
signals in one request, and where an embedding model fits in. DenseVectorField itself is introduced
alongside Solr’s other field types on Field types; `knn’s place among
Solr’s other query parsers — including local-params syntax — is on
Query parsers.
DenseVectorField mapping
Dimensionality and similarity function
vectorDimension is required and fixes the vector length — every value written to the field must
have exactly that many components, or indexing fails. similarityFunction picks the distance metric
used both to build the HNSW graph and to score knn matches: euclidean is the default (l2
distance), dot_product assumes unit-length vectors and is the cheapest to compute, and cosine
normalises for you when vectors are not already unit length. knnAlgorithm defaults to hnsw; a
cagra_hnsw build target is also available where GPU-accelerated index construction is configured.
<!-- in schema.xml / managed-schema -->
<fieldType name="knn_vector" class="solr.DenseVectorField"
vectorDimension="384"
similarityFunction="cosine"
knnAlgorithm="hnsw"
hnswM="16"
hnswEfConstruction="100"/>
<field name="embedding" type="knn_vector" indexed="true" stored="true"/>
HNSW tuning: hnswM and hnswEfConstruction
hnswM (default 16) is the maximum number of edges each node keeps in the HNSW graph — a higher
value improves recall at the cost of a larger index and slower builds. hnswEfConstruction (default
100) is the size of the candidate list explored while inserting each vector during indexing; raising
it builds a better-connected graph at the cost of slower indexing, but has no effect on query-time
latency. Both are set once, in the fieldType, and apply to every field that uses it.
curl -X POST "http://localhost:8983/solr/products/update?commit=true" \
-H 'Content-Type: application/json' \
-d '[
{
"id": "1",
"name": "wireless noise-cancelling headphones",
"embedding": [0.12, -0.98, 0.33, "...384 floats..."]
}
]'
index="false" (or omitting indexed) stores a vector without building a graph entry for it, which
means it can no longer be found by knn — there is no brute-force fallback query on the field the
way Elasticsearch’s script_score provides for an unindexed dense_vector. Full attribute reference:
Dense Vector Search.
The knn query parser
\{!knn f=<denseVectorField> topK=<k>}[v1,v2,…] returns the topK documents whose stored vector
is closest to the query vector, scored by the field’s configured similarityFunction. f and the
vector are required; topK defaults to 10.
curl --get "http://localhost:8983/solr/products/select" \
--data-urlencode 'q={!knn f=embedding topK=10}[0.11,-0.95,0.30,"...384 floats..."]' \
--data-urlencode 'wt=json'
Pre-filtering vs. post-filtering
Any fq present on the same request becomes an implicit pre-filter: Solr traverses the HNSW graph
restricted to the documents the filters allow, so topK results still come back even when the filter
is highly selective. includeTags/excludeTags control which fq clauses feed that pre-filter, and
an explicit preFilter local param can be supplied instead of relying on the ambient fq list. This
is the opposite of a post-filter — applying fq (or a wrapping bool/fq clause) to an already
computed knn result set only thins it out after the fact, so a selective filter can return fewer
than topK documents, or none. Prefer pre-filtering whenever the filter is expected to exclude a
large share of the collection; it plays the same role as the
filter inside Elasticsearch’s knn option.
curl --get "http://localhost:8983/solr/products/select" \
--data-urlencode 'q={!knn f=embedding topK=10}[0.11,-0.95,0.30,"...384 floats..."]' \
--data-urlencode 'fq=in_stock:true' \
--data-urlencode 'fq=price:[* TO 300]' \
--data-urlencode 'wt=json'
Full parameter list, including the advanced early-termination and ACORN filtered-search tunables: Dense Vector Search.
Using knn as a re-ranker
Rather than searching by vector alone, \{!rerank} lets a cheap first-pass query (typically the
usual BM25 q) retrieve a candidate set, then re-scores its top reRankDocs with a second, costlier
query — here, a \{!knn} clause — blended in by reRankWeight. This keeps the first pass fast over
the whole collection while spending the vector comparison only on documents that already look
promising lexically.
curl --get "http://localhost:8983/solr/products/select" \
--data-urlencode 'q=name:headphones' \
--data-urlencode 'rq={!rerank reRankQuery=$rqq reRankDocs=200 reRankWeight=2}' \
--data-urlencode 'rqq={!knn f=embedding topK=200}[0.11,-0.95,0.30,"...384 floats..."]' \
--data-urlencode 'wt=json'
The full \{!rerank}/ReRank mechanism — including Learning To Rank models, which build on the same
two-pass shape — is covered on Spell checking, suggestions & similar documents.
Hybrid lexical + vector search
Solr has no dedicated rank-fusion retriever comparable to Elasticsearch’s
rrf; combining a lexical clause and a
knn clause into one ranking means composing them in the same query tree. The standard/Lucene query
parser accepts a knn clause as an OR operand alongside an ordinary field query, so both
contribute to a single score:
curl --get "http://localhost:8983/solr/products/select" \
--data-urlencode 'q=(name:headphones OR {!knn f=embedding topK=20}[0.11,-0.95,0.30,"...384 floats..."])' \
--data-urlencode 'wt=json'
Because a BM25 term score and a knn similarity score are not on the same scale, an OR combination
like this weights whichever side happens to produce larger numbers — it is a starting point, not a
calibrated blend. The bool query parser’s should clauses give more control over which side
dominates by letting each clause reference a separately-tuned sub-query, and wrapping the whole thing
in \{!rerank} (above) is often a better fit when the lexical signal should decide the candidate set
and the vector signal should only refine the top of it. See
Query parsers for the bool and knn parsers' full local-params syntax.
Integrating an embedding model
Indexing still expects a pre-computed vector — there is no ingest-time processor that turns text
into an embedding for you the way Elasticsearch’s semantic_text field does, so an external embedding
step (an application-side call to an embedding service, or an
update request processor you write) has to run before
the document reaches Solr.
At query time, an experimental knn_text_to_vector parser closes part of that gap: it embeds the
query text itself, using a model registered in /schema/text-to-vector-model-store (Solr’s model
integration is built on LangChain4j), so a plain-text query
string can drive a knn search without the caller computing a vector at all.
curl --get "http://localhost:8983/solr/products/select" \
--data-urlencode 'q={!knn_text_to_vector model=my-embedding-model f=embedding topK=10}headphones that block background noise' \
--data-urlencode 'wt=json'
model and f are required; topK defaults to 10, same as knn. Because this parser only
embeds the query side, indexed documents must already carry vectors produced by the same model — mixing embeddings from two different models in one field silently degrades similarity scores, since
the vectors are no longer comparable. Setup and current limitations:
Dense Vector Search.
Where to go next
-
Field types — where
DenseVectorFieldsits among Solr’s other field-type implementations. -
Query parsers —
knnalongside Solr’s other specialised parsers, and thebool/local-params syntax hybrid queries build on. -
Spell checking, suggestions & similar documents — the full
\{!rerank}mechanism and Learning To Rank. -
Elasticsearch: Vector & semantic search — the equivalent
dense_vector/knn/retrievers model, for contrast.