Skip to content

Commit b0417f3

Browse files
authored
Added approximation support for range queries with now in date field (#18511)
* Added approximation support for range queries with now in date field Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * Updated tests to fit new query hierarchy Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * refactored approximation logic for DateRangeIncludingNowQuery Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * added validation test for DateRangeIncludingNowQuery approximation Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * fixed percolation query analyzer logic to check for DateRangeIncludingNowQuery Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * updated DateFieldTypeTests to reflect new DateRangeIncludingNow query structure Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * refactored date range query to use overloaded function and cleaned up percolation subquery visitor logic Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * cleaned up testApproximateVsExactQuery function signature Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * Rerun gradle check Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * Removed redundancy in DateRangeIncludingNow approximation logic Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * Refactored DateRangeIncludingNowTests for conciseness Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * applied spotless Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * Removed unused handleNow method Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * refactored approximation wrapping logic Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * applied spotless Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * Refactored tests to use DateFieldType Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * applied spotless Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * Refactored DateRangeIncludingNow tests to reflect new parameterized versions Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * update CHANGELOG.md Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> * rerun gradle check Signed-off-by: Sawan Srivastava <sawan1210@gmail.com> --------- Signed-off-by: Sawan Srivastava <sawan1210@gmail.com>
1 parent e1d03f2 commit b0417f3

File tree

6 files changed

+199
-86
lines changed

6 files changed

+199
-86
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
3636
- [Star-Tree] Add star-tree search related stats ([#18707](https://github.com/opensearch-project/OpenSearch/pull/18707))
3737
- Add support for plugins to profile information ([#18656](https://github.com/opensearch-project/OpenSearch/pull/18656))
3838
- Add support for Combined Fields query ([#18724](https://github.com/opensearch-project/OpenSearch/pull/18724))
39+
- Added approximation support for range queries with now in date field ([#18511](https://github.com/opensearch-project/OpenSearch/pull/18511))
3940

4041
### Changed
4142
- Update Subject interface to use CheckedRunnable ([#18570](https://github.com/opensearch-project/OpenSearch/issues/18570))

modules/percolator/src/main/java/org/opensearch/percolator/QueryAnalyzer.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.opensearch.common.lucene.search.function.FunctionScoreQuery;
5656
import org.opensearch.index.query.DateRangeIncludingNowQuery;
5757
import org.opensearch.lucene.queries.BlendedTermQuery;
58+
import org.opensearch.search.approximate.ApproximateScoreQuery;
5859

5960
import java.io.IOException;
6061
import java.io.UncheckedIOException;
@@ -180,7 +181,8 @@ Result getResult() {
180181

181182
@Override
182183
public QueryVisitor getSubVisitor(Occur occur, Query parent) {
183-
if (parent instanceof DateRangeIncludingNowQuery) {
184+
if (parent instanceof ApproximateScoreQuery
185+
&& ((ApproximateScoreQuery) parent).getOriginalQuery() instanceof DateRangeIncludingNowQuery) {
184186
terms.add(Result.UNKNOWN);
185187
return QueryVisitor.EMPTY_VISITOR;
186188
}

server/src/main/java/org/opensearch/index/mapper/DateFieldMapper.java

Lines changed: 54 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.opensearch.Version;
4747
import org.opensearch.common.Explicit;
4848
import org.opensearch.common.Nullable;
49+
import org.opensearch.common.TriFunction;
4950
import org.opensearch.common.geo.ShapeRelation;
5051
import org.opensearch.common.logging.DeprecationLogger;
5152
import org.opensearch.common.lucene.BytesRefs;
@@ -80,7 +81,6 @@
8081
import java.util.Locale;
8182
import java.util.Map;
8283
import java.util.Optional;
83-
import java.util.function.BiFunction;
8484
import java.util.function.Function;
8585
import java.util.function.LongSupplier;
8686
import java.util.function.Supplier;
@@ -511,11 +511,30 @@ public Query rangeQuery(
511511
throw new IllegalArgumentException("Field [" + name() + "] of type [" + typeName() + "] does not support DISJOINT ranges");
512512
}
513513
DateMathParser parser = forcedDateParser == null ? dateMathParser : forcedDateParser;
514-
return dateRangeQuery(lowerTerm, upperTerm, includeLower, includeUpper, timeZone, parser, context, resolution, (l, u) -> {
515-
Query dvQuery = hasDocValues() ? SortedNumericDocValuesField.newSlowRangeQuery(name(), l, u) : null;
516-
if (isSearchable()) {
514+
return dateRangeQuery(
515+
lowerTerm,
516+
upperTerm,
517+
includeLower,
518+
includeUpper,
519+
timeZone,
520+
parser,
521+
context,
522+
resolution,
523+
(l, u, nowUsed) -> {
524+
Query dvQuery = hasDocValues() ? SortedNumericDocValuesField.newSlowRangeQuery(name(), l, u) : null;
525+
526+
// Not searchable. Must have doc values.
527+
if (!isSearchable()) {
528+
if (context.indexSortedOnField(name())) {
529+
dvQuery = new IndexSortSortedNumericDocValuesRangeQuery(name(), l, u, dvQuery);
530+
}
531+
return nowUsed[0] ? new DateRangeIncludingNowQuery(dvQuery) : dvQuery;
532+
}
533+
534+
// Field is searchable
517535
Query pointRangeQuery = LongPoint.newRangeQuery(name(), l, u);
518536
Query query;
537+
519538
if (dvQuery != null) {
520539
query = new IndexOrDocValuesQuery(pointRangeQuery, dvQuery);
521540
if (context.indexSortedOnField(name())) {
@@ -524,24 +543,21 @@ public Query rangeQuery(
524543
} else {
525544
query = pointRangeQuery;
526545
}
527-
return new ApproximateScoreQuery(
528-
query,
529-
new ApproximatePointRangeQuery(
530-
name(),
531-
pack(new long[] { l }).bytes,
532-
pack(new long[] { u }).bytes,
533-
new long[] { l }.length,
534-
ApproximatePointRangeQuery.LONG_FORMAT
535-
)
546+
547+
ApproximatePointRangeQuery approxQuery = new ApproximatePointRangeQuery(
548+
name(),
549+
pack(new long[] { l }).bytes,
550+
pack(new long[] { u }).bytes,
551+
new long[] { l }.length,
552+
ApproximatePointRangeQuery.LONG_FORMAT
536553
);
537-
}
538554

539-
// Not searchable. Must have doc values.
540-
if (context.indexSortedOnField(name())) {
541-
dvQuery = new IndexSortSortedNumericDocValuesRangeQuery(name(), l, u, dvQuery);
555+
return nowUsed[0]
556+
? new ApproximateScoreQuery(new DateRangeIncludingNowQuery(query), approxQuery)
557+
: new ApproximateScoreQuery(query, approxQuery);
558+
542559
}
543-
return dvQuery;
544-
});
560+
);
545561
}
546562

547563
public static Query dateRangeQuery(
@@ -553,44 +569,31 @@ public static Query dateRangeQuery(
553569
DateMathParser parser,
554570
QueryShardContext context,
555571
Resolution resolution,
556-
BiFunction<Long, Long, Query> builder
572+
TriFunction<Long, Long, boolean[], Query> builder
557573
) {
558-
return handleNow(context, nowSupplier -> {
559-
long l, u;
560-
if (lowerTerm == null) {
561-
l = Long.MIN_VALUE;
562-
} else {
563-
l = parseToLong(lowerTerm, !includeLower, timeZone, parser, nowSupplier, resolution);
564-
if (includeLower == false) {
565-
++l;
566-
}
567-
}
568-
if (upperTerm == null) {
569-
u = Long.MAX_VALUE;
570-
} else {
571-
u = parseToLong(upperTerm, includeUpper, timeZone, parser, nowSupplier, resolution);
572-
if (includeUpper == false) {
573-
--u;
574-
}
575-
}
576-
return builder.apply(l, u);
577-
});
578-
}
579-
580-
/**
581-
* Handle {@code now} in queries.
582-
* @param context context from which to read the current time
583-
* @param builder build the query
584-
* @return the result of the builder, wrapped in {@link DateRangeIncludingNowQuery} if {@code now} was used.
585-
*/
586-
public static Query handleNow(QueryShardContext context, Function<LongSupplier, Query> builder) {
587574
boolean[] nowUsed = new boolean[1];
588575
LongSupplier nowSupplier = () -> {
589576
nowUsed[0] = true;
590577
return context.nowInMillis();
591578
};
592-
Query query = builder.apply(nowSupplier);
593-
return nowUsed[0] ? new DateRangeIncludingNowQuery(query) : query;
579+
long l, u;
580+
if (lowerTerm == null) {
581+
l = Long.MIN_VALUE;
582+
} else {
583+
l = parseToLong(lowerTerm, !includeLower, timeZone, parser, nowSupplier, resolution);
584+
if (includeLower == false) {
585+
++l;
586+
}
587+
}
588+
if (upperTerm == null) {
589+
u = Long.MAX_VALUE;
590+
} else {
591+
u = parseToLong(upperTerm, includeUpper, timeZone, parser, nowSupplier, resolution);
592+
if (includeUpper == false) {
593+
--u;
594+
}
595+
}
596+
return builder.apply(l, u, nowUsed);
594597
}
595598

596599
public long parseToLong(Object value, boolean roundUp, @Nullable ZoneId zone, DateMathParser dateParser, LongSupplier now) {

server/src/test/java/org/opensearch/index/mapper/DateFieldTypeTests.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -308,19 +308,20 @@ public void testRangeQuery() throws IOException {
308308

309309
instant1 = nowInMillis;
310310
instant2 = instant1 + 100;
311-
expected = new DateRangeIncludingNowQuery(
312-
new ApproximateScoreQuery(
311+
expected = new ApproximateScoreQuery(
312+
new DateRangeIncludingNowQuery(
313313
new IndexOrDocValuesQuery(
314314
LongPoint.newRangeQuery("field", instant1, instant2),
315315
SortedNumericDocValuesField.newSlowRangeQuery("field", instant1, instant2)
316-
),
317-
new ApproximatePointRangeQuery(
318-
"field",
319-
pack(new long[] { instant1 }).bytes,
320-
pack(new long[] { instant2 }).bytes,
321-
new long[] { instant1 }.length,
322-
ApproximatePointRangeQuery.LONG_FORMAT
323316
)
317+
),
318+
new ApproximatePointRangeQuery(
319+
"field",
320+
pack(new long[] { instant1 }).bytes,
321+
pack(new long[] { instant2 }).bytes,
322+
new long[] { instant1 }.length,
323+
ApproximatePointRangeQuery.LONG_FORMAT
324+
324325
)
325326
);
326327
assertEquals(expected, ft.rangeQuery("now", instant2, true, true, null, null, null, context));

server/src/test/java/org/opensearch/index/query/RangeQueryBuilderTests.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -447,14 +447,22 @@ public void testDateRangeQueryTimezone() throws IOException {
447447
+ " }\n"
448448
+ " }\n"
449449
+ "}";
450+
451+
// TODO what else can we assert
450452
QueryShardContext context = createShardContext();
451453
Query parsedQuery = parseQuery(query).toQuery(context);
452-
assertThat(parsedQuery, instanceOf(DateRangeIncludingNowQuery.class));
453-
parsedQuery = ((DateRangeIncludingNowQuery) parsedQuery).getQuery();
454+
454455
assertThat(parsedQuery, instanceOf(ApproximateScoreQuery.class));
455-
parsedQuery = ((ApproximateScoreQuery) parsedQuery).getApproximationQuery();
456-
assertThat(parsedQuery, instanceOf(ApproximateQuery.class));
457-
// TODO what else can we assert
456+
457+
// Get the exact query from ApproximateScoreQuery (which should be DateRangeIncludingNowQuery)
458+
ApproximateScoreQuery approximateScoreQuery = (ApproximateScoreQuery) parsedQuery;
459+
Query exactQuery = approximateScoreQuery.getOriginalQuery();
460+
461+
// The exact query should be DateRangeIncludingNowQuery
462+
assertThat(exactQuery, instanceOf(DateRangeIncludingNowQuery.class));
463+
464+
ApproximateQuery approximationQuery = approximateScoreQuery.getApproximationQuery();
465+
assertThat(approximationQuery, instanceOf(ApproximatePointRangeQuery.class));
458466

459467
query = "{\n"
460468
+ " \"range\" : {\n"

0 commit comments

Comments
 (0)