Skip to content

Commit d987d0c

Browse files
authored
Release DiskBBQ(bbq_disk) index type for dense_vector fields (#135299)
This releases a new index type called DiskBBQ (configuration `bbq_disk`). DiskBBQ is a cluster based format that provides: - faster and cheaper indexing than HNSW - Better behavior in lower memory environments (degrades linearly, not exponentially) - Is near HNSW for QPS when the index is in memory Current restrictions that will be able to be removed in the future: - only floating point values are allowed currently - quantization is only to a single bit, so not recommended for low dimensionality vectors - all other restrictions for hnsw To utilize the format, its just like any other: ``` PUT vectors { "mappings": { "properties": { "vector": {"type": "dense_vector", "index_options": {"type": "disk_bbq"} } } } ``` Querying is just like any other field. ``` POST vectors/_search { "query": { "knn": { "field": "vector", "query_vector": <vector, "k": 3 } } } ``` `num_candidates` can be used for tuning approximate nature of the search. Or, more granular control can be provided by setting `visit_percentage` directly.
1 parent a77d358 commit d987d0c

File tree

53 files changed

+457
-1108
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+457
-1108
lines changed

docs/changelog/135299.yaml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
pr: 135299
2+
summary: Release DiskBBQ(`bbq_disk`) index type for `dense_vector` fields
3+
area: Vector Search
4+
type: feature
5+
issues: []
6+
highlight:
7+
title: Release DiskBBQ(`bbq_disk`) index type for `dense_vector` fields
8+
body: |-
9+
This provides a new index type called DiskBBQ (`bbq_disk`).
10+
DiskBBQ is a cluster based format that provides:
11+
- faster and cheaper indexing than HNSW
12+
- Better behavior in lower memory environments (degrades linearly, not exponentially)
13+
- Is near HNSW for QPS when the index is in memory
14+
15+
Current restrictions:
16+
- only floating point values are allowed currently
17+
- quantization is only to a single bit, so not recommended for low dimensionality vectors
18+
- all other restrictions that exist for `dense_vector` fields still apply
19+
20+
To utilize the format, its just like any other:
21+
[source,yaml]
22+
----------------------------
23+
PUT vectors
24+
{
25+
"mappings": {
26+
"properties": {
27+
"vector": {"type": "dense_vector", "index_options": {"type": "disk_bbq"}
28+
}
29+
}
30+
}
31+
----------------------------
32+
Querying is just like any other field.
33+
[source,yaml]
34+
----------------------------
35+
POST vectors/_search{
36+
"query": {
37+
"knn": {
38+
"field": "vector",
39+
"query_vector": <vector>,
40+
"k": 3
41+
}
42+
}
43+
}
44+
----------------------------
45+
`num_candidates` can be used for tuning approximate nature of the search.
46+
Or, more granular control can be provided by setting `visit_percentage` directly.
47+
notable: true

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -341,18 +341,19 @@ $$$dense-vector-index-options$$$
341341
`type`
342342
: (Required, string) The type of kNN algorithm to use. Can be either any of:
343343
* `hnsw` - This utilizes the [HNSW algorithm](https://arxiv.org/abs/1603.09320) for scalable approximate kNN search. This supports all `element_type` values.
344-
* `int8_hnsw` - The default index type for some float vectors:
344+
* `int8_hnsw` - The default index type for some float vectors:
345345
* {applies_to}`stack: ga 9.1` Default for float vectors with less than 384 dimensions.
346346
* {applies_to}`stack: ga 9.0` Default for float all vectors.
347347
This utilizes the [HNSW algorithm](https://arxiv.org/abs/1603.09320) in addition to automatically scalar quantization for scalable approximate kNN search with `element_type` of `float`. This can reduce the memory footprint by 4x at the cost of some accuracy. See [Automatically quantize vectors for kNN search](#dense-vector-quantization).
348348
* `int4_hnsw` - This utilizes the [HNSW algorithm](https://arxiv.org/abs/1603.09320) in addition to automatically scalar quantization for scalable approximate kNN search with `element_type` of `float`. This can reduce the memory footprint by 8x at the cost of some accuracy. See [Automatically quantize vectors for kNN search](#dense-vector-quantization).
349349
* `bbq_hnsw` - This utilizes the [HNSW algorithm](https://arxiv.org/abs/1603.09320) in addition to automatically binary quantization for scalable approximate kNN search with `element_type` of `float`. This can reduce the memory footprint by 32x at the cost of accuracy. See [Automatically quantize vectors for kNN search](#dense-vector-quantization).
350-
350+
351351
{applies_to}`stack: ga 9.1` `bbq_hnsw` is the default index type for float vectors with greater than or equal to 384 dimensions.
352352
* `flat` - This utilizes a brute-force search algorithm for exact kNN search. This supports all `element_type` values.
353-
* `int8_flat` - This utilizes a brute-force search algorithm in addition to automatically scalar quantization. Only supports `element_type` of `float`.
354-
* `int4_flat` - This utilizes a brute-force search algorithm in addition to automatically half-byte scalar quantization. Only supports `element_type` of `float`.
355-
* `bbq_flat` - This utilizes a brute-force search algorithm in addition to automatically binary quantization. Only supports `element_type` of `float`.
353+
* `int8_flat` - This utilizes a brute-force search algorithm in addition to automatic scalar quantization. Only supports `element_type` of `float`.
354+
* `int4_flat` - This utilizes a brute-force search algorithm in addition to automatic half-byte scalar quantization. Only supports `element_type` of `float`.
355+
* `bbq_flat` - This utilizes a brute-force search algorithm in addition to automatic binary quantization. Only supports `element_type` of `float`.
356+
* {applies_to}`stack: ga 9.2` `bbq_disk` - This utilizes a variant of [k-means clustering algorithm](https://en.wikipedia.org/wiki/K-means_clustering) in addition to automatic binary quantization to partition vectors and search subspaces rather than an entire graph structure as in with HNSW. Only supports `element_type` of `float`. This combines the benefits of BBQ quantization with partitioning to further reduces the required memory overhead when compared with HNSW and can effectively be run at the smallest possible RAM and heap sizes when HNSW would otherwise cause swapping and grind to a halt. DiskBBQ largely scales linearlly with the total RAM. And search performance is enhanced at scale as a subset of the total vector space is loaded.
356357

357358
`m`
358359
: (Optional, integer) The number of neighbors each node will be connected to in the HNSW graph. Defaults to `16`. Only applicable to `hnsw`, `int8_hnsw`, `int4_hnsw` and `bbq_hnsw` index types.
@@ -363,6 +364,12 @@ $$$dense-vector-index-options$$$
363364
`confidence_interval`
364365
: (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.
365366

367+
`default_visit_percentage` {applies_to}`stack: ga 9.2`
368+
: (Optional, integer) Only applicable to `bbq_disk`. Must be between 0 and 100. 0 will default to using `num_candidates` for calculating the percent visited. Increasing `default_visit_percentage` tends to improve the accuracy of the final results. Defaults to ~1% per shard for every 1 million vectors.
369+
370+
`cluster_size` {applies_to}`stack: ga 9.2`
371+
: (Optional, integer) Only applicable to `bbq_disk`. The number of vectors per cluster. Smaller cluster sizes increases accuracy at the cost of performance. Defaults to `384`. Must be a value between `64` and `65536`.
372+
366373
`rescore_vector` {applies_to}`stack: preview 9.0, ga 9.1`
367374
: (Optional, object) An optional section that configures automatic vector rescoring on knn queries for the given field. Only applicable to quantized index types.
368375
:::::{dropdown} Properties of rescore_vector

docs/reference/elasticsearch/rest-apis/retrievers/knn-retriever.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ A kNN retriever returns top documents from a [k-nearest neighbor search (kNN)](d
4141
The number of nearest neighbor candidates to consider per shard. Needs to be greater than `k`, or `size` if `k` is omitted, and cannot exceed 10,000. {{es}} collects `num_candidates` results from each shard, then merges them to find the top `k` results. Increasing `num_candidates` tends to improve the accuracy of the final `k` results. Defaults to `Math.min(1.5 * k, 10_000)`.
4242

4343

44+
`visit_percentage` {applies_to}`stack: ga 9.2`
45+
: (Optional, float)
46+
47+
The percentage of vectors to explore per shard while doing knn search with `bbq_disk`. Must be between 0 and 100. 0 will default to using `num_candidates` for calculating the percent visited. Increasing `visit_percentage` tends to improve the accuracy of the final results. If `visit_percentage` is set for `bbq_disk`, `num_candidates` is ignored. Defaults to ~1% per shard for every 1 million vectors.
48+
49+
4450
`filter`
4551
: (Optional, [query object or list of query objects](/reference/query-languages/querydsl.md))
4652

docs/reference/query-languages/query-dsl/query-dsl-knn-query.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
navigation_title: "Knn"
33
mapped_pages:
44
- https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-knn-query.html
5+
applies_to:
6+
stack: all
7+
serverless: all
58
---
69

710
# Knn query [query-dsl-knn-query]
@@ -87,6 +90,10 @@ If all queried fields are of type [semantic_text](/reference/elasticsearch/mappi
8790
: (Optional, integer) The number of nearest neighbor candidates to consider per shard while doing knn search. Cannot exceed 10,000. Increasing `num_candidates` tends to improve the accuracy of the final results. Defaults to `1.5 * k` if `k` is set, or `1.5 * size` if `k` is not set.
8891

8992

93+
`visit_percentage` {applies_to}`stack: ga 9.2`
94+
: (Optional, float) The percentage of vectors to explore per shard while doing knn search with `bbq_disk`. Must be between 0 and 100. 0 will default to using `num_candidates` for calculating the percent visited. Increasing `visit_percentage` tends to improve the accuracy of the final results. If `visit_percentage` is set for `bbq_disk`, `num_candidates` is ignored. Defaults to ~1% per shard for every 1 million vectors.
95+
96+
9097
`filter`
9198
: (Optional, query object) Query to filter the documents that can match. The kNN search will return the top documents that also match this filter. The value can be a single query or a list of queries. If `filter` is not provided, all documents are allowed to match.
9299

@@ -108,15 +115,15 @@ The filter is a pre-filter, meaning that it is applied **during** the approximat
108115
: (Optional, object) Apply oversampling and rescoring to quantized vectors.
109116

110117
**Parameters for `rescore_vector`**:
111-
118+
112119
`oversample`
113120
: (Required, float)
114121

115122
Applies the specified oversample factor to `k` on the approximate kNN search. The approximate kNN search will:
116123

117124
* Retrieve `num_candidates` candidates per shard.
118125
* From these candidates, the top `k * oversample` candidates per shard will be rescored using the original vectors.
119-
* The top `k` rescored candidates will be returned. Must be one of the following values:
126+
* The top `k` rescored candidates will be returned. Must be one of the following values:
120127
* \>= 1f to indicate the oversample factor
121128
* Exactly `0` to indicate that no oversampling and rescoring should occur. {applies_to}`stack: ga 9.1`
122129

modules/percolator/src/internalClusterTest/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import java.util.Collections;
4141
import java.util.Map;
4242

43-
import static org.elasticsearch.index.mapper.vectors.DenseVectorFieldMapper.IVF_FORMAT;
4443
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
4544
import static org.elasticsearch.index.query.QueryBuilders.combinedFieldsQuery;
4645
import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery;
@@ -1360,15 +1359,7 @@ public void testKnnQueryNotSupportedInPercolator() throws IOException {
13601359
""");
13611360
indicesAdmin().prepareCreate("index1").setMapping(mappings).get();
13621361
ensureGreen();
1363-
QueryBuilder knnVectorQueryBuilder = new KnnVectorQueryBuilder(
1364-
"my_vector",
1365-
new float[] { 1, 1, 1, 1, 1 },
1366-
10,
1367-
10,
1368-
IVF_FORMAT.isEnabled() ? 10f : null,
1369-
null,
1370-
null
1371-
);
1362+
QueryBuilder knnVectorQueryBuilder = new KnnVectorQueryBuilder("my_vector", new float[] { 1, 1, 1, 1, 1 }, 10, 10, 10f, null, null);
13721363

13731364
IndexRequestBuilder indexRequestBuilder = prepareIndex("index1").setId("knn_query1")
13741365
.setSource(jsonBuilder().startObject().field("my_query", knnVectorQueryBuilder).endObject());

qa/ccs-common-rest/src/yamlRestTest/java/org/elasticsearch/test/rest/yaml/CcsCommonYamlTestSuiteIT.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ public class CcsCommonYamlTestSuiteIT extends ESClientYamlSuiteTestCase {
9999
.setting("xpack.license.self_generated.type", "trial")
100100
.feature(FeatureFlag.TIME_SERIES_MODE)
101101
.feature(FeatureFlag.SUB_OBJECTS_AUTO_ENABLED)
102-
.feature(FeatureFlag.IVF_FORMAT)
103102
.feature(FeatureFlag.SYNTHETIC_VECTORS);
104103

105104
private static ElasticsearchCluster remoteCluster = ElasticsearchCluster.local()

qa/ccs-common-rest/src/yamlRestTest/java/org/elasticsearch/test/rest/yaml/RcsCcsCommonYamlTestSuiteIT.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ public class RcsCcsCommonYamlTestSuiteIT extends ESClientYamlSuiteTestCase {
9898
.setting("xpack.security.remote_cluster_client.ssl.enabled", "false")
9999
.feature(FeatureFlag.TIME_SERIES_MODE)
100100
.feature(FeatureFlag.SUB_OBJECTS_AUTO_ENABLED)
101-
.feature(FeatureFlag.IVF_FORMAT)
102101
.feature(FeatureFlag.SYNTHETIC_VECTORS)
103102
.user("test_admin", "x-pack-test-password");
104103

qa/smoke-test-multinode/src/yamlRestTest/java/org/elasticsearch/smoketest/SmokeTestMultiNodeClientYamlTestSuiteIT.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ public class SmokeTestMultiNodeClientYamlTestSuiteIT extends ESClientYamlSuiteTe
3737
.feature(FeatureFlag.TIME_SERIES_MODE)
3838
.feature(FeatureFlag.SUB_OBJECTS_AUTO_ENABLED)
3939
.feature(FeatureFlag.DOC_VALUES_SKIPPER)
40-
.feature(FeatureFlag.IVF_FORMAT)
4140
.feature(FeatureFlag.SYNTHETIC_VECTORS)
4241
.build();
4342

rest-api-spec/src/yamlRestTest/java/org/elasticsearch/test/rest/ClientYamlTestSuiteIT.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ public class ClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase {
3737
.feature(FeatureFlag.TIME_SERIES_MODE)
3838
.feature(FeatureFlag.SUB_OBJECTS_AUTO_ENABLED)
3939
.feature(FeatureFlag.DOC_VALUES_SKIPPER)
40-
.feature(FeatureFlag.IVF_FORMAT)
4140
.feature(FeatureFlag.SYNTHETIC_VECTORS)
4241
.build();
4342

server/src/internalClusterTest/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldIndexTypeUpdateIT.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import org.elasticsearch.xcontent.XContentFactory;
2525

2626
import java.io.IOException;
27-
import java.util.ArrayList;
2827
import java.util.Collection;
2928
import java.util.List;
3029
import java.util.Map;
@@ -51,12 +50,17 @@ public DenseVectorFieldIndexTypeUpdateIT(@Name("initialType") String initialType
5150

5251
@ParametersFactory
5352
public static Collection<Object[]> params() {
54-
List<String> types = new ArrayList<>(
55-
List.of("flat", "int8_flat", "int4_flat", "bbq_flat", "hnsw", "int8_hnsw", "int4_hnsw", "bbq_hnsw")
53+
List<String> types = List.of(
54+
"flat",
55+
"int8_flat",
56+
"int4_flat",
57+
"bbq_flat",
58+
"hnsw",
59+
"int8_hnsw",
60+
"int4_hnsw",
61+
"bbq_hnsw",
62+
"bbq_disk"
5663
);
57-
if (DenseVectorFieldMapper.IVF_FORMAT.isEnabled()) {
58-
types.add("bbq_disk");
59-
}
6064

6165
// A type can be upgraded to types that follow in the list...
6266
List<Object[]> params = new java.util.ArrayList<>();

0 commit comments

Comments
 (0)