Skip to content

Commit b2c1c4e

Browse files
authored
New vector_rescore parameter as a quantized index type option (#124581)
This adds a new parameter to the quantized index mapping that allows default oversampling and rescoring to occur. This doesn't adjust any of the defaults. It allows it to be configured. When the user provides `rescore_vector: {oversample: <number>}` in the query it will overwrite it. For example, here is how to use it with bbq: ``` PUT rescored_bbq { "mappings": { "properties": { "vector": { "type": "dense_vector", "index_options": { "type": "bbq_hnsw", "rescore_vector": {"oversample": 3.0} } } } } } ``` Then, when querying, it will auto oversample the `k` by `3x` and rerank with the raw vectors. ``` POST _search { "knn": { "query_vector": [...], "field": "vector" } } ```
1 parent cd25958 commit b2c1c4e

File tree

13 files changed

+866
-58
lines changed

13 files changed

+866
-58
lines changed

docs/changelog/124581.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 124581
2+
summary: New `vector_rescore` parameter as a quantized index type option
3+
area: Vector Search
4+
type: enhancement
5+
issues: []

docs/reference/elasticsearch/mapping-reference/dense-vector.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,14 @@ $$$dense-vector-index-options$$$
287287
`confidence_interval`
288288
: (Optional, float) Only applicable to `int8_hnsw`, `int4_hnsw`, `int8_flat`, and `int4_flat` index types. The confidence interval to use when quantizing the vectors. Can be any value between and including `0.90` and `1.0` or exactly `0`. When the value is `0`, this indicates that dynamic quantiles should be calculated for optimized quantization. When between `0.90` and `1.0`, this value restricts the values used when calculating the quantization thresholds. For example, a value of `0.95` will only use the middle 95% of the values when calculating the quantization thresholds (e.g. the highest and lowest 2.5% of values will be ignored). Defaults to `1/(dims + 1)` for `int8` quantized vectors and `0` for `int4` for dynamic quantile calculation.
289289

290+
`rescore_vector`
291+
: (Optional, object) Functionality in [preview]. An optional section that configures automatic vector rescoring on knn queries for the given field. Only applicable to quantized index types.
292+
:::::{dropdown} Properties of `rescore_vector`
293+
`oversample`
294+
: (required, float) The amount to oversample the search results by. This value should be greater than `1.0` and less than `10.0`. The higher the value, the more vectors will be gathered and rescored with the raw values per shard.
295+
: In case a knn query specifies a `rescore_vector` parameter, the query `rescore_vector` parameter will be used instead.
296+
: See [oversampling and rescoring quantized vectors](docs-content://solutions/search/vector/knn.md#dense-vector-knn-search-rescoring) for details.
297+
:::::
290298
::::
291299

292300

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/41_knn_search_bbq_hnsw.yml

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,93 @@ setup:
244244
index: dynamic_dim_bbq_hnsw
245245
body:
246246
vector: [1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0]
247+
---
248+
"Test index configured rescore vector":
249+
- requires:
250+
cluster_features: ["mapper.dense_vector.rescore_vector"]
251+
reason: Needs rescore_vector feature
252+
- skip:
253+
features: "headers"
254+
- do:
255+
indices.create:
256+
index: bbq_rescore_hnsw
257+
body:
258+
settings:
259+
index:
260+
number_of_shards: 1
261+
mappings:
262+
properties:
263+
vector:
264+
type: dense_vector
265+
dims: 64
266+
index: true
267+
similarity: max_inner_product
268+
index_options:
269+
type: bbq_hnsw
270+
rescore_vector:
271+
oversample: 1.5
272+
273+
- do:
274+
bulk:
275+
index: bbq_rescore_hnsw
276+
refresh: true
277+
body: |
278+
{ "index": {"_id": "1"}}
279+
{ "vector": [0.077, 0.32 , -0.205, 0.63 , 0.032, 0.201, 0.167, -0.313, 0.176, 0.531, -0.375, 0.334, -0.046, 0.078, -0.349, 0.272, 0.307, -0.083, 0.504, 0.255, -0.404, 0.289, -0.226, -0.132, -0.216, 0.49 , 0.039, 0.507, -0.307, 0.107, 0.09 , -0.265, -0.285, 0.336, -0.272, 0.369, -0.282, 0.086, -0.132, 0.475, -0.224, 0.203, 0.439, 0.064, 0.246, -0.396, 0.297, 0.242, -0.028, 0.321, -0.022, -0.009, -0.001 , 0.031, -0.533, 0.45, -0.683, 1.331, 0.194, -0.157, -0.1 , -0.279, -0.098, -0.176] }
280+
{ "index": {"_id": "2"}}
281+
{ "vector": [0.196, 0.514, 0.039, 0.555, -0.042, 0.242, 0.463, -0.348, -0.08 , 0.442, -0.067, -0.05 , -0.001, 0.298, -0.377, 0.048, 0.307, 0.159, 0.278, 0.119, -0.057, 0.333, -0.289, -0.438, -0.014, 0.361, -0.169, 0.292, -0.229, 0.123, 0.031, -0.138, -0.139, 0.315, -0.216, 0.322, -0.445, -0.059, 0.071, 0.429, -0.602, -0.142, 0.11 , 0.192, 0.259, -0.241, 0.181, -0.166, 0.082, 0.107, -0.05 , 0.155, 0.011, 0.161, -0.486, 0.569, -0.489, 0.901, 0.208, 0.011, -0.209, -0.153, -0.27 , -0.013] }
282+
{ "index": {"_id": "3"}}
283+
{ "vector": [0.196, 0.514, 0.039, 0.555, -0.042, 0.242, 0.463, -0.348, -0.08 , 0.442, -0.067, -0.05 , -0.001, 0.298, -0.377, 0.048, 0.307, 0.159, 0.278, 0.119, -0.057, 0.333, -0.289, -0.438, -0.014, 0.361, -0.169, 0.292, -0.229, 0.123, 0.031, -0.138, -0.139, 0.315, -0.216, 0.322, -0.445, -0.059, 0.071, 0.429, -0.602, -0.142, 0.11 , 0.192, 0.259, -0.241, 0.181, -0.166, 0.082, 0.107, -0.05 , 0.155, 0.011, 0.161, -0.486, 0.569, -0.489, 0.901, 0.208, 0.011, -0.209, -0.153, -0.27 , -0.013] }
284+
285+
- do:
286+
headers:
287+
Content-Type: application/json
288+
search:
289+
rest_total_hits_as_int: true
290+
index: bbq_rescore_hnsw
291+
body:
292+
knn:
293+
field: vector
294+
query_vector: [0.128, 0.067, -0.08 , 0.395, -0.11 , -0.259, 0.473, -0.393,
295+
0.292, 0.571, -0.491, 0.444, -0.288, 0.198, -0.343, 0.015,
296+
0.232, 0.088, 0.228, 0.151, -0.136, 0.236, -0.273, -0.259,
297+
-0.217, 0.359, -0.207, 0.352, -0.142, 0.192, -0.061, -0.17 ,
298+
-0.343, 0.189, -0.221, 0.32 , -0.301, -0.1 , 0.005, 0.232,
299+
-0.344, 0.136, 0.252, 0.157, -0.13 , -0.244, 0.193, -0.034,
300+
-0.12 , -0.193, -0.102, 0.252, -0.185, -0.167, -0.575, 0.582,
301+
-0.426, 0.983, 0.212, 0.204, 0.03 , -0.276, -0.425, -0.158]
302+
k: 3
303+
num_candidates: 3
304+
305+
- match: { hits.total: 3 }
306+
- set: { hits.hits.0._score: rescore_score0 }
307+
- set: { hits.hits.1._score: rescore_score1 }
308+
- set: { hits.hits.2._score: rescore_score2 }
309+
310+
- do:
311+
headers:
312+
Content-Type: application/json
313+
search:
314+
rest_total_hits_as_int: true
315+
index: bbq_rescore_hnsw
316+
body:
317+
query:
318+
script_score:
319+
query: {match_all: {} }
320+
script:
321+
source: "double similarity = dotProduct(params.query_vector, 'vector'); return similarity < 0 ? 1 / (1 + -1 * similarity) : similarity + 1"
322+
params:
323+
query_vector: [0.128, 0.067, -0.08 , 0.395, -0.11 , -0.259, 0.473, -0.393,
324+
0.292, 0.571, -0.491, 0.444, -0.288, 0.198, -0.343, 0.015,
325+
0.232, 0.088, 0.228, 0.151, -0.136, 0.236, -0.273, -0.259,
326+
-0.217, 0.359, -0.207, 0.352, -0.142, 0.192, -0.061, -0.17 ,
327+
-0.343, 0.189, -0.221, 0.32 , -0.301, -0.1 , 0.005, 0.232,
328+
-0.344, 0.136, 0.252, 0.157, -0.13 , -0.244, 0.193, -0.034,
329+
-0.12 , -0.193, -0.102, 0.252, -0.185, -0.167, -0.575, 0.582,
330+
-0.426, 0.983, 0.212, 0.204, 0.03 , -0.276, -0.425, -0.158]
331+
332+
# Compare scores as hit IDs may change depending on how things are distributed
333+
- match: { hits.total: 3 }
334+
- match: { hits.hits.0._score: $rescore_score0 }
335+
- match: { hits.hits.1._score: $rescore_score1 }
336+
- match: { hits.hits.2._score: $rescore_score2 }

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/41_knn_search_byte_quantized.yml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,3 +611,92 @@ setup:
611611
- match: { hits.hits.0._id: "1"}
612612
- match: { hits.hits.1._id: "2"}
613613
- match: { hits.hits.2._id: "3"}
614+
---
615+
"Test index configured rescore vector":
616+
- requires:
617+
cluster_features: ["mapper.dense_vector.rescore_vector"]
618+
reason: Needs rescore_vector feature
619+
- skip:
620+
features: "headers"
621+
- do:
622+
indices.create:
623+
index: int8_rescore_hnsw
624+
body:
625+
settings:
626+
index:
627+
number_of_shards: 1
628+
mappings:
629+
properties:
630+
vector:
631+
type: dense_vector
632+
dims: 64
633+
index: true
634+
similarity: max_inner_product
635+
index_options:
636+
type: int8_hnsw
637+
rescore_vector:
638+
oversample: 1.5
639+
640+
- do:
641+
bulk:
642+
index: int8_rescore_hnsw
643+
refresh: true
644+
body: |
645+
{ "index": {"_id": "1"}}
646+
{ "vector": [0.077, 0.32 , -0.205, 0.63 , 0.032, 0.201, 0.167, -0.313, 0.176, 0.531, -0.375, 0.334, -0.046, 0.078, -0.349, 0.272, 0.307, -0.083, 0.504, 0.255, -0.404, 0.289, -0.226, -0.132, -0.216, 0.49 , 0.039, 0.507, -0.307, 0.107, 0.09 , -0.265, -0.285, 0.336, -0.272, 0.369, -0.282, 0.086, -0.132, 0.475, -0.224, 0.203, 0.439, 0.064, 0.246, -0.396, 0.297, 0.242, -0.028, 0.321, -0.022, -0.009, -0.001 , 0.031, -0.533, 0.45, -0.683, 1.331, 0.194, -0.157, -0.1 , -0.279, -0.098, -0.176] }
647+
{ "index": {"_id": "2"}}
648+
{ "vector": [0.196, 0.514, 0.039, 0.555, -0.042, 0.242, 0.463, -0.348, -0.08 , 0.442, -0.067, -0.05 , -0.001, 0.298, -0.377, 0.048, 0.307, 0.159, 0.278, 0.119, -0.057, 0.333, -0.289, -0.438, -0.014, 0.361, -0.169, 0.292, -0.229, 0.123, 0.031, -0.138, -0.139, 0.315, -0.216, 0.322, -0.445, -0.059, 0.071, 0.429, -0.602, -0.142, 0.11 , 0.192, 0.259, -0.241, 0.181, -0.166, 0.082, 0.107, -0.05 , 0.155, 0.011, 0.161, -0.486, 0.569, -0.489, 0.901, 0.208, 0.011, -0.209, -0.153, -0.27 , -0.013] }
649+
{ "index": {"_id": "3"}}
650+
{ "vector": [0.196, 0.514, 0.039, 0.555, -0.042, 0.242, 0.463, -0.348, -0.08 , 0.442, -0.067, -0.05 , -0.001, 0.298, -0.377, 0.048, 0.307, 0.159, 0.278, 0.119, -0.057, 0.333, -0.289, -0.438, -0.014, 0.361, -0.169, 0.292, -0.229, 0.123, 0.031, -0.138, -0.139, 0.315, -0.216, 0.322, -0.445, -0.059, 0.071, 0.429, -0.602, -0.142, 0.11 , 0.192, 0.259, -0.241, 0.181, -0.166, 0.082, 0.107, -0.05 , 0.155, 0.011, 0.161, -0.486, 0.569, -0.489, 0.901, 0.208, 0.011, -0.209, -0.153, -0.27 , -0.013] }
651+
652+
- do:
653+
headers:
654+
Content-Type: application/json
655+
search:
656+
rest_total_hits_as_int: true
657+
index: int8_rescore_hnsw
658+
body:
659+
knn:
660+
field: vector
661+
query_vector: [0.128, 0.067, -0.08 , 0.395, -0.11 , -0.259, 0.473, -0.393,
662+
0.292, 0.571, -0.491, 0.444, -0.288, 0.198, -0.343, 0.015,
663+
0.232, 0.088, 0.228, 0.151, -0.136, 0.236, -0.273, -0.259,
664+
-0.217, 0.359, -0.207, 0.352, -0.142, 0.192, -0.061, -0.17 ,
665+
-0.343, 0.189, -0.221, 0.32 , -0.301, -0.1 , 0.005, 0.232,
666+
-0.344, 0.136, 0.252, 0.157, -0.13 , -0.244, 0.193, -0.034,
667+
-0.12 , -0.193, -0.102, 0.252, -0.185, -0.167, -0.575, 0.582,
668+
-0.426, 0.983, 0.212, 0.204, 0.03 , -0.276, -0.425, -0.158]
669+
k: 3
670+
num_candidates: 3
671+
672+
- match: { hits.total: 3 }
673+
- set: { hits.hits.0._score: rescore_score0 }
674+
- set: { hits.hits.1._score: rescore_score1 }
675+
- set: { hits.hits.2._score: rescore_score2 }
676+
677+
- do:
678+
headers:
679+
Content-Type: application/json
680+
search:
681+
rest_total_hits_as_int: true
682+
body:
683+
query:
684+
script_score:
685+
query: {match_all: {} }
686+
script:
687+
source: "double similarity = dotProduct(params.query_vector, 'vector'); return similarity < 0 ? 1 / (1 + -1 * similarity) : similarity + 1"
688+
params:
689+
query_vector: [0.128, 0.067, -0.08 , 0.395, -0.11 , -0.259, 0.473, -0.393,
690+
0.292, 0.571, -0.491, 0.444, -0.288, 0.198, -0.343, 0.015,
691+
0.232, 0.088, 0.228, 0.151, -0.136, 0.236, -0.273, -0.259,
692+
-0.217, 0.359, -0.207, 0.352, -0.142, 0.192, -0.061, -0.17 ,
693+
-0.343, 0.189, -0.221, 0.32 , -0.301, -0.1 , 0.005, 0.232,
694+
-0.344, 0.136, 0.252, 0.157, -0.13 , -0.244, 0.193, -0.034,
695+
-0.12 , -0.193, -0.102, 0.252, -0.185, -0.167, -0.575, 0.582,
696+
-0.426, 0.983, 0.212, 0.204, 0.03 , -0.276, -0.425, -0.158]
697+
698+
# Compare scores as hit IDs may change depending on how things are distributed
699+
- match: { hits.total: 3 }
700+
- match: { hits.hits.0._score: $rescore_score0 }
701+
- match: { hits.hits.1._score: $rescore_score1 }
702+
- match: { hits.hits.2._score: $rescore_score2 }

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/41_knn_search_half_byte_quantized.yml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,3 +642,92 @@ setup:
642642
index: dynamic_dim_hnsw_quantized
643643
body:
644644
vector: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
645+
---
646+
"Test index configured rescore vector":
647+
- requires:
648+
cluster_features: ["mapper.dense_vector.rescore_vector"]
649+
reason: Needs rescore_vector feature
650+
- skip:
651+
features: "headers"
652+
- do:
653+
indices.create:
654+
index: int4_rescore_hnsw
655+
body:
656+
settings:
657+
index:
658+
number_of_shards: 1
659+
mappings:
660+
properties:
661+
vector:
662+
type: dense_vector
663+
dims: 64
664+
index: true
665+
similarity: max_inner_product
666+
index_options:
667+
type: int4_hnsw
668+
rescore_vector:
669+
oversample: 1.5
670+
671+
- do:
672+
bulk:
673+
index: int4_rescore_hnsw
674+
refresh: true
675+
body: |
676+
{ "index": {"_id": "1"}}
677+
{ "vector": [0.077, 0.32 , -0.205, 0.63 , 0.032, 0.201, 0.167, -0.313, 0.176, 0.531, -0.375, 0.334, -0.046, 0.078, -0.349, 0.272, 0.307, -0.083, 0.504, 0.255, -0.404, 0.289, -0.226, -0.132, -0.216, 0.49 , 0.039, 0.507, -0.307, 0.107, 0.09 , -0.265, -0.285, 0.336, -0.272, 0.369, -0.282, 0.086, -0.132, 0.475, -0.224, 0.203, 0.439, 0.064, 0.246, -0.396, 0.297, 0.242, -0.028, 0.321, -0.022, -0.009, -0.001 , 0.031, -0.533, 0.45, -0.683, 1.331, 0.194, -0.157, -0.1 , -0.279, -0.098, -0.176] }
678+
{ "index": {"_id": "2"}}
679+
{ "vector": [0.196, 0.514, 0.039, 0.555, -0.042, 0.242, 0.463, -0.348, -0.08 , 0.442, -0.067, -0.05 , -0.001, 0.298, -0.377, 0.048, 0.307, 0.159, 0.278, 0.119, -0.057, 0.333, -0.289, -0.438, -0.014, 0.361, -0.169, 0.292, -0.229, 0.123, 0.031, -0.138, -0.139, 0.315, -0.216, 0.322, -0.445, -0.059, 0.071, 0.429, -0.602, -0.142, 0.11 , 0.192, 0.259, -0.241, 0.181, -0.166, 0.082, 0.107, -0.05 , 0.155, 0.011, 0.161, -0.486, 0.569, -0.489, 0.901, 0.208, 0.011, -0.209, -0.153, -0.27 , -0.013] }
680+
{ "index": {"_id": "3"}}
681+
{ "vector": [0.196, 0.514, 0.039, 0.555, -0.042, 0.242, 0.463, -0.348, -0.08 , 0.442, -0.067, -0.05 , -0.001, 0.298, -0.377, 0.048, 0.307, 0.159, 0.278, 0.119, -0.057, 0.333, -0.289, -0.438, -0.014, 0.361, -0.169, 0.292, -0.229, 0.123, 0.031, -0.138, -0.139, 0.315, -0.216, 0.322, -0.445, -0.059, 0.071, 0.429, -0.602, -0.142, 0.11 , 0.192, 0.259, -0.241, 0.181, -0.166, 0.082, 0.107, -0.05 , 0.155, 0.011, 0.161, -0.486, 0.569, -0.489, 0.901, 0.208, 0.011, -0.209, -0.153, -0.27 , -0.013] }
682+
683+
- do:
684+
headers:
685+
Content-Type: application/json
686+
search:
687+
rest_total_hits_as_int: true
688+
index: int4_rescore_hnsw
689+
body:
690+
knn:
691+
field: vector
692+
query_vector: [0.128, 0.067, -0.08 , 0.395, -0.11 , -0.259, 0.473, -0.393,
693+
0.292, 0.571, -0.491, 0.444, -0.288, 0.198, -0.343, 0.015,
694+
0.232, 0.088, 0.228, 0.151, -0.136, 0.236, -0.273, -0.259,
695+
-0.217, 0.359, -0.207, 0.352, -0.142, 0.192, -0.061, -0.17 ,
696+
-0.343, 0.189, -0.221, 0.32 , -0.301, -0.1 , 0.005, 0.232,
697+
-0.344, 0.136, 0.252, 0.157, -0.13 , -0.244, 0.193, -0.034,
698+
-0.12 , -0.193, -0.102, 0.252, -0.185, -0.167, -0.575, 0.582,
699+
-0.426, 0.983, 0.212, 0.204, 0.03 , -0.276, -0.425, -0.158]
700+
k: 3
701+
num_candidates: 3
702+
703+
- match: { hits.total: 3 }
704+
- set: { hits.hits.0._score: rescore_score0 }
705+
- set: { hits.hits.1._score: rescore_score1 }
706+
- set: { hits.hits.2._score: rescore_score2 }
707+
708+
- do:
709+
headers:
710+
Content-Type: application/json
711+
search:
712+
rest_total_hits_as_int: true
713+
body:
714+
query:
715+
script_score:
716+
query: {match_all: {} }
717+
script:
718+
source: "double similarity = dotProduct(params.query_vector, 'vector'); return similarity < 0 ? 1 / (1 + -1 * similarity) : similarity + 1"
719+
params:
720+
query_vector: [0.128, 0.067, -0.08 , 0.395, -0.11 , -0.259, 0.473, -0.393,
721+
0.292, 0.571, -0.491, 0.444, -0.288, 0.198, -0.343, 0.015,
722+
0.232, 0.088, 0.228, 0.151, -0.136, 0.236, -0.273, -0.259,
723+
-0.217, 0.359, -0.207, 0.352, -0.142, 0.192, -0.061, -0.17 ,
724+
-0.343, 0.189, -0.221, 0.32 , -0.301, -0.1 , 0.005, 0.232,
725+
-0.344, 0.136, 0.252, 0.157, -0.13 , -0.244, 0.193, -0.034,
726+
-0.12 , -0.193, -0.102, 0.252, -0.185, -0.167, -0.575, 0.582,
727+
-0.426, 0.983, 0.212, 0.204, 0.03 , -0.276, -0.425, -0.158]
728+
729+
# Compare scores as hit IDs may change depending on how things are distributed
730+
- match: { hits.total: 3 }
731+
- match: { hits.hits.0._score: $rescore_score0 }
732+
- match: { hits.hits.1._score: $rescore_score1 }
733+
- match: { hits.hits.2._score: $rescore_score2 }

0 commit comments

Comments
 (0)