Skip to main content

Vector Operations

Choose the Search Path

Elasticsearch vector queries depend on the server version and field mapping. An API method name alone does not select the server's vector engine.

PathBehavior
ES 7 script_scoreScore matching documents with a script.
ES 8.4+ _search knnNative approximate search on an indexed dense_vector.
Elastic7DialectUses script_score and descending _score ordering, not native knn.
Elastic8DialectProduces top-level knn with fixed k=10 and num_candidates=100; initPage does not change these values.

Vector Type Mapping

ES 7 uses dense_vector with dims, without the ES 8 index and similarity options below. Bind numeric Lists when writing. Expanded vector fields are also returned as numeric Lists, not JSON text.

In ES 7.17, a dense_vector field may be absent and reads as null, but explicitly inserting or updating it to null is rejected. To clear an existing vector, remove the field with a native command instead of assigning null in a builder update.

For the ES 8.4+ kNN example, create this index first:

PUT /product_index
{
"mappings": {"properties": {
"category": {"type": "keyword"},
"embedding": {"type": "dense_vector", "dims": 3, "index": true, "similarity": "cosine"}
}}
}

The similarity setting determines the index metric. Calling orderByL2 does not change a cosine index into an L2 index.

Restrict the Vector Candidates

To search only electronics documents, place the filter inside knn.filter and bind the vector as a numeric List:

String command = """
POST /product_index/_search
{
"size": 10,
"knn": {
"field": "embedding",
"query_vector": ?,
"k": 10,
"num_candidates": 100,
"filter": {"term": {"category": ?}}
}
}
""";
List<Map<String, Object>> rows = jdbc.queryForList(
command, new Object[] { List.of(0.1f, 0.2f, 0.3f), "electronics" });
Note

Elastic8Dialect places Fluent scalar conditions in top-level query, not knn.filter. Elasticsearch combines query and knn as alternatives; eq(...).orderByL2(...) is therefore not a mandatory candidate filter. Use the command above for tenant or category restrictions.

KNN Ordering

Vector ordering integrates L2, cosine, and inner product. Hamming, Jaccard, and BM25 are not integrated into these builder methods; full-text BM25 scoring is a separate feature.

The ES 7 builder generates script_score and orders by _score descending. It supports one vector score per query, not native approximate kNN.

On ES 7, declare embedding as dense_vector with dims=3; omit the ES 8 index and similarity options. For cosine scoring, submit script_score:

String command = """
POST /product_index/_search
{
"size": 10,
"query": {"script_score": {
"query": {"term": {"category": ?}},
"script": {
"source": "cosineSimilarity(params.vec, 'embedding') + 1.0",
"params": {"vec": ?}
}
}}
}
""";
List<Map<String, Object>> rows = jdbc.queryForList(
command, new Object[] { "electronics", List.of(0.1f, 0.2f, 0.3f) });

Every matching document must contain embedding. The script adds 1 to keep scores nonnegative; the score is not the original cosine similarity.

Distance Range Filtering

The ES 7 builder compares distances in a script and uses min_score=1 to retain matching documents. vectorByL2 compares L2 distance, vectorByCosine compares 1 - cosineSimilarity, and vectorByIP compares the negative inner product. All use < threshold.

Cosine similarity above 0.8 therefore uses 0.2; inner product above 0.8 uses -0.8. The range threshold is not the _score returned by vector ordering.

Hamming, Jaccard, and BM25 range filters are not available through the builder API.

Combined Queries

The ES 7 builder puts scalar predicates in script_score.query, scoring only matching documents. eq(...).orderByCosine(...) restricts the candidate set; range filters can also combine scalar predicates. For ES 8, use the candidate-filtering command above rather than assuming ES 7 behavior.

Read Vector Values

Read _DOC for the whole document. Read embedding as a numeric List; use Number.floatValue() when Float elements are needed:

List<?> values = (List<?>) row.get("embedding");
List<Float> embedding = values.stream()
.map(value -> ((Number) value).floatValue())
.toList();

Request syntax and version requirements: script_scorekNN.