Skip to content

Commit 31acb01

Browse files
committed
Rename option, add basic tests
1 parent 3776a01 commit 31acb01

File tree

4 files changed

+136
-12
lines changed

4 files changed

+136
-12
lines changed

qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/VectorSearchIT.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.elasticsearch.cluster.metadata.IndexMetadata;
1717
import org.elasticsearch.common.settings.Settings;
1818
import org.elasticsearch.common.xcontent.support.XContentMapValues;
19+
import org.elasticsearch.search.SearchFeatures;
1920

2021
import java.io.IOException;
2122
import java.util.List;
@@ -35,6 +36,7 @@ public VectorSearchIT(@Name("upgradedNodes") int upgradedNodes) {
3536
private static final String BYTE_INDEX_NAME = "byte_vector_index";
3637
private static final String QUANTIZED_INDEX_NAME = "quantized_vector_index";
3738
private static final String BBQ_INDEX_NAME = "bbq_vector_index";
39+
private static final String BBQ_INDEX_NAME_RESCORE = "bbq_vector_index_rescore";
3840
private static final String FLAT_QUANTIZED_INDEX_NAME = "flat_quantized_vector_index";
3941
private static final String FLAT_BBQ_INDEX_NAME = "flat_bbq_vector_index";
4042

@@ -507,6 +509,63 @@ public void testBBQVectorSearch() throws Exception {
507509
);
508510
}
509511

512+
public void testBBQVectorSearchOffheapRescoring() throws Exception {
513+
assumeTrue("Disabling off-heap rescoring is not supported", oldClusterHasFeature(SearchFeatures.BBQ_OFFHEAP_RESCORING));
514+
if (isOldCluster()) {
515+
String mapping = """
516+
{
517+
"properties": {
518+
"vector": {
519+
"type": "dense_vector",
520+
"dims": 64,
521+
"index": true,
522+
"similarity": "cosine",
523+
"index_options": {
524+
"type": "bbq_hnsw",
525+
"ef_construction": 100,
526+
"m": 16,
527+
"disable_offheap_cache_rescoring": true
528+
}
529+
}
530+
}
531+
}
532+
""";
533+
// create index and index 10 random floating point vectors
534+
createIndex(
535+
BBQ_INDEX_NAME_RESCORE,
536+
Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0).build(),
537+
mapping
538+
);
539+
index64DimVectors(BBQ_INDEX_NAME_RESCORE);
540+
// force merge the index
541+
client().performRequest(new Request("POST", "/" + BBQ_INDEX_NAME_RESCORE + "/_forcemerge?max_num_segments=1"));
542+
}
543+
Request searchRequest = new Request("POST", "/" + BBQ_INDEX_NAME_RESCORE + "/_search");
544+
searchRequest.setJsonEntity("""
545+
{
546+
"knn": {
547+
"field": "vector",
548+
"query_vector": [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
549+
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
550+
"k": 2,
551+
"num_candidates": 5,
552+
"rescore_vector": {
553+
"oversample": 2.0
554+
}
555+
}
556+
}
557+
""");
558+
Map<String, Object> response = search(searchRequest);
559+
assertThat(extractValue(response, "hits.total.value"), equalTo(2));
560+
List<Map<String, Object>> hits = extractValue(response, "hits.hits");
561+
assertThat("expected: 0 received" + hits.get(0).get("_id") + " hits: " + response, hits.get(0).get("_id"), equalTo("0"));
562+
assertThat(
563+
"expected_near: 0.99 received" + hits.get(0).get("_score") + "hits: " + response,
564+
(double) hits.get(0).get("_score"),
565+
closeTo(0.9934857, 0.005)
566+
);
567+
}
568+
510569
public void testFlatBBQVectorSearch() throws Exception {
511570
assumeTrue("Quantized vector search is not supported on this version", oldClusterHasFeature(BBQ_VECTOR_SEARCH_TEST_FEATURE));
512571
if (isOldCluster()) {

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

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,69 @@ setup:
338338
- match: { hits.hits.1._score: $rescore_score1 }
339339
- match: { hits.hits.2._score: $rescore_score2 }
340340
---
341+
"Test index configured rescore vector with no off-heap scoring":
342+
- requires:
343+
cluster_features: ["search.vectors.bbq_offheap_rescoring"]
344+
reason: Needs bbq_offheap_rescoring feature
345+
- skip:
346+
features: "headers"
347+
- do:
348+
indices.create:
349+
index: bbq_rescore_hnsw
350+
body:
351+
settings:
352+
index:
353+
number_of_shards: 1
354+
mappings:
355+
properties:
356+
vector:
357+
type: dense_vector
358+
dims: 64
359+
index: true
360+
similarity: max_inner_product
361+
index_options:
362+
type: bbq_hnsw
363+
disable_offheap_cache_rescoring: true
364+
rescore_vector:
365+
oversample: 1.5
366+
367+
- do:
368+
bulk:
369+
index: bbq_rescore_hnsw
370+
refresh: true
371+
body: |
372+
{ "index": {"_id": "1"}}
373+
{ "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] }
374+
{ "index": {"_id": "2"}}
375+
{ "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] }
376+
{ "index": {"_id": "3"}}
377+
{ "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] }
378+
379+
- do:
380+
headers:
381+
Content-Type: application/json
382+
search:
383+
rest_total_hits_as_int: true
384+
index: bbq_rescore_hnsw
385+
body:
386+
knn:
387+
field: vector
388+
query_vector: [0.128, 0.067, -0.08 , 0.395, -0.11 , -0.259, 0.473, -0.393,
389+
0.292, 0.571, -0.491, 0.444, -0.288, 0.198, -0.343, 0.015,
390+
0.232, 0.088, 0.228, 0.151, -0.136, 0.236, -0.273, -0.259,
391+
-0.217, 0.359, -0.207, 0.352, -0.142, 0.192, -0.061, -0.17 ,
392+
-0.343, 0.189, -0.221, 0.32 , -0.301, -0.1 , 0.005, 0.232,
393+
-0.344, 0.136, 0.252, 0.157, -0.13 , -0.244, 0.193, -0.034,
394+
-0.12 , -0.193, -0.102, 0.252, -0.185, -0.167, -0.575, 0.582,
395+
-0.426, 0.983, 0.212, 0.204, 0.03 , -0.276, -0.425, -0.158]
396+
k: 3
397+
num_candidates: 3
398+
399+
- match: { hits.total: 3 }
400+
- set: { hits.hits.0._score: rescore_score0 }
401+
- set: { hits.hits.1._score: rescore_score1 }
402+
- set: { hits.hits.2._score: rescore_score2 }
403+
---
341404
"Test index configured rescore vector updateable and settable to 0":
342405
- requires:
343406
cluster_features: ["mapper.dense_vector.rescore_zero_vector"]

server/src/main/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapper.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1623,7 +1623,7 @@ public boolean supportsDimension(int dims) {
16231623
public DenseVectorIndexOptions parseIndexOptions(String fieldName, Map<String, ?> indexOptionsMap, IndexVersion indexVersion) {
16241624
Object mNode = indexOptionsMap.remove("m");
16251625
Object efConstructionNode = indexOptionsMap.remove("ef_construction");
1626-
Object useDirectIONode = indexOptionsMap.remove("use_direct_io");
1626+
Object disableOffheapCacheRescoringNode = indexOptionsMap.remove("disable_offheap_cache_rescoring");
16271627

16281628
if (mNode == null) {
16291629
mNode = Lucene99HnswVectorsFormat.DEFAULT_MAX_CONN;
@@ -1642,10 +1642,10 @@ public DenseVectorIndexOptions parseIndexOptions(String fieldName, Map<String, ?
16421642
}
16431643
}
16441644

1645-
boolean useDirectIO = XContentMapValues.nodeBooleanValue("use_direct_io", false);
1645+
boolean disableOffheapCacheRescoring = XContentMapValues.nodeBooleanValue(disableOffheapCacheRescoringNode, false);
16461646

16471647
MappingParser.checkNoRemainingFields(fieldName, indexOptionsMap);
1648-
return new BBQHnswIndexOptions(m, efConstruction, rescoreVector, useDirectIO);
1648+
return new BBQHnswIndexOptions(m, efConstruction, rescoreVector, disableOffheapCacheRescoring);
16491649
}
16501650

16511651
@Override
@@ -2181,19 +2181,19 @@ public String toString() {
21812181
public static class BBQHnswIndexOptions extends QuantizedIndexOptions {
21822182
private final int m;
21832183
private final int efConstruction;
2184-
private final boolean useDirectIO;
2184+
private final boolean disableOffheapCacheRescoring;
21852185

2186-
public BBQHnswIndexOptions(int m, int efConstruction, RescoreVector rescoreVector, boolean useDirectIO) {
2186+
public BBQHnswIndexOptions(int m, int efConstruction, RescoreVector rescoreVector, boolean disableOffheapCacheRescoring) {
21872187
super(VectorIndexType.BBQ_HNSW, rescoreVector);
21882188
this.m = m;
21892189
this.efConstruction = efConstruction;
2190-
this.useDirectIO = useDirectIO;
2190+
this.disableOffheapCacheRescoring = disableOffheapCacheRescoring;
21912191
}
21922192

21932193
@Override
21942194
KnnVectorsFormat getVectorsFormat(ElementType elementType) {
21952195
assert elementType == ElementType.FLOAT;
2196-
return new ES818HnswBinaryQuantizedVectorsFormat(m, efConstruction, useDirectIO);
2196+
return new ES818HnswBinaryQuantizedVectorsFormat(m, efConstruction, disableOffheapCacheRescoring);
21972197
}
21982198

21992199
@Override
@@ -2207,12 +2207,12 @@ boolean doEquals(DenseVectorIndexOptions other) {
22072207
return m == that.m
22082208
&& efConstruction == that.efConstruction
22092209
&& Objects.equals(rescoreVector, that.rescoreVector)
2210-
&& useDirectIO == that.useDirectIO;
2210+
&& disableOffheapCacheRescoring == that.disableOffheapCacheRescoring;
22112211
}
22122212

22132213
@Override
22142214
int doHashCode() {
2215-
return Objects.hash(m, efConstruction, rescoreVector, useDirectIO);
2215+
return Objects.hash(m, efConstruction, rescoreVector, disableOffheapCacheRescoring);
22162216
}
22172217

22182218
@Override
@@ -2226,8 +2226,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
22262226
builder.field("type", type);
22272227
builder.field("m", m);
22282228
builder.field("ef_construction", efConstruction);
2229-
if (useDirectIO) {
2230-
builder.field("use_direct_io", true);
2229+
if (disableOffheapCacheRescoring) {
2230+
builder.field("disable_offheap_cache_rescoring", true);
22312231
}
22322232
if (rescoreVector != null) {
22332233
rescoreVector.toXContent(builder, params);

server/src/main/java/org/elasticsearch/search/SearchFeatures.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public Set<NodeFeature> getFeatures() {
3333
static final NodeFeature MULTI_MATCH_CHECKS_POSITIONS = new NodeFeature("search.multi.match.checks.positions");
3434
public static final NodeFeature BBQ_HNSW_DEFAULT_INDEXING = new NodeFeature("search.vectors.mappers.default_bbq_hnsw");
3535
public static final NodeFeature SEARCH_WITH_NO_DIMENSIONS_BUGFIX = new NodeFeature("search.vectors.no_dimensions_bugfix");
36+
public static final NodeFeature BBQ_OFFHEAP_RESCORING = new NodeFeature("search.vectors.bbq_offheap_rescoring");
3637

3738
@Override
3839
public Set<NodeFeature> getTestFeatures() {
@@ -43,7 +44,8 @@ public Set<NodeFeature> getTestFeatures() {
4344
INT_SORT_FOR_INT_SHORT_BYTE_FIELDS,
4445
MULTI_MATCH_CHECKS_POSITIONS,
4546
BBQ_HNSW_DEFAULT_INDEXING,
46-
SEARCH_WITH_NO_DIMENSIONS_BUGFIX
47+
SEARCH_WITH_NO_DIMENSIONS_BUGFIX,
48+
BBQ_OFFHEAP_RESCORING
4749
);
4850
}
4951
}

0 commit comments

Comments
 (0)