diff --git a/server/src/main/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapper.java index b52bf9759be69..dc8179bab34cf 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapper.java @@ -2067,7 +2067,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field("type", type); builder.field("m", m); builder.field("ef_construction", efConstruction); - if (confidenceInterval != null) { + if (hasSerializableConfidenceInterval(confidenceInterval)) { builder.field("confidence_interval", confidenceInterval); } if (onDiskRescore) { @@ -2161,7 +2161,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (rescoreVector != null) { rescoreVector.toXContent(builder, params); } - if (confidenceInterval != null) { + if (hasSerializableConfidenceInterval(confidenceInterval)) { builder.field("confidence_interval", confidenceInterval); } builder.endObject(); @@ -3555,6 +3555,10 @@ private static Float parseConfidenceInterval(String fieldName, Map in return confidenceInterval; } + private static boolean hasSerializableConfidenceInterval(Float confidenceInterval) { + return confidenceInterval != null && Float.compare(confidenceInterval, 0.0f) != 0; + } + /** * @return the custom kNN vectors format that is configured for this field or * {@code null} if the default format should be used. diff --git a/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapperTests.java index a956441219ada..685fd546ca2a9 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapperTests.java @@ -85,6 +85,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.startsWith; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -2033,6 +2034,41 @@ public void testConfidenceIntervalNoDeprecationBeforeLatestIndexVersion() throws assertWarnings(); } + public void testConfidenceIntervalZeroNormalizedAndOmittedFromMappingSource() throws IOException { + DocumentMapper mapper = createDocumentMapper(IndexVersions.UPGRADE_TO_LUCENE_10_4_0, fieldMapping(b -> { + b.field("type", "dense_vector"); + b.field("dims", 6); + b.field("index", true); + b.field("similarity", "dot_product"); + b.startObject("index_options"); + b.field("type", "int4_hnsw"); + b.field("m", 16); + b.field("ef_construction", 100); + b.field("confidence_interval", 0.0); + b.endObject(); + })); + String source = mapper.mappingSource().string(); + assertThat(source, containsString("\"type\":\"int4_hnsw\"")); + assertThat(source, not(containsString("confidence_interval"))); + } + + public void testConfidenceIntervalAbsentInt4HnswMappingSourceStable() throws IOException { + DocumentMapper mapper = createDocumentMapper(IndexVersions.UPGRADE_TO_LUCENE_10_4_0, fieldMapping(b -> { + b.field("type", "dense_vector"); + b.field("dims", 6); + b.field("index", true); + b.field("similarity", "dot_product"); + b.startObject("index_options"); + b.field("type", "int4_hnsw"); + b.field("m", 16); + b.field("ef_construction", 100); + b.endObject(); + })); + String source = mapper.mappingSource().string(); + assertThat(source, containsString("\"type\":\"int4_hnsw\"")); + assertThat(source, not(containsString("confidence_interval"))); + } + public void testKnnQuantizedFlatVectorsFormat() throws IOException { for (String quantizedFlatFormat : new String[] { "int8_flat", "int4_flat" }) { MapperService mapperService = createMapperService(