From 6887c731e4b5a26e58009f401ddc5fc225ecdfc9 Mon Sep 17 00:00:00 2001 From: Panagiotis Bailis Date: Thu, 26 Jun 2025 13:06:57 +0300 Subject: [PATCH] Updating exact long and scaled_float queries to make use of the docvalues impl only when available --- .../mapper/extras/ScaledFloatFieldMapper.java | 2 +- .../rest-api-spec/test/search/issue129995.yml | 121 ++++++++++++++++++ .../index/mapper/NumberFieldMapper.java | 32 +++-- .../index/mapper/NumberFieldTypeTests.java | 18 ++- 4 files changed, 154 insertions(+), 19 deletions(-) create mode 100644 rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search/issue129995.yml diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapper.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapper.java index a91ca66faa405..12fdbeca9ff68 100644 --- a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapper.java @@ -259,7 +259,7 @@ public boolean isSearchable() { public Query termQuery(Object value, SearchExecutionContext context) { failIfNotIndexedNorDocValuesFallback(context); long scaledValue = Math.round(scale(value)); - return NumberFieldMapper.NumberType.LONG.termQuery(name(), scaledValue, isIndexed()); + return NumberFieldMapper.NumberType.LONG.termQuery(name(), scaledValue, isIndexed(), hasDocValues()); } @Override diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search/issue129995.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search/issue129995.yml new file mode 100644 index 0000000000000..81d90b5afef33 --- /dev/null +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search/issue129995.yml @@ -0,0 +1,121 @@ +--- +setup: + - do: + indices.create: + index: test + body: + mappings: + properties: + id: + type: keyword + long_no_dv: + type: long + doc_values: false + long_dv: + type: long + doc_values: true + scaled_float_no_dv: + type: scaled_float + doc_values: false + scaling_factor: 1.0 + scaled_float_dv: + type: scaled_float + doc_values: true + scaling_factor: 1.0 + + - do: + index: + index: test + body: + id: "1" + long_no_dv: 1 + + - do: + index: + index: test + body: + id: "2" + long_dv: 2 + + + - do: + index: + index: test + body: + id: "3" + scaled_float_no_dv: 3.0 + + + - do: + index: + index: test + body: + id: "4" + scaled_float_dv: 4.0 + + + - do: + indices.refresh: + index: [test] + +--- +"Test exact queries on long fields with doc_values": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match: + long_dv: + query: 2 + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.id: "2" } + +--- +"Test exact queries on scaled_float fields with doc_values": + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match: + scaled_float_dv: + query: 4.0 + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.id: "4" } + +--- +"Test exact queries on long fields without doc_values": + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match: + long_no_dv: + query: 1 + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.id: "1" } + + +--- +"Test exact queries on sacled_float fields without doc_values": + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match: + scaled_float_no_dv: + query: 3.0 + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.id: "3" } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java index 97c6db437d4da..78e0817f6c6f3 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java @@ -325,7 +325,7 @@ public Float parse(XContentParser parser, boolean coerce) throws IOException { } @Override - public Query termQuery(String field, Object value, boolean isIndexed) { + public Query termQuery(String field, Object value, boolean isIndexed, boolean hasDocValues) { float v = parseToFloat(value); if (Float.isFinite(HalfFloatPoint.sortableShortToHalfFloat(HalfFloatPoint.halfFloatToSortableShort(v))) == false) { return Queries.newMatchNoDocsQuery("Value [" + value + "] is out of range"); @@ -510,7 +510,7 @@ public Float parse(XContentParser parser, boolean coerce) throws IOException { } @Override - public Query termQuery(String field, Object value, boolean isIndexed) { + public Query termQuery(String field, Object value, boolean isIndexed, boolean hasDocValues) { float v = parseToFloat(value); if (Float.isFinite(v) == false) { return new MatchNoDocsQuery("Value [" + value + "] is out of range"); @@ -678,7 +678,7 @@ public FieldValues compile(String fieldName, Script script, ScriptCompil } @Override - public Query termQuery(String field, Object value, boolean isIndexed) { + public Query termQuery(String field, Object value, boolean isIndexed, boolean hasDocValues) { double v = objectToDouble(value); if (Double.isFinite(v) == false) { return Queries.newMatchNoDocsQuery("Value [" + value + "] has a decimal part"); @@ -833,12 +833,12 @@ public Short parse(XContentParser parser, boolean coerce) throws IOException { } @Override - public Query termQuery(String field, Object value, boolean isIndexed) { + public Query termQuery(String field, Object value, boolean isIndexed, boolean hasDocValues) { if (isOutOfRange(value)) { return new MatchNoDocsQuery("Value [" + value + "] is out of range"); } - return INTEGER.termQuery(field, value, isIndexed); + return INTEGER.termQuery(field, value, isIndexed, hasDocValues); } @Override @@ -947,11 +947,11 @@ public Short parse(XContentParser parser, boolean coerce) throws IOException { } @Override - public Query termQuery(String field, Object value, boolean isIndexed) { + public Query termQuery(String field, Object value, boolean isIndexed, boolean hasDocValues) { if (isOutOfRange(value)) { return Queries.newMatchNoDocsQuery("Value [" + value + "] is out of range"); } - return INTEGER.termQuery(field, value, isIndexed); + return INTEGER.termQuery(field, value, isIndexed, hasDocValues); } @Override @@ -1063,7 +1063,7 @@ public Integer parse(XContentParser parser, boolean coerce) throws IOException { } @Override - public Query termQuery(String field, Object value, boolean isIndexed) { + public Query termQuery(String field, Object value, boolean isIndexed, boolean hasDocValues) { if (hasDecimalPart(value)) { return Queries.newMatchNoDocsQuery("Value [" + value + "] has a decimal part"); } @@ -1237,7 +1237,7 @@ public FieldValues compile(String fieldName, Script script, ScriptCompil } @Override - public Query termQuery(String field, Object value, boolean isIndexed) { + public Query termQuery(String field, Object value, boolean isIndexed, boolean hasDocValues) { if (hasDecimalPart(value)) { return Queries.newMatchNoDocsQuery("Value [" + value + "] has a decimal part"); } @@ -1246,10 +1246,14 @@ public Query termQuery(String field, Object value, boolean isIndexed) { } long v = parse(value, true); - if (isIndexed) { - return NumericField.newExactLongQuery(field, v); + if (hasDocValues) { + if (isIndexed) { + return NumericField.newExactLongQuery(field, v); + } else { + return SortedNumericDocValuesField.newSlowExactQuery(field, v); + } } else { - return SortedNumericDocValuesField.newSlowExactQuery(field, v); + return LongPoint.newExactQuery(field, v); } } @@ -1396,7 +1400,7 @@ public final TypeParser parser() { return parser; } - public abstract Query termQuery(String field, Object value, boolean isIndexed); + public abstract Query termQuery(String field, Object value, boolean isIndexed, boolean hasDocValues); public abstract Query termsQuery(String field, Collection values); @@ -1724,7 +1728,7 @@ public boolean isSearchable() { @Override public Query termQuery(Object value, SearchExecutionContext context) { failIfNotIndexedNorDocValuesFallback(context); - return type.termQuery(name(), value, isIndexed()); + return type.termQuery(name(), value, isIndexed(), hasDocValues()); } @Override diff --git a/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldTypeTests.java b/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldTypeTests.java index 978f2c1d2642a..24c66ecfbe807 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldTypeTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldTypeTests.java @@ -201,7 +201,7 @@ public void testTermQueryWithOutOfRangeValues() { ); for (OutOfRangeTermQueryTestCase testCase : testCases) { - assertTrue(testCase.type.termQuery("field", testCase.value, randomBoolean()) instanceof MatchNoDocsQuery); + assertTrue(testCase.type.termQuery("field", testCase.value, randomBoolean(), randomBoolean()) instanceof MatchNoDocsQuery); } } @@ -625,6 +625,7 @@ public void testHalfFloatRange() throws IOException { public void testNegativeZero() { final boolean isIndexed = randomBoolean(); + final boolean hasDocValues = randomBoolean(); assertEquals( NumberType.DOUBLE.rangeQuery("field", null, -0d, true, true, false, MOCK_CONTEXT, isIndexed), NumberType.DOUBLE.rangeQuery("field", null, +0d, true, false, false, MOCK_CONTEXT, isIndexed) @@ -638,9 +639,18 @@ public void testNegativeZero() { NumberType.HALF_FLOAT.rangeQuery("field", null, +0f, true, false, false, MOCK_CONTEXT, isIndexed) ); - assertNotEquals(NumberType.DOUBLE.termQuery("field", -0d, isIndexed), NumberType.DOUBLE.termQuery("field", +0d, isIndexed)); - assertNotEquals(NumberType.FLOAT.termQuery("field", -0f, isIndexed), NumberType.FLOAT.termQuery("field", +0f, isIndexed)); - assertNotEquals(NumberType.HALF_FLOAT.termQuery("field", -0f, isIndexed), NumberType.HALF_FLOAT.termQuery("field", +0f, isIndexed)); + assertNotEquals( + NumberType.DOUBLE.termQuery("field", -0d, isIndexed, hasDocValues), + NumberType.DOUBLE.termQuery("field", +0d, isIndexed, hasDocValues) + ); + assertNotEquals( + NumberType.FLOAT.termQuery("field", -0f, isIndexed, hasDocValues), + NumberType.FLOAT.termQuery("field", +0f, isIndexed, hasDocValues) + ); + assertNotEquals( + NumberType.HALF_FLOAT.termQuery("field", -0f, isIndexed, hasDocValues), + NumberType.HALF_FLOAT.termQuery("field", +0f, isIndexed, hasDocValues) + ); } // Make sure we construct the IndexOrDocValuesQuery objects with queries that match