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 f0e539b44ca44..9edf0f0a8a7d1 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.exc.InputCoercionException; + import org.apache.lucene.document.DoublePoint; import org.apache.lucene.document.Field; import org.apache.lucene.document.FieldType; @@ -66,6 +67,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.BiFunction; import java.util.function.Function; /** A {@link FieldMapper} for numeric types: byte, short, int, long, float and double. */ @@ -748,44 +750,17 @@ public Query termsQuery(String field, List values) { public Query rangeQuery(String field, Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, boolean hasDocValues, QueryShardContext context) { - long l = Long.MIN_VALUE; - long u = Long.MAX_VALUE; - if (lowerTerm != null) { - l = parse(lowerTerm, true); - // if the lower bound is decimal: - // - if the bound is positive then we increment it: - // if lowerTerm=1.5 then the (inclusive) bound becomes 2 - // - if the bound is negative then we leave it as is: - // if lowerTerm=-1.5 then the (inclusive) bound becomes -1 due to the call to longValue - boolean lowerTermHasDecimalPart = hasDecimalPart(lowerTerm); - if ((lowerTermHasDecimalPart == false && includeLower == false) || - (lowerTermHasDecimalPart && signum(lowerTerm) > 0)) { - if (l == Long.MAX_VALUE) { - return new MatchNoDocsQuery(); - } - ++l; - } - } - if (upperTerm != null) { - u = parse(upperTerm, true); - boolean upperTermHasDecimalPart = hasDecimalPart(upperTerm); - if ((upperTermHasDecimalPart == false && includeUpper == false) || - (upperTermHasDecimalPart && signum(upperTerm) < 0)) { - if (u == Long.MIN_VALUE) { - return new MatchNoDocsQuery(); + return longRangeQuery(lowerTerm, upperTerm, includeLower, includeUpper, (l, u) -> { + Query query = LongPoint.newRangeQuery(field, l, u); + if (hasDocValues) { + Query dvQuery = SortedNumericDocValuesField.newSlowRangeQuery(field, l, u); + query = new IndexOrDocValuesQuery(query, dvQuery); + if (context.indexSortedOnField(field)) { + query = new IndexSortSortedNumericDocValuesRangeQuery(field, l, u, query); } - --u; } - } - Query query = LongPoint.newRangeQuery(field, l, u); - if (hasDocValues) { - Query dvQuery = SortedNumericDocValuesField.newSlowRangeQuery(field, l, u); - query = new IndexOrDocValuesQuery(query, dvQuery); - if (context.indexSortedOnField(field)) { - query = new IndexSortSortedNumericDocValuesRangeQuery(field, l, u, query); - } - } - return query; + return query; + }); } @Override @@ -855,7 +830,7 @@ public static boolean hasDecimalPart(Object number) { /** * Returns -1, 0, or 1 if the value is lower than, equal to, or greater than 0 */ - double signum(Object value) { + static double signum(Object value) { if (value instanceof Number) { double doubleValue = ((Number) value).doubleValue(); return Math.signum(doubleValue); @@ -906,6 +881,47 @@ public static long objectToLong(Object value, boolean coerce) { String stringValue = (value instanceof BytesRef) ? ((BytesRef) value).utf8ToString() : value.toString(); return Numbers.toLong(stringValue, coerce); } + + /** + * Processes query bounds into {@code long}s and delegates the + * provided {@code builder} to build a range query. + */ + public static Query longRangeQuery( + Object lowerTerm, + Object upperTerm, + boolean includeLower, + boolean includeUpper, + BiFunction builder + ) { + long l = Long.MIN_VALUE; + long u = Long.MAX_VALUE; + if (lowerTerm != null) { + l = objectToLong(lowerTerm, true); + // if the lower bound is decimal: + // - if the bound is positive then we increment it: + // if lowerTerm=1.5 then the (inclusive) bound becomes 2 + // - if the bound is negative then we leave it as is: + // if lowerTerm=-1.5 then the (inclusive) bound becomes -1 due to the call to longValue + boolean lowerTermHasDecimalPart = hasDecimalPart(lowerTerm); + if ((lowerTermHasDecimalPart == false && includeLower == false) || (lowerTermHasDecimalPart && signum(lowerTerm) > 0)) { + if (l == Long.MAX_VALUE) { + return new MatchNoDocsQuery(); + } + ++l; + } + } + if (upperTerm != null) { + u = objectToLong(upperTerm, true); + boolean upperTermHasDecimalPart = hasDecimalPart(upperTerm); + if ((upperTermHasDecimalPart == false && includeUpper == false) || (upperTermHasDecimalPart && signum(upperTerm) < 0)) { + if (u == Long.MIN_VALUE) { + return new MatchNoDocsQuery(); + } + --u; + } + } + return builder.apply(l, u); + } } public static final class NumberFieldType extends SimpleMappedFieldType { diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldType.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldType.java index ad2690e9db65f..cb54a94f5a4e7 100644 --- a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldType.java +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldType.java @@ -6,16 +6,25 @@ package org.elasticsearch.xpack.runtimefields.mapper; +import com.carrotsearch.hppc.LongHashSet; +import com.carrotsearch.hppc.LongSet; + import org.apache.lucene.search.Query; +import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.lucene.search.Queries; +import org.elasticsearch.common.time.DateMathParser; import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.script.Script; import org.elasticsearch.xpack.runtimefields.LongScriptFieldScript; import org.elasticsearch.xpack.runtimefields.fielddata.ScriptLongFieldData; import org.elasticsearch.xpack.runtimefields.query.LongScriptFieldExistsQuery; +import org.elasticsearch.xpack.runtimefields.query.LongScriptFieldRangeQuery; import org.elasticsearch.xpack.runtimefields.query.LongScriptFieldTermQuery; +import org.elasticsearch.xpack.runtimefields.query.LongScriptFieldTermsQuery; +import java.time.ZoneId; +import java.util.List; import java.util.Map; public class ScriptLongMappedFieldType extends AbstractScriptMappedFieldType { @@ -52,6 +61,27 @@ public Query existsQuery(QueryShardContext context) { return new LongScriptFieldExistsQuery(script, leafFactory(context), name()); } + @Override + public Query rangeQuery( + Object lowerTerm, + Object upperTerm, + boolean includeLower, + boolean includeUpper, + ShapeRelation relation, + ZoneId timeZone, + DateMathParser parser, + QueryShardContext context + ) { + checkAllowExpensiveQueries(context); + return NumberType.longRangeQuery( + lowerTerm, + upperTerm, + includeLower, + includeUpper, + (l, u) -> new LongScriptFieldRangeQuery(script, leafFactory(context), name(), l, u) + ); + } + @Override public Query termQuery(Object value, QueryShardContext context) { if (NumberType.hasDecimalPart(value)) { @@ -60,4 +90,23 @@ public Query termQuery(Object value, QueryShardContext context) { checkAllowExpensiveQueries(context); return new LongScriptFieldTermQuery(script, leafFactory(context), name(), NumberType.objectToLong(value, true)); } + + @Override + public Query termsQuery(List values, QueryShardContext context) { + if (values.isEmpty()) { + return Queries.newMatchAllQuery(); + } + LongSet terms = new LongHashSet(values.size()); + for (Object value : values) { + if (NumberType.hasDecimalPart(value)) { + continue; + } + terms.add(NumberType.objectToLong(value, true)); + } + if (terms.isEmpty()) { + return Queries.newMatchNoDocsQuery("All values have a decimal part"); + } + checkAllowExpensiveQueries(context); + return new LongScriptFieldTermsQuery(script, leafFactory(context), name(), terms); + } } diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldRangeQuery.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldRangeQuery.java new file mode 100644 index 0000000000000..db403cffc50c3 --- /dev/null +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldRangeQuery.java @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.runtimefields.query; + +import org.elasticsearch.script.Script; +import org.elasticsearch.xpack.runtimefields.LongScriptFieldScript; + +import java.util.Objects; + +public class LongScriptFieldRangeQuery extends AbstractLongScriptFieldQuery { + private final long lowerValue; + private final long upperValue; + + public LongScriptFieldRangeQuery( + Script script, + LongScriptFieldScript.LeafFactory leafFactory, + String fieldName, + long lowerValue, + long upperValue + ) { + super(script, leafFactory, fieldName); + this.lowerValue = lowerValue; + this.upperValue = upperValue; + assert lowerValue <= upperValue; + } + + @Override + protected boolean matches(long[] values, int count) { + for (int i = 0; i < count; i++) { + if (lowerValue <= values[i] && values[i] <= upperValue) { + return true; + } + } + return false; + } + + @Override + public final String toString(String field) { + StringBuilder b = new StringBuilder(); + if (false == fieldName().contentEquals(field)) { + b.append(fieldName()).append(':'); + } + b.append('[').append(lowerValue).append(" TO ").append(upperValue).append(']'); + return b.toString(); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), lowerValue, upperValue); + } + + @Override + public boolean equals(Object obj) { + if (false == super.equals(obj)) { + return false; + } + LongScriptFieldRangeQuery other = (LongScriptFieldRangeQuery) obj; + return lowerValue == other.lowerValue && upperValue == other.upperValue; + } + + long lowerValue() { + return lowerValue; + } + + long upperValue() { + return upperValue; + } +} diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldTermsQuery.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldTermsQuery.java new file mode 100644 index 0000000000000..4176286ee6516 --- /dev/null +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldTermsQuery.java @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.runtimefields.query; + +import com.carrotsearch.hppc.LongSet; + +import org.elasticsearch.script.Script; +import org.elasticsearch.xpack.runtimefields.LongScriptFieldScript; + +import java.util.Objects; + +public class LongScriptFieldTermsQuery extends AbstractLongScriptFieldQuery { + private final LongSet terms; + + public LongScriptFieldTermsQuery(Script script, LongScriptFieldScript.LeafFactory leafFactory, String fieldName, LongSet terms) { + super(script, leafFactory, fieldName); + this.terms = terms; + } + + @Override + protected boolean matches(long[] values, int count) { + for (int i = 0; i < count; i++) { + if (terms.contains(values[i])) { + return true; + } + } + return false; + } + + @Override + public final String toString(String field) { + if (fieldName().contentEquals(field)) { + return terms.toString(); + } + return fieldName() + ":" + terms; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), terms); + } + + @Override + public boolean equals(Object obj) { + if (false == super.equals(obj)) { + return false; + } + LongScriptFieldTermsQuery other = (LongScriptFieldTermsQuery) obj; + return terms.equals(other.terms); + } + + LongSet terms() { + return terms; + } +} diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldTypeTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldTypeTests.java index e4279b54bd3cb..8474227a9c0d5 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldTypeTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldTypeTests.java @@ -105,6 +105,42 @@ public void testExistsQueryIsExpensive() throws IOException { checkExpensiveQuery(ScriptLongMappedFieldType::existsQuery); } + public void testRangeQuery() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": 1}")))); + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": 2}")))); + try (DirectoryReader reader = iw.getReader()) { + IndexSearcher searcher = newSearcher(reader); + assertThat( + searcher.count(build("value(source.foo)").rangeQuery("2", "3", true, true, null, null, null, mockContext())), + equalTo(1) + ); + assertThat( + searcher.count(build("value(source.foo)").rangeQuery(2, 3, true, true, null, null, null, mockContext())), + equalTo(1) + ); + assertThat( + searcher.count(build("value(source.foo)").rangeQuery(1.1, 3, true, true, null, null, null, mockContext())), + equalTo(1) + ); + assertThat( + searcher.count(build("value(source.foo)").rangeQuery(1.1, 3, false, true, null, null, null, mockContext())), + equalTo(1) + ); + assertThat( + searcher.count(build("value(source.foo)").rangeQuery(2, 3, false, true, null, null, null, mockContext())), + equalTo(0) + ); + } + } + } + + public void testRangeQueryIsExpensive() throws IOException { + checkExpensiveQuery( + (ft, ctx) -> ft.rangeQuery(randomLong(), randomLong(), randomBoolean(), randomBoolean(), null, null, null, ctx) + ); + } + public void testTermQuery() throws IOException { try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": 1}")))); @@ -126,6 +162,25 @@ public void testTermQueryIsExpensive() throws IOException { checkExpensiveQuery((ft, ctx) -> ft.termQuery(randomLong(), ctx)); } + public void testTermsQuery() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": 1}")))); + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": 2}")))); + try (DirectoryReader reader = iw.getReader()) { + IndexSearcher searcher = newSearcher(reader); + assertThat(searcher.count(build("value(source.foo)").termsQuery(List.of("1"), mockContext())), equalTo(1)); + assertThat(searcher.count(build("value(source.foo)").termsQuery(List.of(1), mockContext())), equalTo(1)); + assertThat(searcher.count(build("value(source.foo)").termsQuery(List.of(1.1), mockContext())), equalTo(0)); + assertThat(searcher.count(build("value(source.foo)").termsQuery(List.of(1.1, 2), mockContext())), equalTo(1)); + assertThat(searcher.count(build("value(source.foo)").termsQuery(List.of(2, 1), mockContext())), equalTo(2)); + } + } + } + + public void testTermsQueryIsExpensive() throws IOException { + checkExpensiveQuery((ft, ctx) -> ft.termsQuery(List.of(randomLong()), ctx)); + } + private ScriptLongMappedFieldType build(String code) throws IOException { return build(new Script(code)); } diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/AbstractScriptFieldQueryTestCase.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/AbstractScriptFieldQueryTestCase.java index 25cb5e595b94a..743450cd5f933 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/AbstractScriptFieldQueryTestCase.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/AbstractScriptFieldQueryTestCase.java @@ -32,6 +32,7 @@ public final void testEqualsAndHashCode() { public final void testToString() { T query = createTestInstance(); assertThat(query.toString(), equalTo(query.fieldName() + ":" + query.toString(query.fieldName()))); + assertToString(query); } protected abstract void assertToString(T query); diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldExistsQueryTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldExistsQueryTests.java index 706dc3cd023eb..a77e8c5011f11 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldExistsQueryTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldExistsQueryTests.java @@ -36,6 +36,6 @@ public void testMatches() { @Override protected void assertToString(LongScriptFieldExistsQuery query) { - assertThat(query.toString(query.fieldName()), equalTo("ScriptFieldExists")); + assertThat(query.toString(query.fieldName()), equalTo("LongScriptFieldExistsQuery")); } } diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldRangeQueryTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldRangeQueryTests.java new file mode 100644 index 0000000000000..5db5644dbfa62 --- /dev/null +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldRangeQueryTests.java @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.runtimefields.query; + +import org.elasticsearch.script.Script; +import org.elasticsearch.test.ESTestCase; + +import static org.hamcrest.Matchers.equalTo; + +public class LongScriptFieldRangeQueryTests extends AbstractLongScriptFieldQueryTestCase { + @Override + protected LongScriptFieldRangeQuery createTestInstance() { + long lower = randomLong(); + long upper = randomValueOtherThan(lower, ESTestCase::randomLong); + if (lower > upper) { + long tmp = lower; + lower = upper; + upper = tmp; + } + return new LongScriptFieldRangeQuery(randomScript(), leafFactory, randomAlphaOfLength(5), lower, upper); + } + + @Override + protected LongScriptFieldRangeQuery copy(LongScriptFieldRangeQuery orig) { + return new LongScriptFieldRangeQuery(orig.script(), leafFactory, orig.fieldName(), orig.lowerValue(), orig.upperValue()); + } + + @Override + protected LongScriptFieldRangeQuery mutate(LongScriptFieldRangeQuery orig) { + Script script = orig.script(); + String fieldName = orig.fieldName(); + long lower = orig.lowerValue(); + long upper = orig.upperValue(); + switch (randomInt(3)) { + case 0: + script = randomValueOtherThan(script, this::randomScript); + break; + case 1: + fieldName += "modified"; + break; + case 2: + if (lower == Long.MIN_VALUE) { + fieldName += "modified_instead_of_lower"; + } else { + lower -= 1; + } + break; + case 3: + if (upper == Long.MAX_VALUE) { + fieldName += "modified_instead_of_upper"; + } else { + upper += 1; + } + break; + default: + fail(); + } + return new LongScriptFieldRangeQuery(script, leafFactory, fieldName, lower, upper); + } + + @Override + public void testMatches() { + LongScriptFieldRangeQuery query = new LongScriptFieldRangeQuery(randomScript(), leafFactory, "test", 1, 3); + assertTrue(query.matches(new long[] { 1 }, 1)); + assertTrue(query.matches(new long[] { 2 }, 1)); + assertTrue(query.matches(new long[] { 3 }, 1)); + assertFalse(query.matches(new long[] { 1 }, 0)); + assertFalse(query.matches(new long[] { 5 }, 1)); + assertTrue(query.matches(new long[] { 1, 5 }, 2)); + assertTrue(query.matches(new long[] { 5, 1 }, 2)); + } + + @Override + protected void assertToString(LongScriptFieldRangeQuery query) { + assertThat(query.toString(query.fieldName()), equalTo("[" + query.lowerValue() + " TO " + query.upperValue() + "]")); + } +} diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldTermsQueryTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldTermsQueryTests.java new file mode 100644 index 0000000000000..a797c05e8112e --- /dev/null +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldTermsQueryTests.java @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.runtimefields.query; + +import com.carrotsearch.hppc.LongHashSet; +import com.carrotsearch.hppc.LongSet; + +import org.elasticsearch.script.Script; + +import static org.hamcrest.Matchers.equalTo; + +public class LongScriptFieldTermsQueryTests extends AbstractLongScriptFieldQueryTestCase { + @Override + protected LongScriptFieldTermsQuery createTestInstance() { + LongSet terms = new LongHashSet(); + int count = between(1, 100); + while (terms.size() < count) { + terms.add(randomLong()); + } + return new LongScriptFieldTermsQuery(randomScript(), leafFactory, randomAlphaOfLength(5), terms); + } + + @Override + protected LongScriptFieldTermsQuery copy(LongScriptFieldTermsQuery orig) { + return new LongScriptFieldTermsQuery(orig.script(), leafFactory, orig.fieldName(), orig.terms()); + } + + @Override + protected LongScriptFieldTermsQuery mutate(LongScriptFieldTermsQuery orig) { + Script script = orig.script(); + String fieldName = orig.fieldName(); + LongSet terms = orig.terms(); + switch (randomInt(2)) { + case 0: + script = randomValueOtherThan(script, this::randomScript); + break; + case 1: + fieldName += "modified"; + break; + case 2: + terms = new LongHashSet(terms); + while (false == terms.add(randomLong())) { + // Random long was already in the set + } + break; + default: + fail(); + } + return new LongScriptFieldTermsQuery(script, leafFactory, fieldName, terms); + } + + @Override + public void testMatches() { + LongScriptFieldTermsQuery query = new LongScriptFieldTermsQuery(randomScript(), leafFactory, "test", LongHashSet.from(1, 2, 3)); + assertTrue(query.matches(new long[] { 1 }, 1)); + assertTrue(query.matches(new long[] { 2 }, 1)); + assertTrue(query.matches(new long[] { 3 }, 1)); + assertTrue(query.matches(new long[] { 1, 0 }, 2)); + assertTrue(query.matches(new long[] { 0, 1 }, 2)); + assertFalse(query.matches(new long[] { 0 }, 1)); + assertFalse(query.matches(new long[] { 0, 1 }, 1)); + } + + @Override + protected void assertToString(LongScriptFieldTermsQuery query) { + assertThat(query.toString(query.fieldName()), equalTo(query.terms().toString())); + } +} diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldExistsQueryTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldExistsQueryTests.java index f900145251d02..c10ca022703c0 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldExistsQueryTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldExistsQueryTests.java @@ -43,7 +43,7 @@ public void testMatches() { @Override protected void assertToString(StringScriptFieldExistsQuery query) { - assertThat(query.toString(query.fieldName()), equalTo("ScriptFieldExists")); + assertThat(query.toString(query.fieldName()), equalTo("StringScriptFieldExistsQuery")); } @Override diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldFuzzyQueryTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldFuzzyQueryTests.java index 014a499abf942..b28ea7612849d 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldFuzzyQueryTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldFuzzyQueryTests.java @@ -95,7 +95,10 @@ public void testMatches() { @Override protected void assertToString(StringScriptFieldFuzzyQuery query) { - assertThat(query.toString(query.fieldName()), equalTo(query.delegate().getTerm().bytes().utf8ToString())); + assertThat( + query.toString(query.fieldName()), + equalTo(query.delegate().getTerm().bytes().utf8ToString() + "~" + query.delegate().getMaxEdits()) + ); } @Override diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldPrefixQueryTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldPrefixQueryTests.java index ad1efc1d43b2d..8e63621b998a2 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldPrefixQueryTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldPrefixQueryTests.java @@ -58,7 +58,7 @@ public void testMatches() { @Override protected void assertToString(StringScriptFieldPrefixQuery query) { - assertThat(query.toString(query.fieldName()), equalTo(query.prefix())); + assertThat(query.toString(query.fieldName()), equalTo(query.prefix() + "*")); } @Override diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldWildcardQueryTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldWildcardQueryTests.java index 0841585e07fc1..e7c5a62911180 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldWildcardQueryTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/StringScriptFieldWildcardQueryTests.java @@ -60,7 +60,7 @@ public void testMatches() { @Override protected void assertToString(StringScriptFieldWildcardQuery query) { - assertThat(query.toString(query.fieldName()), equalTo("/" + query.pattern() + "/")); + assertThat(query.toString(query.fieldName()), equalTo(query.pattern())); } @Override diff --git a/x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/20_long.yml b/x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/20_long.yml index d5f716b362630..1a978db1f2575 100644 --- a/x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/20_long.yml +++ b/x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/20_long.yml @@ -101,3 +101,77 @@ setup: - match: {hits.hits.2._source.voltage: 5.6} - match: {hits.hits.3._source.voltage: 5.8} - match: {hits.hits.4._source.voltage: 5.2} + +--- +"range query": + - do: + search: + index: sensor + body: + query: + range: + voltage_times_ten: + gt: 57 + - match: {hits.total.value: 1} + - match: {hits.hits.0._source.voltage: 5.8} + + - do: + search: + index: sensor + body: + query: + range: + voltage_times_ten: + gt: 58 + - match: {hits.total.value: 0} + + - do: + search: + index: sensor + body: + query: + range: + voltage_times_ten: + gte: 58 + - match: {hits.total.value: 1} + - match: {hits.hits.0._source.voltage: 5.8} + + - do: + search: + index: sensor + body: + query: + range: + voltage_times_ten: + gte: 51 + lte: 52 + sort: timestamp + - match: {hits.total.value: 2} + - match: {hits.hits.0._source.voltage: 5.1} + - match: {hits.hits.1._source.voltage: 5.2} + +--- +"term query": + - do: + search: + index: sensor + body: + query: + term: + voltage_times_ten: 58 + - match: {hits.total.value: 1} + - match: {hits.hits.0._source.voltage: 5.8} + +--- +"terms query": + - do: + search: + index: sensor + body: + query: + terms: + voltage_times_ten: [58, 59, 40] + sort: timestamp + - match: {hits.total.value: 2} + - match: {hits.hits.0._source.voltage: 4.0} + - match: {hits.hits.1._source.voltage: 5.8}