Skip to content

Commit 024bf59

Browse files
[8.19] Default new semantic_text fields to use BBQ when models are compatible (elastic#126629) (elastic#127831)
* Default new semantic_text fields to use BBQ when models are compatible (elastic#126629) * Default new semantic_text fields to use BBQ when models are compatible * Update docs/changelog/126629.yaml * Gate default BBQ by IndexVersion * Cleanup from PR feedback * PR feedback * Fix test * Fix test * PR feedback * Update test to test correct options * Hack alert: Fix issue where mapper service was always being created with current index version (cherry picked from commit a72883e) # Conflicts: # server/src/main/java/org/elasticsearch/index/IndexVersions.java # server/src/main/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapper.java # x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/mapper/SemanticInferenceMetadataFieldsMapperTests.java # x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/mapper/SemanticTextFieldMapperTests.java * Spotless * Fix compilation errors after backport --------- Co-authored-by: Elastic Machine <[email protected]>
1 parent 6197328 commit 024bf59

File tree

7 files changed

+222
-27
lines changed

7 files changed

+222
-27
lines changed

docs/changelog/126629.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 126629
2+
summary: Default new `semantic_text` fields to use BBQ when models are compatible
3+
area: Relevance
4+
type: enhancement
5+
issues: []

server/src/main/java/org/elasticsearch/index/IndexVersions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ private static IndexVersion def(int id, Version luceneVersion) {
131131
public static final IndexVersion ADD_RESCORE_PARAMS_TO_QUANTIZED_VECTORS = def(8_528_0_00, Version.LUCENE_9_12_1);
132132
public static final IndexVersion RESCORE_PARAMS_ALLOW_ZERO_TO_QUANTIZED_VECTORS = def(8_529_0_00, Version.LUCENE_9_12_1);
133133
public static final IndexVersion DEFAULT_OVERSAMPLE_VALUE_FOR_BBQ_BACKPORT_8_X = def(8_530_0_00, Version.LUCENE_9_12_1);
134+
public static final IndexVersion SEMANTIC_TEXT_DEFAULTS_TO_BBQ_BACKPORT_8_X = def(8_531_0_00, Version.LUCENE_9_12_1);
134135
/*
135136
* STOP! READ THIS FIRST! No, really,
136137
* ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _

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

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,11 @@ public Builder elementType(ElementType elementType) {
304304
return this;
305305
}
306306

307+
public Builder indexOptions(IndexOptions indexOptions) {
308+
this.indexOptions.setValue(indexOptions);
309+
return this;
310+
}
311+
307312
@Override
308313
public DenseVectorFieldMapper build(MapperBuilderContext context) {
309314
// Validate again here because the dimensions or element type could have been set programmatically,
@@ -1202,7 +1207,7 @@ public final String toString() {
12021207
public abstract VectorSimilarityFunction vectorSimilarityFunction(IndexVersion indexVersion, ElementType elementType);
12031208
}
12041209

1205-
abstract static class IndexOptions implements ToXContent {
1210+
public abstract static class IndexOptions implements ToXContent {
12061211
final VectorIndexType type;
12071212

12081213
IndexOptions(VectorIndexType type) {
@@ -1211,21 +1216,36 @@ abstract static class IndexOptions implements ToXContent {
12111216

12121217
abstract KnnVectorsFormat getVectorsFormat(ElementType elementType);
12131218

1214-
final void validateElementType(ElementType elementType) {
1215-
if (type.supportsElementType(elementType) == false) {
1219+
public boolean validate(ElementType elementType, int dim, boolean throwOnError) {
1220+
return validateElementType(elementType, throwOnError) && validateDimension(dim, throwOnError);
1221+
}
1222+
1223+
public boolean validateElementType(ElementType elementType) {
1224+
return validateElementType(elementType, true);
1225+
}
1226+
1227+
final boolean validateElementType(ElementType elementType, boolean throwOnError) {
1228+
boolean validElementType = type.supportsElementType(elementType);
1229+
if (throwOnError && validElementType == false) {
12161230
throw new IllegalArgumentException(
12171231
"[element_type] cannot be [" + elementType.toString() + "] when using index type [" + type + "]"
12181232
);
12191233
}
1234+
return validElementType;
12201235
}
12211236

12221237
abstract boolean updatableTo(IndexOptions update);
12231238

1224-
public void validateDimension(int dim) {
1225-
if (type.supportsDimension(dim)) {
1226-
return;
1239+
public boolean validateDimension(int dim) {
1240+
return validateDimension(dim, true);
1241+
}
1242+
1243+
public boolean validateDimension(int dim, boolean throwOnError) {
1244+
boolean supportsDimension = type.supportsDimension(dim);
1245+
if (throwOnError && supportsDimension == false) {
1246+
throw new IllegalArgumentException(type.name + " only supports even dimensions; provided=" + dim);
12271247
}
1228-
throw new IllegalArgumentException(type.name + " only supports even dimensions; provided=" + dim);
1248+
return supportsDimension;
12291249
}
12301250

12311251
abstract boolean doEquals(IndexOptions other);
@@ -1734,12 +1754,12 @@ boolean updatableTo(IndexOptions update) {
17341754

17351755
}
17361756

1737-
static class Int8HnswIndexOptions extends QuantizedIndexOptions {
1757+
public static class Int8HnswIndexOptions extends QuantizedIndexOptions {
17381758
private final int m;
17391759
private final int efConstruction;
17401760
private final Float confidenceInterval;
17411761

1742-
Int8HnswIndexOptions(int m, int efConstruction, Float confidenceInterval, RescoreVector rescoreVector) {
1762+
public Int8HnswIndexOptions(int m, int efConstruction, Float confidenceInterval, RescoreVector rescoreVector) {
17431763
super(VectorIndexType.INT8_HNSW, rescoreVector);
17441764
this.m = m;
17451765
this.efConstruction = efConstruction;
@@ -1877,11 +1897,11 @@ public String toString() {
18771897
}
18781898
}
18791899

1880-
static class BBQHnswIndexOptions extends QuantizedIndexOptions {
1900+
public static class BBQHnswIndexOptions extends QuantizedIndexOptions {
18811901
private final int m;
18821902
private final int efConstruction;
18831903

1884-
BBQHnswIndexOptions(int m, int efConstruction, RescoreVector rescoreVector) {
1904+
public BBQHnswIndexOptions(int m, int efConstruction, RescoreVector rescoreVector) {
18851905
super(VectorIndexType.BBQ_HNSW, rescoreVector);
18861906
this.m = m;
18871907
this.efConstruction = efConstruction;
@@ -1923,11 +1943,14 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
19231943
}
19241944

19251945
@Override
1926-
public void validateDimension(int dim) {
1927-
if (type.supportsDimension(dim)) {
1928-
return;
1946+
public boolean validateDimension(int dim, boolean throwOnError) {
1947+
boolean supportsDimension = type.supportsDimension(dim);
1948+
if (throwOnError && supportsDimension == false) {
1949+
throw new IllegalArgumentException(
1950+
type.name + " does not support dimensions fewer than " + BBQ_MIN_DIMS + "; provided=" + dim
1951+
);
19291952
}
1930-
throw new IllegalArgumentException(type.name + " does not support dimensions fewer than " + BBQ_MIN_DIMS + "; provided=" + dim);
1953+
return supportsDimension;
19311954
}
19321955
}
19331956

@@ -1971,15 +1994,19 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
19711994
}
19721995

19731996
@Override
1974-
public void validateDimension(int dim) {
1975-
if (type.supportsDimension(dim)) {
1976-
return;
1997+
public boolean validateDimension(int dim, boolean throwOnError) {
1998+
boolean supportsDimension = type.supportsDimension(dim);
1999+
if (throwOnError && supportsDimension == false) {
2000+
throw new IllegalArgumentException(
2001+
type.name + " does not support dimensions fewer than " + BBQ_MIN_DIMS + "; provided=" + dim
2002+
);
19772003
}
1978-
throw new IllegalArgumentException(type.name + " does not support dimensions fewer than " + BBQ_MIN_DIMS + "; provided=" + dim);
2004+
return supportsDimension;
19792005
}
2006+
19802007
}
19812008

1982-
record RescoreVector(float oversample) implements ToXContentObject {
2009+
public record RescoreVector(float oversample) implements ToXContentObject {
19832010
static final String NAME = "rescore_vector";
19842011
static final String OVERSAMPLE = "oversample";
19852012

@@ -2299,7 +2326,7 @@ ElementType getElementType() {
22992326
return elementType;
23002327
}
23012328

2302-
IndexOptions getIndexOptions() {
2329+
public IndexOptions getIndexOptions() {
23032330
return indexOptions;
23042331
}
23052332
}

test/framework/src/main/java/org/elasticsearch/index/mapper/MapperServiceTestCase.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,13 @@ protected final MapperService createMapperService(Settings settings, String mapp
207207
return mapperService;
208208
}
209209

210+
protected final MapperService createMapperService(IndexVersion indexVersion, Settings settings, XContentBuilder mappings)
211+
throws IOException {
212+
MapperService mapperService = createMapperService(indexVersion, settings, () -> true, mappings);
213+
merge(mapperService, mappings);
214+
return mapperService;
215+
}
216+
210217
protected final MapperService createMapperService(IndexVersion version, XContentBuilder mapping) throws IOException {
211218
return createMapperService(version, getIndexSettings(), () -> true, mapping);
212219
}

x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/mapper/SemanticTextFieldMapper.java

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

1010
import org.apache.logging.log4j.LogManager;
1111
import org.apache.logging.log4j.Logger;
12+
import org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat;
1213
import org.apache.lucene.index.FieldInfos;
1314
import org.apache.lucene.index.LeafReaderContext;
1415
import org.apache.lucene.search.DocIdSetIterator;
@@ -96,6 +97,7 @@
9697
import java.util.function.Function;
9798
import java.util.function.Supplier;
9899

100+
import static org.elasticsearch.index.IndexVersions.SEMANTIC_TEXT_DEFAULTS_TO_BBQ_BACKPORT_8_X;
99101
import static org.elasticsearch.inference.TaskType.SPARSE_EMBEDDING;
100102
import static org.elasticsearch.inference.TaskType.TEXT_EMBEDDING;
101103
import static org.elasticsearch.search.SearchService.DEFAULT_SIZE;
@@ -136,6 +138,8 @@ public class SemanticTextFieldMapper extends FieldMapper implements InferenceFie
136138
public static final String CONTENT_TYPE = "semantic_text";
137139
public static final String DEFAULT_ELSER_2_INFERENCE_ID = DEFAULT_ELSER_ID;
138140

141+
public static final float DEFAULT_RESCORE_OVERSAMPLE = 3.0f;
142+
139143
public static final TypeParser parser(Supplier<ModelRegistry> modelRegistry) {
140144
return new TypeParser(
141145
(n, c) -> new Builder(n, c::bitSetProducer, c.getIndexSettings(), modelRegistry.get()),
@@ -1078,12 +1082,30 @@ private static Mapper.Builder createEmbeddingsField(
10781082
denseVectorMapperBuilder.dimensions(modelSettings.dimensions());
10791083
denseVectorMapperBuilder.elementType(modelSettings.elementType());
10801084

1085+
DenseVectorFieldMapper.IndexOptions defaultIndexOptions = null;
1086+
if (indexVersionCreated.onOrAfter(SEMANTIC_TEXT_DEFAULTS_TO_BBQ_BACKPORT_8_X)) {
1087+
defaultIndexOptions = defaultSemanticDenseIndexOptions();
1088+
}
1089+
if (defaultIndexOptions != null
1090+
&& defaultIndexOptions.validate(modelSettings.elementType(), modelSettings.dimensions(), false)) {
1091+
denseVectorMapperBuilder.indexOptions(defaultIndexOptions);
1092+
}
1093+
10811094
yield denseVectorMapperBuilder;
10821095
}
10831096
default -> throw new IllegalArgumentException("Invalid task_type in model_settings [" + modelSettings.taskType().name() + "]");
10841097
};
10851098
}
10861099

1100+
static DenseVectorFieldMapper.IndexOptions defaultSemanticDenseIndexOptions() {
1101+
// As embedding models for text perform better with BBQ, we aggressively default semantic_text fields to use optimized index
1102+
// options outside of dense_vector defaults
1103+
int m = Lucene99HnswVectorsFormat.DEFAULT_MAX_CONN;
1104+
int efConstruction = Lucene99HnswVectorsFormat.DEFAULT_BEAM_WIDTH;
1105+
DenseVectorFieldMapper.RescoreVector rescoreVector = new DenseVectorFieldMapper.RescoreVector(DEFAULT_RESCORE_OVERSAMPLE);
1106+
return new DenseVectorFieldMapper.BBQHnswIndexOptions(m, efConstruction, rescoreVector);
1107+
}
1108+
10871109
private static boolean canMergeModelSettings(MinimalServiceSettings previous, MinimalServiceSettings current, Conflicts conflicts) {
10881110
if (previous != null && current != null && previous.canMergeWith(current)) {
10891111
return true;

x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/mapper/SemanticInferenceMetadataFieldsMapperTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,34 @@
1010
import org.apache.lucene.index.FieldInfo;
1111
import org.apache.lucene.index.FieldInfos;
1212
import org.elasticsearch.common.settings.Settings;
13+
import org.elasticsearch.index.IndexVersion;
14+
import org.elasticsearch.index.IndexVersions;
1315
import org.elasticsearch.index.mapper.MappedFieldType;
1416
import org.elasticsearch.index.mapper.MapperServiceTestCase;
1517
import org.elasticsearch.plugins.Plugin;
18+
import org.elasticsearch.test.index.IndexVersionUtils;
1619
import org.elasticsearch.xpack.inference.InferencePlugin;
1720

1821
import java.util.Collection;
1922
import java.util.Collections;
2023

2124
public class SemanticInferenceMetadataFieldsMapperTests extends MapperServiceTestCase {
25+
26+
static IndexVersion getRandomCompatibleIndexVersion(boolean useLegacyFormat) {
27+
return getRandomCompatibleIndexVersion(useLegacyFormat, IndexVersion.current());
28+
}
29+
30+
static IndexVersion getRandomCompatibleIndexVersion(boolean useLegacyFormat, IndexVersion maxVersion) {
31+
if (useLegacyFormat) {
32+
if (randomBoolean()) {
33+
return IndexVersionUtils.randomVersionBetween(random(), IndexVersions.INFERENCE_METADATA_FIELDS_BACKPORT, maxVersion);
34+
}
35+
return IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.INFERENCE_METADATA_FIELDS_BACKPORT);
36+
} else {
37+
return IndexVersionUtils.randomVersionBetween(random(), IndexVersions.INFERENCE_METADATA_FIELDS_BACKPORT, maxVersion);
38+
}
39+
}
40+
2241
@Override
2342
protected Collection<? extends Plugin> getPlugins() {
2443
return Collections.singletonList(new InferencePlugin(Settings.EMPTY));

0 commit comments

Comments
 (0)