diff --git a/docs/changelog/99801.yaml b/docs/changelog/99801.yaml new file mode 100644 index 0000000000000..d92e99c67e2b7 --- /dev/null +++ b/docs/changelog/99801.yaml @@ -0,0 +1,5 @@ +pr: 99801 +summary: Feature/allow numeric field exact searches to use docvalues +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 14c180dc0b65c..f9b31325c45d7 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 @@ -258,7 +258,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 @@ -270,7 +270,12 @@ public Query termsQuery(Collection values, SearchExecutionContext context) { long scaledValue = Math.round(scale(value)); scaledValues.add(scaledValue); } - return NumberFieldMapper.NumberType.LONG.termsQuery(name(), Collections.unmodifiableList(scaledValues)); + return NumberFieldMapper.NumberType.LONG.termsQuery( + name(), + Collections.unmodifiableList(scaledValues), + isIndexed(), + hasDocValues() + ); } else { return super.termsQuery(values, context); } 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 222f0f05d548d..43ad35c0d0755 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 @@ -15,6 +15,7 @@ import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.search.IndexOrDocValuesQuery; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.store.Directory; @@ -46,7 +47,10 @@ 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)); + Query indexQuery = LongPoint.newExactQuery("scaled_float", scaledValue); + Query dvQuery = SortedNumericDocValuesField.newSlowExactQuery("scaled_float", scaledValue); + Query query = new IndexOrDocValuesQuery(indexQuery, dvQuery); + assertEquals(query, 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)); @@ -65,10 +69,10 @@ public void testTermsQuery() { long scaledValue1 = Math.round(value1 * ft.getScalingFactor()); double value2 = (randomDouble() * 2 - 1) * 10000; long scaledValue2 = Math.round(value2 * ft.getScalingFactor()); - assertEquals( - LongPoint.newSetQuery("scaled_float", scaledValue1, scaledValue2), - ft.termsQuery(Arrays.asList(value1, value2), MOCK_CONTEXT) - ); + Query indexQuery = LongPoint.newSetQuery("scaled_float", scaledValue1, scaledValue2); + Query dvQuery = SortedNumericDocValuesField.newSlowSetQuery("scaled_float", scaledValue1, scaledValue2); + Query query = new IndexOrDocValuesQuery(indexQuery, dvQuery); + assertEquals(query, ft.termsQuery(Arrays.asList(value1, value2), MOCK_CONTEXT)); MappedFieldType ft2 = new ScaledFloatFieldMapper.ScaledFloatFieldType("scaled_float", 0.1 + randomDouble() * 100, false); ElasticsearchException e2 = expectThrows( 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 fce0fb7a83ae4..a3a0103cb86ef 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java @@ -76,6 +76,7 @@ import java.util.Set; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.stream.IntStream; /** A {@link FieldMapper} for numeric types: byte, short, int, long, float and double. */ public class NumberFieldMapper extends FieldMapper { @@ -318,18 +319,23 @@ public Float parse(XContentParser parser, boolean coerce) throws IOException { } @Override - public Query termQuery(String field, Object value, boolean isIndexed) { - float v = parseToFloat(value); - validateFiniteValue(v); + public Query termQuery(String field, Object value, boolean isIndexed, boolean hasDocValues) { + float fValue = parseToFloat(value); + validateFiniteValue(fValue); if (isIndexed) { - return HalfFloatPoint.newExactQuery(field, v); + Query query = HalfFloatPoint.newExactQuery(field, fValue); + if (hasDocValues) { + Query dvQuery = SortedNumericDocValuesField.newSlowExactQuery(field, NumericUtils.floatToSortableInt(fValue)); + query = new IndexOrDocValuesQuery(query, dvQuery); + } + return query; } else { - return SortedNumericDocValuesField.newSlowExactQuery(field, HalfFloatPoint.halfFloatToSortableShort(v)); + return SortedNumericDocValuesField.newSlowExactQuery(field, HalfFloatPoint.halfFloatToSortableShort(fValue)); } } @Override - public Query termsQuery(String field, Collection values) { + public Query termsQuery(String field, Collection values, boolean isIndexed, boolean hasDocValues) { float[] v = new float[values.size()]; int pos = 0; for (Object value : values) { @@ -337,7 +343,16 @@ public Query termsQuery(String field, Collection values) { validateFiniteValue(float_value); v[pos++] = float_value; } - return HalfFloatPoint.newSetQuery(field, v); + Query query = HalfFloatPoint.newSetQuery(field, v); + if (hasDocValues) { + long[] longValues = IntStream.range(0, v.length) + .map(HalfFloatPoint::halfFloatToSortableShort) + .mapToLong(i -> (long) i) + .toArray(); + Query dvQuery = SortedNumericDocValuesField.newSlowSetQuery(field, longValues); + query = new IndexOrDocValuesQuery(query, dvQuery); + } + return query; } @Override @@ -500,23 +515,37 @@ public Float parse(XContentParser parser, boolean coerce) throws IOException { } @Override - public Query termQuery(String field, Object value, boolean isIndexed) { - float v = parseToFloat(value); + public Query termQuery(String field, Object value, boolean isIndexed, boolean hasDocValues) { + float fValue = parseToFloat(value); if (isIndexed) { - return FloatPoint.newExactQuery(field, v); + Query query = FloatPoint.newExactQuery(field, fValue); + if (hasDocValues) { + Query dvQuery = SortedNumericDocValuesField.newSlowExactQuery(field, NumericUtils.floatToSortableInt(fValue)); + query = new IndexOrDocValuesQuery(query, dvQuery); + } + return query; } else { - return SortedNumericDocValuesField.newSlowExactQuery(field, NumericUtils.floatToSortableInt(v)); + return SortedNumericDocValuesField.newSlowExactQuery(field, NumericUtils.floatToSortableInt(fValue)); } } @Override - public Query termsQuery(String field, Collection values) { + public Query termsQuery(String field, Collection values, boolean isIndexed, boolean hasDocValues) { float[] v = new float[values.size()]; int pos = 0; for (Object value : values) { v[pos++] = parse(value, false); } - return FloatPoint.newSetQuery(field, v); + Query query = FloatPoint.newSetQuery(field, v); + if (hasDocValues) { + long[] longValues = IntStream.range(0, v.length) + .map(NumericUtils::floatToSortableInt) + .mapToLong(i -> (long) i) + .toArray(); + Query dvQuery = SortedNumericDocValuesField.newSlowSetQuery(field, longValues); + query = new IndexOrDocValuesQuery(query, dvQuery); + } + return query; } @Override @@ -664,19 +693,31 @@ 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 = parse(value, false); if (isIndexed) { - return DoublePoint.newExactQuery(field, v); + Query query = DoublePoint.newExactQuery(field, v); + if (hasDocValues) { + SortedNumericDocValuesField.newSlowExactQuery(field, NumericUtils.doubleToSortableLong(v)); + } + return query; } else { return SortedNumericDocValuesField.newSlowExactQuery(field, NumericUtils.doubleToSortableLong(v)); } } @Override - public Query termsQuery(String field, Collection values) { + public Query termsQuery(String field, Collection values, boolean isIndexed, boolean hasDocValues) { double[] v = values.stream().mapToDouble(value -> parse(value, false)).toArray(); - return DoublePoint.newSetQuery(field, v); + Query query = DoublePoint.newSetQuery(field, v); + if (hasDocValues) { + Query dvQuery = SortedNumericDocValuesField.newSlowSetQuery( + field, + Arrays.stream(v).mapToLong(NumericUtils::doubleToSortableLong).toArray() + ); + query = new IndexOrDocValuesQuery(query, dvQuery); + } + return query; } @Override @@ -815,13 +856,13 @@ public Short parse(XContentParser parser, boolean coerce) throws IOException { } @Override - public Query termQuery(String field, Object value, boolean isIndexed) { - return INTEGER.termQuery(field, value, isIndexed); + public Query termQuery(String field, Object value, boolean isIndexed, boolean hasDocValues) { + return INTEGER.termQuery(field, value, isIndexed, hasDocValues); } @Override - public Query termsQuery(String field, Collection values) { - return INTEGER.termsQuery(field, values); + public Query termsQuery(String field, Collection values, boolean isIndexed, boolean hasDocValues) { + return INTEGER.termsQuery(field, values, isIndexed, hasDocValues); } @Override @@ -920,13 +961,13 @@ public Short parse(XContentParser parser, boolean coerce) throws IOException { } @Override - public Query termQuery(String field, Object value, boolean isIndexed) { - return INTEGER.termQuery(field, value, isIndexed); + public Query termQuery(String field, Object value, boolean isIndexed, boolean hasDocValues) { + return INTEGER.termQuery(field, value, isIndexed, hasDocValues); } @Override - public Query termsQuery(String field, Collection values) { - return INTEGER.termsQuery(field, values); + public Query termsQuery(String field, Collection values, boolean isIndexed, boolean hasDocValues) { + return INTEGER.termsQuery(field, values, isIndexed, hasDocValues); } @Override @@ -1025,26 +1066,40 @@ 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"); } - int v = parse(value, true); + int iValue = parse(value, true); if (isIndexed) { - return IntPoint.newExactQuery(field, v); + Query query = IntPoint.newExactQuery(field, iValue); + if (hasDocValues) { + Query dvQuery = SortedNumericDocValuesField.newSlowExactQuery(field, iValue); + query = new IndexOrDocValuesQuery(query, dvQuery); + } + return query; } else { - return SortedNumericDocValuesField.newSlowExactQuery(field, v); + return SortedNumericDocValuesField.newSlowExactQuery(field, iValue); } } @Override - public Query termsQuery(String field, Collection values) { + public Query termsQuery(String field, Collection values, boolean isIndexed, boolean hasDocValues) { int[] v = new int[values.size()]; int upTo = 0; + int lowerValue = Integer.MAX_VALUE; + int upperValue = Integer.MIN_VALUE; for (Object value : values) { if (hasDecimalPart(value) == false) { - v[upTo++] = parse(value, true); + int iValue = parse(value, true); + v[upTo++] = iValue; + if (iValue > upperValue) { + upperValue = iValue; + } + if (iValue < lowerValue) { + lowerValue = iValue; + } } } @@ -1054,7 +1109,13 @@ public Query termsQuery(String field, Collection values) { if (upTo != v.length) { v = Arrays.copyOf(v, upTo); } - return IntPoint.newSetQuery(field, v); + + Query query = IntPoint.newSetQuery(field, v); + if (hasDocValues) { + Query dvQuery = SortedNumericDocValuesField.newSlowSetQuery(field, Arrays.stream(v).asLongStream().toArray()); + query = new IndexOrDocValuesQuery(query, dvQuery); + } + return query; } @Override @@ -1193,20 +1254,25 @@ 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"); } - long v = parse(value, true); + long lValue = parse(value, true); if (isIndexed) { - return LongPoint.newExactQuery(field, v); + Query query = LongPoint.newExactQuery(field, lValue); + if (hasDocValues) { + Query dvQuery = SortedNumericDocValuesField.newSlowExactQuery(field, lValue); + query = new IndexOrDocValuesQuery(query, dvQuery); + } + return query; } else { - return SortedNumericDocValuesField.newSlowExactQuery(field, v); + return SortedNumericDocValuesField.newSlowExactQuery(field, lValue); } } @Override - public Query termsQuery(String field, Collection values) { + public Query termsQuery(String field, Collection values, boolean isIndexed, boolean hasDocValues) { long[] v = new long[values.size()]; int upTo = 0; @@ -1222,7 +1288,13 @@ public Query termsQuery(String field, Collection values) { if (upTo != v.length) { v = Arrays.copyOf(v, upTo); } - return LongPoint.newSetQuery(field, v); + + Query query = LongPoint.newSetQuery(field, v); + if (hasDocValues) { + Query dvQuery = SortedNumericDocValuesField.newSlowSetQuery(field, v); + query = new IndexOrDocValuesQuery(query, dvQuery); + } + return query; } @Override @@ -1346,9 +1418,9 @@ 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); + public abstract Query termsQuery(String field, Collection values, boolean isIndexed, boolean hasDocValues); public abstract Query rangeQuery( String field, @@ -1674,14 +1746,14 @@ 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 public Query termsQuery(Collection values, SearchExecutionContext context) { failIfNotIndexedNorDocValuesFallback(context); if (isIndexed()) { - return type.termsQuery(name(), values); + return type.termsQuery(name(), values, isIndexed(), hasDocValues()); } else { return super.termsQuery(values, context); } 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 7acb89a857772..fe99161680c5a 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldTypeTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldTypeTests.java @@ -94,15 +94,21 @@ public void testIsFieldWithinQuery() throws IOException { public void testIntegerTermsQueryWithDecimalPart() { MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberType.INTEGER); - assertEquals(IntPoint.newSetQuery("field", 1), ft.termsQuery(Arrays.asList(1, 2.1), MOCK_CONTEXT)); - assertEquals(IntPoint.newSetQuery("field", 1), ft.termsQuery(Arrays.asList(1.0, 2.1), MOCK_CONTEXT)); + Query indexQuery = IntPoint.newSetQuery("field", 1); + Query dvQuery = SortedNumericDocValuesField.newSlowSetQuery("field", 1); + Query indexOrDocValuesQuery = new IndexOrDocValuesQuery(indexQuery, dvQuery); + assertEquals(indexOrDocValuesQuery, ft.termsQuery(Arrays.asList(1, 2.1), MOCK_CONTEXT)); + assertEquals(indexOrDocValuesQuery, ft.termsQuery(Arrays.asList(1.0, 2.1), MOCK_CONTEXT)); assertTrue(ft.termsQuery(Arrays.asList(1.1, 2.1), MOCK_CONTEXT) instanceof MatchNoDocsQuery); } public void testLongTermsQueryWithDecimalPart() { MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberType.LONG); - assertEquals(LongPoint.newSetQuery("field", 1), ft.termsQuery(Arrays.asList(1, 2.1), MOCK_CONTEXT)); - assertEquals(LongPoint.newSetQuery("field", 1), ft.termsQuery(Arrays.asList(1.0, 2.1), MOCK_CONTEXT)); + Query indexQuery = LongPoint.newSetQuery("field", 1); + Query dvQuery = SortedNumericDocValuesField.newSlowSetQuery("field", 1); + Query indexOrDocValuesQuery = new IndexOrDocValuesQuery(indexQuery, dvQuery); + assertEquals(indexOrDocValuesQuery, ft.termsQuery(Arrays.asList(1, 2.1), MOCK_CONTEXT)); + assertEquals(indexOrDocValuesQuery, ft.termsQuery(Arrays.asList(1.0, 2.1), MOCK_CONTEXT)); assertTrue(ft.termsQuery(Arrays.asList(1.1, 2.1), MOCK_CONTEXT) instanceof MatchNoDocsQuery); } @@ -145,7 +151,9 @@ private static MappedFieldType unsearchable() { public void testTermQuery() { MappedFieldType ft = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.LONG); - assertEquals(LongPoint.newExactQuery("field", 42), ft.termQuery("42", MOCK_CONTEXT)); + Query indexQuery = LongPoint.newExactQuery("field", 42); + Query dvQuery = SortedNumericDocValuesField.newSlowExactQuery("field", 42); + assertEquals(new IndexOrDocValuesQuery(indexQuery, dvQuery), ft.termQuery("42", MOCK_CONTEXT)); ft = new NumberFieldMapper.NumberFieldType("field", NumberFieldMapper.NumberType.LONG, false); assertEquals(SortedNumericDocValuesField.newSlowExactQuery("field", 42), ft.termQuery("42", MOCK_CONTEXT)); @@ -582,6 +590,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) @@ -595,9 +604,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 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 c8bfbe304918c..b72da254ee7c5 100644 --- a/server/src/test/java/org/elasticsearch/index/query/TermQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/TermQueryBuilderTests.java @@ -11,6 +11,7 @@ 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.MatchNoDocsQuery; import org.apache.lucene.search.PointRangeQuery; import org.apache.lucene.search.Query; @@ -90,6 +91,7 @@ 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)) ); MappedFieldType mapper = context.getFieldType(queryBuilder.fieldName()); if (query instanceof TermQuery termQuery) { diff --git a/x-pack/plugin/mapper-aggregate-metric/src/test/java/org/elasticsearch/xpack/aggregatemetric/mapper/AggregateDoubleMetricFieldTypeTests.java b/x-pack/plugin/mapper-aggregate-metric/src/test/java/org/elasticsearch/xpack/aggregatemetric/mapper/AggregateDoubleMetricFieldTypeTests.java index c32e7e583c787..2d35dd9b7200b 100644 --- a/x-pack/plugin/mapper-aggregate-metric/src/test/java/org/elasticsearch/xpack/aggregatemetric/mapper/AggregateDoubleMetricFieldTypeTests.java +++ b/x-pack/plugin/mapper-aggregate-metric/src/test/java/org/elasticsearch/xpack/aggregatemetric/mapper/AggregateDoubleMetricFieldTypeTests.java @@ -8,6 +8,7 @@ import org.apache.lucene.document.DoublePoint; import org.apache.lucene.document.NumericDocValuesField; +import org.apache.lucene.document.SortedNumericDocValuesField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.search.IndexOrDocValuesQuery; import org.apache.lucene.search.IndexSearcher; @@ -15,6 +16,7 @@ import org.apache.lucene.search.Query; import org.apache.lucene.store.Directory; import org.apache.lucene.tests.index.RandomIndexWriter; +import org.apache.lucene.util.NumericUtils; import org.elasticsearch.common.lucene.search.function.ScriptScoreQuery; import org.elasticsearch.index.IndexVersion; import org.elasticsearch.index.fielddata.FieldDataContext; @@ -71,7 +73,16 @@ public void testTermQuery() { public void testTermsQuery() { final MappedFieldType fieldType = createDefaultFieldType("foo", Collections.emptyMap(), Metric.max); Query query = fieldType.termsQuery(asList(55.2, 500.3), MOCK_CONTEXT); - assertThat(query, equalTo(DoublePoint.newSetQuery("foo.max", 55.2, 500.3))); + Query indexQuery = DoublePoint.newSetQuery("foo.max", 55.2, 500.3); + Query dvQuery = SortedNumericDocValuesField.newSlowSetQuery( + "foo.max", + NumericUtils.doubleToSortableLong(55.2), + NumericUtils.doubleToSortableLong(500.3) + ); + assertThat(query, instanceOf(IndexOrDocValuesQuery.class)); + IndexOrDocValuesQuery indexOrDocValuesQuery = (IndexOrDocValuesQuery) query; + assertThat(indexOrDocValuesQuery.getIndexQuery(), equalTo(indexQuery)); + assertThat(indexOrDocValuesQuery.getRandomAccessQuery(), equalTo(dvQuery)); } public void testRangeQuery() { diff --git a/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapper.java b/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapper.java index 05c807cffdd35..70b81f078a827 100644 --- a/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapper.java +++ b/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapper.java @@ -261,7 +261,13 @@ public Query termQuery(Object value, SearchExecutionContext context) { if (longValue == null) { return new MatchNoDocsQuery(); } - return LongPoint.newExactQuery(name(), unsignedToSortableSignedLong(longValue)); + long lValue = unsignedToSortableSignedLong(longValue); + Query query = LongPoint.newExactQuery(name(), lValue); + if (super.hasDocValues()) { + Query dvQuery = SortedNumericDocValuesField.newSlowExactQuery(name(), lValue); + query = new IndexOrDocValuesQuery(query, dvQuery); + } + return query; } @Override @@ -281,7 +287,12 @@ public Query termsQuery(Collection values, SearchExecutionContext context) { if (upTo != lvalues.length) { lvalues = Arrays.copyOf(lvalues, upTo); } - return LongPoint.newSetQuery(name(), lvalues); + Query query = LongPoint.newSetQuery(name(), lvalues); + if (super.hasDocValues()) { + Query dvQuery = SortedNumericDocValuesField.newSlowSetQuery(name(), lvalues); + query = new IndexOrDocValuesQuery(query, dvQuery); + } + return query; } @Override diff --git a/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldTypeTests.java b/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldTypeTests.java index e5f85f8b87b12..1946e52826671 100644 --- a/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldTypeTests.java +++ b/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldTypeTests.java @@ -8,7 +8,10 @@ package org.elasticsearch.xpack.unsignedlong; import org.apache.lucene.document.LongPoint; +import org.apache.lucene.document.SortedNumericDocValuesField; +import org.apache.lucene.search.IndexOrDocValuesQuery; import org.apache.lucene.search.MatchNoDocsQuery; +import org.apache.lucene.search.Query; import org.elasticsearch.index.mapper.FieldTypeTestCase; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MapperBuilderContext; @@ -27,10 +30,21 @@ public class UnsignedLongFieldTypeTests extends FieldTypeTestCase { public void testTermQuery() { UnsignedLongFieldType ft = new UnsignedLongFieldType("my_unsigned_long"); - - assertEquals(LongPoint.newExactQuery("my_unsigned_long", -9223372036854775808L), ft.termQuery(0, null)); - assertEquals(LongPoint.newExactQuery("my_unsigned_long", 0L), ft.termQuery("9223372036854775808", null)); - assertEquals(LongPoint.newExactQuery("my_unsigned_long", 9223372036854775807L), ft.termQuery("18446744073709551615", null)); + Query dvQuery1 = SortedNumericDocValuesField.newSlowExactQuery("my_unsigned_long", -9223372036854775808L); + Query dvQuery2 = SortedNumericDocValuesField.newSlowExactQuery("my_unsigned_long", 0L); + Query dvQuery3 = SortedNumericDocValuesField.newSlowExactQuery("my_unsigned_long", 9223372036854775807L); + assertEquals( + new IndexOrDocValuesQuery(LongPoint.newExactQuery("my_unsigned_long", -9223372036854775808L), dvQuery1), + ft.termQuery(0, null) + ); + assertEquals( + new IndexOrDocValuesQuery(LongPoint.newExactQuery("my_unsigned_long", 0L), dvQuery2), + ft.termQuery("9223372036854775808", null) + ); + assertEquals( + new IndexOrDocValuesQuery(LongPoint.newExactQuery("my_unsigned_long", 9223372036854775807L), dvQuery3), + ft.termQuery("18446744073709551615", null) + ); assertEquals(new MatchNoDocsQuery(), ft.termQuery(-1L, null)); assertEquals(new MatchNoDocsQuery(), ft.termQuery(10.5, null)); @@ -41,9 +55,9 @@ public void testTermQuery() { public void testTermsQuery() { UnsignedLongFieldType ft = new UnsignedLongFieldType("my_unsigned_long"); - + Query dvQuery = SortedNumericDocValuesField.newSlowSetQuery("my_unsigned_long", -9223372036854775808L, 0L, 9223372036854775807L); assertEquals( - LongPoint.newSetQuery("my_unsigned_long", -9223372036854775808L, 0L, 9223372036854775807L), + new IndexOrDocValuesQuery(LongPoint.newSetQuery("my_unsigned_long", -9223372036854775808L, 0L, 9223372036854775807L), dvQuery), ft.termsQuery(List.of("0", "9223372036854775808", "18446744073709551615"), null) );