diff --git a/docs/changelog/128293.yaml b/docs/changelog/128293.yaml new file mode 100644 index 0000000000000..92181f0b46704 --- /dev/null +++ b/docs/changelog/128293.yaml @@ -0,0 +1,5 @@ +pr: 128293 +summary: Use `IndexOrDocValuesQuery` in `NumberFieldType#termQuery` implementations +area: Search +type: enhancement +issues: [] 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 f2b55f1460be5..e4733eb4bce17 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 @@ -323,7 +323,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/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldTypeTests.java b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldTypeTests.java index f2c6f81c3f742..f753d4b91f501 100644 --- a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldTypeTests.java +++ b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldTypeTests.java @@ -11,6 +11,7 @@ import org.apache.lucene.document.Document; import org.apache.lucene.document.DoublePoint; +import org.apache.lucene.document.LongField; import org.apache.lucene.document.LongPoint; import org.apache.lucene.document.SortedNumericDocValuesField; import org.apache.lucene.index.DirectoryReader; @@ -47,7 +48,7 @@ public void testTermQuery() { ); double value = (randomDouble() * 2 - 1) * 10000; long scaledValue = Math.round(value * ft.getScalingFactor()); - assertEquals(LongPoint.newExactQuery("scaled_float", scaledValue), ft.termQuery(value, MOCK_CONTEXT)); + assertEquals(LongField.newExactQuery("scaled_float", scaledValue), ft.termQuery(value, MOCK_CONTEXT)); MappedFieldType ft2 = new ScaledFloatFieldMapper.ScaledFloatFieldType("scaled_float", 0.1 + randomDouble() * 100, false); ElasticsearchException e2 = expectThrows(ElasticsearchException.class, () -> ft2.termQuery("42", MOCK_CONTEXT_DISALLOW_EXPENSIVE)); 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 7ed576805e41a..ea3b8d0ba9dc0 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java @@ -352,13 +352,19 @@ 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"); } if (isIndexed) { + if (hasDocValues) { + return new IndexOrDocValuesQuery( + HalfFloatPoint.newExactQuery(field, v), + SortedNumericDocValuesField.newSlowExactQuery(field, HalfFloatPoint.halfFloatToSortableShort(v)) + ); + } return HalfFloatPoint.newExactQuery(field, v); } else { return SortedNumericDocValuesField.newSlowExactQuery(field, HalfFloatPoint.halfFloatToSortableShort(v)); @@ -542,13 +548,15 @@ 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"); } - if (isIndexed) { + if (isIndexed && hasDocValues) { + return FloatField.newExactQuery(field, v); + } else if (isIndexed) { return FloatPoint.newExactQuery(field, v); } else { return SortedNumericDocValuesField.newSlowExactQuery(field, NumericUtils.floatToSortableInt(v)); @@ -715,13 +723,15 @@ 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"); } - if (isIndexed) { + if (isIndexed && hasDocValues) { + return DoubleField.newExactQuery(field, v); + } else if (isIndexed) { return DoublePoint.newExactQuery(field, v); } else { return SortedNumericDocValuesField.newSlowExactQuery(field, NumericUtils.doubleToSortableLong(v)); @@ -875,12 +885,12 @@ public Byte 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 @@ -999,11 +1009,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 @@ -1125,7 +1135,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"); } @@ -1136,7 +1146,9 @@ public Query termQuery(String field, Object value, boolean isIndexed) { } int v = parse(value, true); - if (isIndexed) { + if (isIndexed && hasDocValues) { + return IntField.newExactQuery(field, v); + } else if (isIndexed) { return IntPoint.newExactQuery(field, v); } else { return SortedNumericDocValuesField.newSlowExactQuery(field, v); @@ -1309,7 +1321,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"); } @@ -1318,7 +1330,9 @@ public Query termQuery(String field, Object value, boolean isIndexed) { } long v = parse(value, true); - if (isIndexed) { + if (isIndexed && hasDocValues) { + return LongField.newExactQuery(field, v); + } else if (isIndexed) { return LongPoint.newExactQuery(field, v); } else { return SortedNumericDocValuesField.newSlowExactQuery(field, v); @@ -1502,7 +1516,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); @@ -1893,11 +1907,11 @@ public NumberFieldType( } public NumberFieldType(String name, NumberType type) { - this(name, type, true); + this(name, type, true, true); } - public NumberFieldType(String name, NumberType type, boolean isIndexed) { - this(name, type, isIndexed, false, true, true, null, Collections.emptyMap(), null, false, null, null, false); + public NumberFieldType(String name, NumberType type, boolean isIndexed, boolean hasDocValues) { + this(name, type, isIndexed, false, hasDocValues, true, null, Collections.emptyMap(), null, false, null, null, false); } @Override @@ -1936,7 +1950,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 d4420f4baa0f4..52bebdf716611 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldTypeTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldTypeTests.java @@ -12,9 +12,13 @@ import com.carrotsearch.randomizedtesting.generators.RandomPicks; import org.apache.lucene.document.Document; +import org.apache.lucene.document.DoubleField; import org.apache.lucene.document.DoublePoint; +import org.apache.lucene.document.FloatField; import org.apache.lucene.document.FloatPoint; +import org.apache.lucene.document.IntField; import org.apache.lucene.document.IntPoint; +import org.apache.lucene.document.LongField; import org.apache.lucene.document.LongPoint; import org.apache.lucene.document.SortedNumericDocValuesField; import org.apache.lucene.index.DirectoryReader; @@ -57,7 +61,6 @@ import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.function.Supplier; @@ -108,60 +111,117 @@ public void testLongTermsQueryWithDecimalPart() { } public void testByteTermQueryWithDecimalPart() { - MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberType.BYTE, randomBoolean()); + MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberType.BYTE, true, true); + assertTrue(ft.termQuery(42.1, MOCK_CONTEXT) instanceof MatchNoDocsQuery); + + ft = new NumberFieldMapper.NumberFieldType("field", NumberType.BYTE, true, false); + assertTrue(ft.termQuery(42.1, MOCK_CONTEXT) instanceof MatchNoDocsQuery); + + ft = new NumberFieldMapper.NumberFieldType("field", NumberType.BYTE, false, true); assertTrue(ft.termQuery(42.1, MOCK_CONTEXT) instanceof MatchNoDocsQuery); } public void testShortTermQueryWithDecimalPart() { - MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberType.SHORT, randomBoolean()); + MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberType.SHORT, true, true); + assertTrue(ft.termQuery(42.1, MOCK_CONTEXT) instanceof MatchNoDocsQuery); + + ft = new NumberFieldMapper.NumberFieldType("field", NumberType.SHORT, false, true); + assertTrue(ft.termQuery(42.1, MOCK_CONTEXT) instanceof MatchNoDocsQuery); + + ft = new NumberFieldMapper.NumberFieldType("field", NumberType.SHORT, true, false); assertTrue(ft.termQuery(42.1, MOCK_CONTEXT) instanceof MatchNoDocsQuery); } public void testIntegerTermQueryWithDecimalPart() { - MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberType.INTEGER, randomBoolean()); + MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberType.INTEGER, true, true); assertTrue(ft.termQuery(42.1, MOCK_CONTEXT) instanceof MatchNoDocsQuery); - } - public void testLongTermQueryWithDecimalPart() { - MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.LONG, randomBoolean()); + ft = new NumberFieldMapper.NumberFieldType("field", NumberType.INTEGER, true, false); assertTrue(ft.termQuery(42.1, MOCK_CONTEXT) instanceof MatchNoDocsQuery); - } - private static MappedFieldType unsearchable() { - return new NumberFieldType( - "field", - NumberType.LONG, - false, - false, - false, - true, - null, - Collections.emptyMap(), - null, - false, - null, - null, - false - ); + ft = new NumberFieldMapper.NumberFieldType("field", NumberType.INTEGER, false, true); + assertTrue(ft.termQuery(42.1, MOCK_CONTEXT) instanceof MatchNoDocsQuery); } - public void testTermQuery() { - MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.LONG); - assertEquals(LongPoint.newExactQuery("field", 42), ft.termQuery("42", MOCK_CONTEXT)); + public void testLongTermQueryWithDecimalPart() { + MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.LONG, true, true); + assertTrue(ft.termQuery(42.1, MOCK_CONTEXT) instanceof MatchNoDocsQuery); - ft = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.LONG, false); - assertEquals(SortedNumericDocValuesField.newSlowExactQuery("field", 42), ft.termQuery("42", MOCK_CONTEXT)); + ft = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.LONG, true, false); + assertTrue(ft.termQuery(42.1, MOCK_CONTEXT) instanceof MatchNoDocsQuery); - MappedFieldType unsearchable = unsearchable(); - IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> unsearchable.termQuery("42", MOCK_CONTEXT)); - assertEquals("Cannot search on field [field] since it is not indexed nor has doc values.", e.getMessage()); + ft = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.LONG, false, true); + assertTrue(ft.termQuery(42.1, MOCK_CONTEXT) instanceof MatchNoDocsQuery); + } - MappedFieldType ft2 = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.LONG, false); - ElasticsearchException e2 = expectThrows(ElasticsearchException.class, () -> ft2.termQuery("42", MOCK_CONTEXT_DISALLOW_EXPENSIVE)); - assertEquals( - "Cannot search on field [field] since it is not indexed and 'search.allow_expensive_queries' is set to false.", - e2.getMessage() - ); + private record TermQueryTestCase(NumberType type, Query[] expectedQueries) {} + + public void testTermQuery() { + Query[] expectedIntegerQueries = new Query[] { + IntField.newExactQuery("field", 42), + IntPoint.newExactQuery("field", 42), + SortedNumericDocValuesField.newSlowExactQuery("field", 42) }; + List testCases = List.of( + new TermQueryTestCase(NumberType.BYTE, expectedIntegerQueries), + new TermQueryTestCase(NumberType.SHORT, expectedIntegerQueries), + new TermQueryTestCase(NumberType.INTEGER, expectedIntegerQueries), + new TermQueryTestCase( + NumberType.LONG, + new Query[] { + LongField.newExactQuery("field", 42), + LongPoint.newExactQuery("field", 42), + SortedNumericDocValuesField.newSlowExactQuery("field", 42) } + ), + new TermQueryTestCase( + NumberType.FLOAT, + new Query[] { + FloatField.newExactQuery("field", 42), + FloatPoint.newExactQuery("field", 42), + SortedNumericDocValuesField.newSlowExactQuery("field", NumericUtils.floatToSortableInt(42)) } + ), + new TermQueryTestCase( + NumberType.DOUBLE, + new Query[] { + DoubleField.newExactQuery("field", 42), + DoublePoint.newExactQuery("field", 42), + SortedNumericDocValuesField.newSlowExactQuery("field", NumericUtils.doubleToSortableLong(42)) } + ), + new TermQueryTestCase( + NumberType.HALF_FLOAT, + new Query[] { + new IndexOrDocValuesQuery( + HalfFloatPoint.newExactQuery("field", 42), + SortedNumericDocValuesField.newSlowExactQuery("field", HalfFloatPoint.halfFloatToSortableShort(42)) + ), + HalfFloatPoint.newExactQuery("field", 42), + SortedNumericDocValuesField.newSlowExactQuery("field", HalfFloatPoint.halfFloatToSortableShort(42)) } + ) + ); + + for (TermQueryTestCase testCase : testCases) { + MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", testCase.type()); + assertEquals(testCase.expectedQueries[0], ft.termQuery("42", MOCK_CONTEXT)); + + ft = new NumberFieldMapper.NumberFieldType("field", testCase.type(), true, false); + assertEquals(testCase.expectedQueries[1], ft.termQuery("42", MOCK_CONTEXT)); + + ft = new NumberFieldMapper.NumberFieldType("field", testCase.type(), false, true); + assertEquals(testCase.expectedQueries[2], ft.termQuery("42", MOCK_CONTEXT)); + + MappedFieldType unsearchable = new NumberFieldMapper.NumberFieldType("field", testCase.type(), false, false); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> unsearchable.termQuery("42", MOCK_CONTEXT)); + assertEquals("Cannot search on field [field] since it is not indexed nor has doc values.", e.getMessage()); + + MappedFieldType ft2 = new NumberFieldMapper.NumberFieldType("field", testCase.type(), false, true); + ElasticsearchException e2 = expectThrows( + ElasticsearchException.class, + () -> ft2.termQuery("42", MOCK_CONTEXT_DISALLOW_EXPENSIVE) + ); + assertEquals( + "Cannot search on field [field] since it is not indexed and 'search.allow_expensive_queries' is set to false.", + e2.getMessage() + ); + } } private record OutOfRangeTermQueryTestCase(NumberType type, Object value) {} @@ -201,12 +261,23 @@ public void testTermQueryWithOutOfRangeValues() { ); for (OutOfRangeTermQueryTestCase testCase : testCases) { - assertTrue(testCase.type.termQuery("field", testCase.value, randomBoolean()) instanceof MatchNoDocsQuery); + boolean indexed = randomBoolean(); + boolean hasDocValues = indexed == false || randomBoolean(); + assertTrue(testCase.type.termQuery("field", testCase.value, indexed, hasDocValues) instanceof MatchNoDocsQuery); } } public void testRangeQueryWithNegativeBounds() { - MappedFieldType ftInt = new NumberFieldMapper.NumberFieldType("field", NumberType.INTEGER, randomBoolean()); + testIntegerRangeQueryWithNegativeBounds(new NumberFieldMapper.NumberFieldType("field", NumberType.INTEGER, true, true)); + testIntegerRangeQueryWithNegativeBounds(new NumberFieldMapper.NumberFieldType("field", NumberType.INTEGER, false, true)); + testIntegerRangeQueryWithNegativeBounds(new NumberFieldMapper.NumberFieldType("field", NumberType.INTEGER, true, false)); + + testLongRangeQueryWithNegativeBounds(new NumberFieldMapper.NumberFieldType("field", NumberType.LONG, true, true)); + testLongRangeQueryWithNegativeBounds(new NumberFieldMapper.NumberFieldType("field", NumberType.LONG, false, true)); + testLongRangeQueryWithNegativeBounds(new NumberFieldMapper.NumberFieldType("field", NumberType.LONG, true, false)); + } + + private void testIntegerRangeQueryWithNegativeBounds(MappedFieldType ftInt) { assertEquals( ftInt.rangeQuery(-3, -3, true, true, null, null, null, MOCK_CONTEXT), ftInt.rangeQuery(-3.5, -2.5, true, true, null, null, null, MOCK_CONTEXT) @@ -256,8 +327,9 @@ public void testRangeQueryWithNegativeBounds() { ftInt.rangeQuery(-2, -1, true, true, null, null, null, MOCK_CONTEXT), ftInt.rangeQuery(-2.5, -0.5, false, false, null, null, null, MOCK_CONTEXT) ); + } - MappedFieldType ftLong = new NumberFieldMapper.NumberFieldType("field", NumberType.LONG, randomBoolean()); + private void testLongRangeQueryWithNegativeBounds(MappedFieldType ftLong) { assertEquals( ftLong.rangeQuery(-3, -3, true, true, null, null, null, MOCK_CONTEXT), ftLong.rangeQuery(-3.5, -2.5, true, true, null, null, null, MOCK_CONTEXT) @@ -310,7 +382,12 @@ public void testRangeQueryWithNegativeBounds() { } public void testByteRangeQueryWithDecimalParts() { - MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberType.BYTE, randomBoolean()); + testByteRangeQueryWithDecimalParts(new NumberFieldMapper.NumberFieldType("field", NumberType.BYTE, true, true)); + testByteRangeQueryWithDecimalParts(new NumberFieldMapper.NumberFieldType("field", NumberType.BYTE, false, true)); + testByteRangeQueryWithDecimalParts(new NumberFieldMapper.NumberFieldType("field", NumberType.BYTE, true, false)); + } + + private void testByteRangeQueryWithDecimalParts(MappedFieldType ft) { assertEquals( ft.rangeQuery(2, 10, true, true, null, null, null, MOCK_CONTEXT), ft.rangeQuery(1.1, 10, true, true, null, null, null, MOCK_CONTEXT) @@ -330,7 +407,12 @@ public void testByteRangeQueryWithDecimalParts() { } public void testShortRangeQueryWithDecimalParts() { - MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberType.SHORT, randomBoolean()); + testShortRangeQueryWithDecimalParts(new NumberFieldMapper.NumberFieldType("field", NumberType.SHORT, true, true)); + testShortRangeQueryWithDecimalParts(new NumberFieldMapper.NumberFieldType("field", NumberType.SHORT, true, false)); + testShortRangeQueryWithDecimalParts(new NumberFieldMapper.NumberFieldType("field", NumberType.SHORT, false, true)); + } + + private void testShortRangeQueryWithDecimalParts(MappedFieldType ft) { assertEquals( ft.rangeQuery(2, 10, true, true, null, null, null, MOCK_CONTEXT), ft.rangeQuery(1.1, 10, true, true, null, null, null, MOCK_CONTEXT) @@ -350,7 +432,12 @@ public void testShortRangeQueryWithDecimalParts() { } public void testIntegerRangeQueryWithDecimalParts() { - MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberType.INTEGER, randomBoolean()); + testIntegerRangeQueryWithDecimalParts(new NumberFieldMapper.NumberFieldType("field", NumberType.INTEGER, true, true)); + testIntegerRangeQueryWithDecimalParts(new NumberFieldMapper.NumberFieldType("field", NumberType.INTEGER, false, true)); + testIntegerRangeQueryWithDecimalParts(new NumberFieldMapper.NumberFieldType("field", NumberType.INTEGER, true, false)); + } + + private void testIntegerRangeQueryWithDecimalParts(MappedFieldType ft) { assertEquals( ft.rangeQuery(2, 10, true, true, null, null, null, MOCK_CONTEXT), ft.rangeQuery(1.1, 10, true, true, null, null, null, MOCK_CONTEXT) @@ -370,7 +457,12 @@ public void testIntegerRangeQueryWithDecimalParts() { } public void testLongRangeQueryWithDecimalParts() { - MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberType.LONG, randomBoolean()); + testLongRangeQueryWithDecimalParts(new NumberFieldMapper.NumberFieldType("field", NumberType.LONG, true, true)); + testLongRangeQueryWithDecimalParts(new NumberFieldMapper.NumberFieldType("field", NumberType.LONG, false, true)); + testLongRangeQueryWithDecimalParts(new NumberFieldMapper.NumberFieldType("field", NumberType.LONG, true, false)); + } + + private void testLongRangeQueryWithDecimalParts(MappedFieldType ft) { assertEquals( ft.rangeQuery(2, 10, true, true, null, null, null, MOCK_CONTEXT), ft.rangeQuery(1.1, 10, true, true, null, null, null, MOCK_CONTEXT) @@ -390,7 +482,12 @@ public void testLongRangeQueryWithDecimalParts() { } public void testHalfFloatRangeQueryWithOverflowingBounds() { - MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberType.HALF_FLOAT, randomBoolean()); + testHalfFloatRangeQueryWithOverflowingBounds(new NumberFieldMapper.NumberFieldType("field", NumberType.HALF_FLOAT, true, true)); + testHalfFloatRangeQueryWithOverflowingBounds(new NumberFieldMapper.NumberFieldType("field", NumberType.HALF_FLOAT, false, true)); + testHalfFloatRangeQueryWithOverflowingBounds(new NumberFieldMapper.NumberFieldType("field", NumberType.HALF_FLOAT, true, false)); + } + + private void testHalfFloatRangeQueryWithOverflowingBounds(MappedFieldType ft) { final float min_half_float = -65504; final float max_half_float = 65504; assertEquals( @@ -435,8 +532,12 @@ public void testHalfFloatRangeQueryWithOverflowingBounds() { } public void testFloatRangeQueryWithOverflowingBounds() { - MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberType.FLOAT, randomBoolean()); + testFloatRangeQueryWithOverflowingBounds(new NumberFieldMapper.NumberFieldType("field", NumberType.FLOAT, true, true)); + testFloatRangeQueryWithOverflowingBounds(new NumberFieldMapper.NumberFieldType("field", NumberType.FLOAT, false, true)); + testFloatRangeQueryWithOverflowingBounds(new NumberFieldMapper.NumberFieldType("field", NumberType.FLOAT, true, false)); + } + private void testFloatRangeQueryWithOverflowingBounds(MappedFieldType ft) { assertEquals( ft.rangeQuery(-Float.MAX_VALUE, 10.0, true, true, null, null, null, MOCK_CONTEXT), ft.rangeQuery(-1e+300, 10.0, true, true, null, null, null, MOCK_CONTEXT) @@ -486,18 +587,18 @@ public void testRangeQuery() { ); assertEquals(expected, ft.rangeQuery("1", "3", true, true, null, null, null, MOCK_CONTEXT)); - ft = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.LONG, false); + ft = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.LONG, false, true); expected = SortedNumericDocValuesField.newSlowRangeQuery("field", 1, 3); assertEquals(expected, ft.rangeQuery("1", "3", true, true, null, null, null, MOCK_CONTEXT)); - MappedFieldType unsearchable = unsearchable(); + MappedFieldType unsearchable = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.LONG, false, false); IllegalArgumentException e = expectThrows( IllegalArgumentException.class, () -> unsearchable.rangeQuery("1", "3", true, true, null, null, null, MOCK_CONTEXT) ); assertEquals("Cannot search on field [field] since it is not indexed nor has doc values.", e.getMessage()); - MappedFieldType ft2 = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.LONG, false); + MappedFieldType ft2 = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.LONG, false, true); ElasticsearchException e2 = expectThrows( ElasticsearchException.class, () -> ft2.rangeQuery("1", "3", true, true, null, null, null, MOCK_CONTEXT_DISALLOW_EXPENSIVE) @@ -638,9 +739,19 @@ 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)); + final boolean hasDocValues = isIndexed == false || randomBoolean(); // at least one should be true + 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 diff --git a/server/src/test/java/org/elasticsearch/index/query/MatchPhraseQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/MatchPhraseQueryBuilderTests.java index 4d76f7c522417..3532751359cfe 100644 --- a/server/src/test/java/org/elasticsearch/index/query/MatchPhraseQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/MatchPhraseQueryBuilderTests.java @@ -11,6 +11,7 @@ import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.IndexOrDocValuesQuery; +import org.apache.lucene.search.IndexSortSortedNumericDocValuesRangeQuery; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.PhraseQuery; @@ -106,6 +107,7 @@ protected void doAssertLuceneQuery(MatchPhraseQueryBuilder queryBuilder, Query q .or(instanceOf(PointRangeQuery.class)) .or(instanceOf(IndexOrDocValuesQuery.class)) .or(instanceOf(MatchNoDocsQuery.class)) + .or(instanceOf(IndexSortSortedNumericDocValuesRangeQuery.class)) ); } diff --git a/server/src/test/java/org/elasticsearch/index/query/TermQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/TermQueryBuilderTests.java index bbac216754eed..9d5db1b3c32f8 100644 --- a/server/src/test/java/org/elasticsearch/index/query/TermQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/TermQueryBuilderTests.java @@ -12,6 +12,8 @@ import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.search.AutomatonQuery; +import org.apache.lucene.search.IndexOrDocValuesQuery; +import org.apache.lucene.search.IndexSortSortedNumericDocValuesRangeQuery; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.PointRangeQuery; import org.apache.lucene.search.Query; @@ -93,6 +95,8 @@ protected void doAssertLuceneQuery(TermQueryBuilder queryBuilder, Query query, S either(instanceOf(TermQuery.class)).or(instanceOf(PointRangeQuery.class)) .or(instanceOf(MatchNoDocsQuery.class)) .or(instanceOf(AutomatonQuery.class)) + .or(instanceOf(IndexOrDocValuesQuery.class)) + .or(instanceOf(IndexSortSortedNumericDocValuesRangeQuery.class)) ); MappedFieldType mapper = context.getFieldType(queryBuilder.fieldName()); if (query instanceof TermQuery termQuery) { diff --git a/x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/ttest/TTestAggregatorTests.java b/x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/ttest/TTestAggregatorTests.java index dcee4d4b05c21..47ad5d8346b91 100644 --- a/x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/ttest/TTestAggregatorTests.java +++ b/x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/ttest/TTestAggregatorTests.java @@ -582,8 +582,8 @@ public void testHeteroscedastic() throws IOException { public void testFiltered() throws IOException { TTestType tTestType = randomFrom(TTestType.values()); - MappedFieldType fieldType1 = new NumberFieldMapper.NumberFieldType("a", NumberFieldMapper.NumberType.INTEGER); - MappedFieldType fieldType2 = new NumberFieldMapper.NumberFieldType("b", NumberFieldMapper.NumberType.INTEGER); + MappedFieldType fieldType1 = new NumberFieldMapper.NumberFieldType("a", NumberFieldMapper.NumberType.INTEGER, false, true); + MappedFieldType fieldType2 = new NumberFieldMapper.NumberFieldType("b", NumberFieldMapper.NumberType.INTEGER, true, false); TTestAggregationBuilder aggregationBuilder = new TTestAggregationBuilder("t_test").a( new MultiValuesSourceFieldConfig.Builder().setFieldName("a").setFilter(QueryBuilders.termQuery("b", 1)).build() ) @@ -638,9 +638,9 @@ public void testFiltered() throws IOException { public void testFilteredAsSubAgg() throws IOException { TTestType tTestType = randomFrom(TTestType.values()); - MappedFieldType fieldType1 = new NumberFieldMapper.NumberFieldType("h", NumberFieldMapper.NumberType.INTEGER); - MappedFieldType fieldType2 = new NumberFieldMapper.NumberFieldType("a", NumberFieldMapper.NumberType.INTEGER); - MappedFieldType fieldType3 = new NumberFieldMapper.NumberFieldType("b", NumberFieldMapper.NumberType.INTEGER); + MappedFieldType fieldType1 = new NumberFieldMapper.NumberFieldType("h", NumberFieldMapper.NumberType.INTEGER, false, true); + MappedFieldType fieldType2 = new NumberFieldMapper.NumberFieldType("a", NumberFieldMapper.NumberType.INTEGER, false, true); + MappedFieldType fieldType3 = new NumberFieldMapper.NumberFieldType("b", NumberFieldMapper.NumberType.INTEGER, true, false); TTestAggregationBuilder ttestAggregationBuilder = new TTestAggregationBuilder("t_test").a( new MultiValuesSourceFieldConfig.Builder().setFieldName("a").setFilter(QueryBuilders.termQuery("b", 1)).build() ) @@ -711,7 +711,7 @@ public void testFilterByFilterOrScript() throws IOException { boolean fieldInA = randomBoolean(); TTestType tTestType = randomFrom(TTestType.HOMOSCEDASTIC, TTestType.HETEROSCEDASTIC); - MappedFieldType fieldType1 = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.INTEGER); + MappedFieldType fieldType1 = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.INTEGER, false, true); MappedFieldType fieldType2 = new NumberFieldMapper.NumberFieldType("term", NumberFieldMapper.NumberType.INTEGER); boolean filterTermOne = randomBoolean(); diff --git a/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/PushQueriesIT.java b/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/PushQueriesIT.java index c59e07a326350..52e6038378137 100644 --- a/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/PushQueriesIT.java +++ b/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/PushQueriesIT.java @@ -150,7 +150,7 @@ public void testEqualityAndOther() throws IOException { * single_value_match is here because there are extra documents hiding in the index * that don't have the `foo` field. */ - List.of("#foo:[1 TO 1] #single_value_match(foo)", "foo:[1 TO 1]"); + List.of("#(#foo:[1 TO 1] #single_value_match(foo)) #FieldExistsQuery [field=_primary_term]", "foo:[1 TO 1]"); default -> throw new UnsupportedOperationException("unknown type [" + type + "]"); }; boolean filterInCompute = switch (type) { diff --git a/x-pack/plugin/mapper-aggregate-metric/src/test/java/org/elasticsearch/xpack/aggregatemetric/mapper/AggregateMetricDoubleFieldTypeTests.java b/x-pack/plugin/mapper-aggregate-metric/src/test/java/org/elasticsearch/xpack/aggregatemetric/mapper/AggregateMetricDoubleFieldTypeTests.java index 55ecfc13b1f3e..ee5609d8f5735 100644 --- a/x-pack/plugin/mapper-aggregate-metric/src/test/java/org/elasticsearch/xpack/aggregatemetric/mapper/AggregateMetricDoubleFieldTypeTests.java +++ b/x-pack/plugin/mapper-aggregate-metric/src/test/java/org/elasticsearch/xpack/aggregatemetric/mapper/AggregateMetricDoubleFieldTypeTests.java @@ -6,6 +6,7 @@ */ package org.elasticsearch.xpack.aggregatemetric.mapper; +import org.apache.lucene.document.DoubleField; import org.apache.lucene.document.DoublePoint; import org.apache.lucene.document.NumericDocValuesField; import org.apache.lucene.index.DirectoryReader; @@ -65,7 +66,7 @@ protected AggregateMetricDoubleFieldType createDefaultFieldType(String name, Map public void testTermQuery() { final MappedFieldType fieldType = createDefaultFieldType("foo", Collections.emptyMap(), Metric.max); Query query = fieldType.termQuery(55.2, MOCK_CONTEXT); - assertThat(query, equalTo(DoublePoint.newRangeQuery("foo.max", 55.2, 55.2))); + assertThat(query, equalTo(DoubleField.newRangeQuery("foo.max", 55.2, 55.2))); } public void testTermsQuery() {