Skip to content

Commit 2d3733f

Browse files
Merge branch 'main' into take-date-nanos-implicit-casting-out-of-snapshot
2 parents f3939c4 + b3a06fb commit 2d3733f

File tree

13 files changed

+245
-278
lines changed

13 files changed

+245
-278
lines changed

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

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -115,23 +115,23 @@ To retrieve vector values explicitly, you can use:
115115

116116
* The `fields` option to request specific vector fields directly:
117117

118-
```console
119-
POST my-index-2/_search
120-
{
121-
"fields": ["my_vector"]
122-
}
123-
```
118+
```console
119+
POST my-index-2/_search
120+
{
121+
"fields": ["my_vector"]
122+
}
123+
```
124124

125125
- The `_source.exclude_vectors` flag to re-enable vector inclusion in `_source` responses:
126126

127-
```console
128-
POST my-index-2/_search
129-
{
130-
"_source": {
131-
"exclude_vectors": false
127+
```console
128+
POST my-index-2/_search
129+
{
130+
"_source": {
131+
"exclude_vectors": false
132+
}
132133
}
133-
}
134-
```
134+
```
135135

136136
### Storage behavior and `_source`
137137

@@ -309,7 +309,7 @@ $$$dense-vector-similarity$$$
309309
`l2_norm`
310310
: Computes similarity based on the L2 distance (also known as Euclidean distance) between the vectors. The document `_score` is computed as `1 / (1 + l2_norm(query, vector)^2)`.
311311

312-
For `bit` vectors, instead of using `l2_norm`, the `hamming` distance between the vectors is used. The `_score` transformation is `(numBits - hamming(a, b)) / numBits`
312+
For `bit` vectors, instead of using `l2_norm`, the `hamming` distance between the vectors is used. The `_score` transformation is `(numBits - hamming(a, b)) / numBits`
313313

314314
`dot_product`
315315
: Computes the dot product of two unit vectors. This option provides an optimized way to perform cosine similarity. The constraints and computed score are defined by `element_type`.
@@ -341,15 +341,13 @@ $$$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:
345-
344+
* `int8_hnsw` - The default index type for some float vectors:
346345
* {applies_to}`stack: ga 9.1` Default for float vectors with less than 384 dimensions.
347346
* {applies_to}`stack: ga 9.0` Default for float all vectors.
348-
349347
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).
350348
* `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).
351349
* `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).
352-
350+
353351
{applies_to}`stack: ga 9.1` `bbq_hnsw` is the default index type for float vectors with greater than or equal to 384 dimensions.
354352
* `flat` - This utilizes a brute-force search algorithm for exact kNN search. This supports all `element_type` values.
355353
* `int8_flat` - This utilizes a brute-force search algorithm in addition to automatically scalar quantization. Only supports `element_type` of `float`.
@@ -365,7 +363,6 @@ $$$dense-vector-index-options$$$
365363
`confidence_interval`
366364
: (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.
367365

368-
369366
`rescore_vector` {applies_to}`stack: preview 9.0, ga 9.1`
370367
: (Optional, object) An optional section that configures automatic vector rescoring on knn queries for the given field. Only applicable to quantized index types.
371368
:::::{dropdown} Properties of rescore_vector
@@ -386,7 +383,7 @@ $$$dense-vector-index-options$$$
386383
`dense_vector` fields support [synthetic `_source`](/reference/elasticsearch/mapping-reference/mapping-source-field.md#synthetic-source) .
387384

388385

389-
## Indexing & Searching bit vectors [dense-vector-index-bit]
386+
## Indexing and searching bit vectors [dense-vector-index-bit]
390387

391388
When using `element_type: bit`, this will treat all vectors as bit vectors. Bit vectors utilize only a single bit per dimension and are internally encoded as bytes. This can be useful for very high-dimensional vectors or models.
392389

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.index.codec.vectors;
11+
12+
import org.apache.lucene.codecs.hnsw.FlatVectorsFormat;
13+
import org.apache.lucene.codecs.hnsw.FlatVectorsScorer;
14+
import org.elasticsearch.core.SuppressForbidden;
15+
16+
import static org.elasticsearch.index.mapper.vectors.DenseVectorFieldMapper.MAX_DIMS_COUNT;
17+
18+
public abstract class AbstractFlatVectorsFormat extends FlatVectorsFormat {
19+
20+
public static final boolean USE_DIRECT_IO = getUseDirectIO();
21+
22+
@SuppressForbidden(
23+
reason = "TODO Deprecate any lenient usage of Boolean#parseBoolean https://github.com/elastic/elasticsearch/issues/128993"
24+
)
25+
private static boolean getUseDirectIO() {
26+
return Boolean.parseBoolean(System.getProperty("vector.rescoring.directio", "false"));
27+
}
28+
29+
protected AbstractFlatVectorsFormat(String name) {
30+
super(name);
31+
}
32+
33+
protected abstract FlatVectorsScorer flatVectorsScorer();
34+
35+
@Override
36+
public int getMaxDimensions(String fieldName) {
37+
return MAX_DIMS_COUNT;
38+
}
39+
40+
@Override
41+
public String toString() {
42+
return getName() + "(name=" + getName() + ", flatVectorScorer=" + flatVectorsScorer() + ")";
43+
}
44+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.index.codec.vectors;
11+
12+
import org.apache.lucene.codecs.KnnVectorsFormat;
13+
import org.apache.lucene.codecs.hnsw.FlatVectorsFormat;
14+
import org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat;
15+
import org.apache.lucene.search.TaskExecutor;
16+
import org.apache.lucene.util.hnsw.HnswGraph;
17+
18+
import java.util.concurrent.ExecutorService;
19+
20+
import static org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat.DEFAULT_BEAM_WIDTH;
21+
import static org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat.DEFAULT_MAX_CONN;
22+
import static org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat.DEFAULT_NUM_MERGE_WORKER;
23+
import static org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat.MAXIMUM_BEAM_WIDTH;
24+
import static org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat.MAXIMUM_MAX_CONN;
25+
import static org.elasticsearch.index.mapper.vectors.DenseVectorFieldMapper.MAX_DIMS_COUNT;
26+
27+
public abstract class AbstractHnswVectorsFormat extends KnnVectorsFormat {
28+
29+
/**
30+
* Controls how many of the nearest neighbor candidates are connected to the new node. Defaults to
31+
* {@link Lucene99HnswVectorsFormat#DEFAULT_MAX_CONN}. See {@link HnswGraph} for more details.
32+
*/
33+
protected final int maxConn;
34+
35+
/**
36+
* The number of candidate neighbors to track while searching the graph for each newly inserted
37+
* node. Defaults to {@link Lucene99HnswVectorsFormat#DEFAULT_BEAM_WIDTH}. See {@link HnswGraph}
38+
* for details.
39+
*/
40+
protected final int beamWidth;
41+
42+
protected final int numMergeWorkers;
43+
protected final TaskExecutor mergeExec;
44+
45+
/** Constructs a format using default graph construction parameters */
46+
protected AbstractHnswVectorsFormat(String name) {
47+
this(name, DEFAULT_MAX_CONN, DEFAULT_BEAM_WIDTH, DEFAULT_NUM_MERGE_WORKER, null);
48+
}
49+
50+
/**
51+
* Constructs a format using the given graph construction parameters.
52+
*
53+
* @param maxConn the maximum number of connections to a node in the HNSW graph
54+
* @param beamWidth the size of the queue maintained during graph construction.
55+
*/
56+
protected AbstractHnswVectorsFormat(String name, int maxConn, int beamWidth) {
57+
this(name, maxConn, beamWidth, DEFAULT_NUM_MERGE_WORKER, null);
58+
}
59+
60+
/**
61+
* Constructs a format using the given graph construction parameters and scalar quantization.
62+
*
63+
* @param maxConn the maximum number of connections to a node in the HNSW graph
64+
* @param beamWidth the size of the queue maintained during graph construction.
65+
* @param numMergeWorkers number of workers (threads) that will be used when doing merge. If
66+
* larger than 1, a non-null {@link ExecutorService} must be passed as mergeExec
67+
* @param mergeExec the {@link ExecutorService} that will be used by ALL vector writers that are
68+
* generated by this format to do the merge
69+
*/
70+
protected AbstractHnswVectorsFormat(String name, int maxConn, int beamWidth, int numMergeWorkers, ExecutorService mergeExec) {
71+
super(name);
72+
if (maxConn <= 0 || maxConn > MAXIMUM_MAX_CONN) {
73+
throw new IllegalArgumentException(
74+
"maxConn must be positive and less than or equal to " + MAXIMUM_MAX_CONN + "; maxConn=" + maxConn
75+
);
76+
}
77+
if (beamWidth <= 0 || beamWidth > MAXIMUM_BEAM_WIDTH) {
78+
throw new IllegalArgumentException(
79+
"beamWidth must be positive and less than or equal to " + MAXIMUM_BEAM_WIDTH + "; beamWidth=" + beamWidth
80+
);
81+
}
82+
this.maxConn = maxConn;
83+
this.beamWidth = beamWidth;
84+
if (numMergeWorkers == 1 && mergeExec != null) {
85+
throw new IllegalArgumentException("No executor service is needed as we'll use single thread to merge");
86+
}
87+
this.numMergeWorkers = numMergeWorkers;
88+
if (mergeExec != null) {
89+
this.mergeExec = new TaskExecutor(mergeExec);
90+
} else {
91+
this.mergeExec = null;
92+
}
93+
}
94+
95+
protected abstract FlatVectorsFormat flatVectorsFormat();
96+
97+
@Override
98+
public int getMaxDimensions(String fieldName) {
99+
return MAX_DIMS_COUNT;
100+
}
101+
102+
@Override
103+
public String toString() {
104+
return getName()
105+
+ "(name="
106+
+ getName()
107+
+ ", maxConn="
108+
+ maxConn
109+
+ ", beamWidth="
110+
+ beamWidth
111+
+ ", flatVectorFormat="
112+
+ flatVectorsFormat()
113+
+ ")";
114+
}
115+
}

server/src/main/java/org/elasticsearch/index/codec/vectors/ES814HnswScalarQuantizedVectorsFormat.java

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
package org.elasticsearch.index.codec.vectors;
1111

12-
import org.apache.lucene.codecs.KnnVectorsFormat;
1312
import org.apache.lucene.codecs.KnnVectorsReader;
1413
import org.apache.lucene.codecs.KnnVectorsWriter;
1514
import org.apache.lucene.codecs.hnsw.FlatVectorsFormat;
@@ -22,19 +21,11 @@
2221

2322
import static org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat.DEFAULT_BEAM_WIDTH;
2423
import static org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat.DEFAULT_MAX_CONN;
25-
import static org.elasticsearch.index.mapper.vectors.DenseVectorFieldMapper.MAX_DIMS_COUNT;
2624

27-
public final class ES814HnswScalarQuantizedVectorsFormat extends KnnVectorsFormat {
25+
public final class ES814HnswScalarQuantizedVectorsFormat extends AbstractHnswVectorsFormat {
2826

2927
static final String NAME = "ES814HnswScalarQuantizedVectorsFormat";
3028

31-
static final int MAXIMUM_MAX_CONN = 512;
32-
static final int MAXIMUM_BEAM_WIDTH = 3200;
33-
34-
private final int maxConn;
35-
36-
private final int beamWidth;
37-
3829
/** The format for storing, reading, merging vectors on disk */
3930
private final FlatVectorsFormat flatVectorsFormat;
4031

@@ -43,45 +34,22 @@ public ES814HnswScalarQuantizedVectorsFormat() {
4334
}
4435

4536
public ES814HnswScalarQuantizedVectorsFormat(int maxConn, int beamWidth, Float confidenceInterval, int bits, boolean compress) {
46-
super(NAME);
47-
if (maxConn <= 0 || maxConn > MAXIMUM_MAX_CONN) {
48-
throw new IllegalArgumentException(
49-
"maxConn must be positive and less than or equal to " + MAXIMUM_MAX_CONN + "; maxConn=" + maxConn
50-
);
51-
}
52-
if (beamWidth <= 0 || beamWidth > MAXIMUM_BEAM_WIDTH) {
53-
throw new IllegalArgumentException(
54-
"beamWidth must be positive and less than or equal to " + MAXIMUM_BEAM_WIDTH + "; beamWidth=" + beamWidth
55-
);
56-
}
57-
this.maxConn = maxConn;
58-
this.beamWidth = beamWidth;
37+
super(NAME, maxConn, beamWidth);
5938
this.flatVectorsFormat = new ES814ScalarQuantizedVectorsFormat(confidenceInterval, bits, compress);
6039
}
6140

6241
@Override
63-
public KnnVectorsWriter fieldsWriter(SegmentWriteState state) throws IOException {
64-
return new Lucene99HnswVectorsWriter(state, maxConn, beamWidth, flatVectorsFormat.fieldsWriter(state), 1, null);
65-
}
66-
67-
@Override
68-
public KnnVectorsReader fieldsReader(SegmentReadState state) throws IOException {
69-
return new Lucene99HnswVectorsReader(state, flatVectorsFormat.fieldsReader(state));
42+
protected FlatVectorsFormat flatVectorsFormat() {
43+
return flatVectorsFormat;
7044
}
7145

7246
@Override
73-
public int getMaxDimensions(String fieldName) {
74-
return MAX_DIMS_COUNT;
47+
public KnnVectorsWriter fieldsWriter(SegmentWriteState state) throws IOException {
48+
return new Lucene99HnswVectorsWriter(state, maxConn, beamWidth, flatVectorsFormat.fieldsWriter(state), numMergeWorkers, mergeExec);
7549
}
7650

7751
@Override
78-
public String toString() {
79-
return "ES814HnswScalarQuantizedVectorsFormat(name=ES814HnswScalarQuantizedVectorsFormat, maxConn="
80-
+ maxConn
81-
+ ", beamWidth="
82-
+ beamWidth
83-
+ ", flatVectorFormat="
84-
+ flatVectorsFormat
85-
+ ")";
52+
public KnnVectorsReader fieldsReader(SegmentReadState state) throws IOException {
53+
return new Lucene99HnswVectorsReader(state, flatVectorsFormat.fieldsReader(state));
8654
}
8755
}

0 commit comments

Comments
 (0)