mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-25 09:28:27 +00:00
Ignore date ranges containing 'now' when pre-processing a percolator query (#35160)
Today when a percolator query contains a date range then the query analyzer extracts that range, so that at search time the `percolate` query can exclude percolator queries efficiently that are never going to match. The problem is that if 'now' is used it is evaluated at index time. So the idea is to rewrite date ranges with 'now' to a match all query, so that the query analyzer can't extract it and the `percolate` query is then able to evaluate 'now' at query time.
This commit is contained in:
parent
0cc0fd2d15
commit
8de3c6e618
@ -409,7 +409,15 @@ public class PercolatorFieldMapper extends FieldMapper {
|
||||
|
||||
Version indexVersion = context.mapperService().getIndexSettings().getIndexVersionCreated();
|
||||
createQueryBuilderField(indexVersion, queryBuilderField, queryBuilder, context);
|
||||
Query query = toQuery(queryShardContext, isMapUnmappedFieldAsText(), queryBuilder);
|
||||
|
||||
QueryBuilder queryBuilderForProcessing = queryBuilder.rewrite(new QueryShardContext(queryShardContext) {
|
||||
|
||||
@Override
|
||||
public boolean convertNowRangeToMatchAll() {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
Query query = toQuery(queryShardContext, isMapUnmappedFieldAsText(), queryBuilderForProcessing);
|
||||
processQuery(query, context);
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.elasticsearch.percolator;
|
||||
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.join.ScoreMode;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.support.WriteRequest;
|
||||
@ -26,10 +28,14 @@ import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.IndexService;
|
||||
import org.elasticsearch.index.cache.bitset.BitsetFilterCache;
|
||||
import org.elasticsearch.index.engine.Engine;
|
||||
import org.elasticsearch.index.fielddata.ScriptDocValues;
|
||||
import org.elasticsearch.index.query.Operator;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.MockScriptPlugin;
|
||||
import org.elasticsearch.script.Script;
|
||||
@ -47,9 +53,14 @@ import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.scriptQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class PercolatorQuerySearchTests extends ESSingleNodeTestCase {
|
||||
|
||||
@ -221,4 +232,53 @@ public class PercolatorQuerySearchTests extends ESSingleNodeTestCase {
|
||||
assertSearchHits(response, "1");
|
||||
}
|
||||
|
||||
public void testRangeQueriesWithNow() throws Exception {
|
||||
IndexService indexService = createIndex("test", Settings.builder().put("index.number_of_shards", 1).build(), "_doc",
|
||||
"field1", "type=keyword", "field2", "type=date", "query", "type=percolator");
|
||||
|
||||
client().prepareIndex("test", "_doc", "1")
|
||||
.setSource(jsonBuilder().startObject().field("query", rangeQuery("field2").from("now-1h").to("now+1h")).endObject())
|
||||
.get();
|
||||
client().prepareIndex("test", "_doc", "2")
|
||||
.setSource(jsonBuilder().startObject().field("query", boolQuery()
|
||||
.filter(termQuery("field1", "value"))
|
||||
.filter(rangeQuery("field2").from("now-1h").to("now+1h"))
|
||||
).endObject())
|
||||
.get();
|
||||
|
||||
|
||||
Script script = new Script(ScriptType.INLINE, MockScriptPlugin.NAME, "1==1", Collections.emptyMap());
|
||||
client().prepareIndex("test", "_doc", "3")
|
||||
.setSource(jsonBuilder().startObject().field("query", boolQuery()
|
||||
.filter(scriptQuery(script))
|
||||
.filter(rangeQuery("field2").from("now-1h").to("now+1h"))
|
||||
).endObject())
|
||||
.get();
|
||||
client().admin().indices().prepareRefresh().get();
|
||||
|
||||
try (Engine.Searcher engineSearcher = indexService.getShard(0).acquireSearcher("test")) {
|
||||
IndexSearcher indexSearcher = engineSearcher.searcher();
|
||||
long[] currentTime = new long[] {System.currentTimeMillis()};
|
||||
QueryShardContext queryShardContext =
|
||||
indexService.newQueryShardContext(0, engineSearcher.reader(), () -> currentTime[0], null);
|
||||
|
||||
BytesReference source = BytesReference.bytes(jsonBuilder().startObject()
|
||||
.field("field1", "value")
|
||||
.field("field2", currentTime[0])
|
||||
.endObject());
|
||||
QueryBuilder queryBuilder = new PercolateQueryBuilder("query", source, XContentType.JSON);
|
||||
Query query = queryBuilder.toQuery(queryShardContext);
|
||||
assertThat(indexSearcher.count(query), equalTo(3));
|
||||
|
||||
currentTime[0] = currentTime[0] + 10800000; // + 3 hours
|
||||
source = BytesReference.bytes(jsonBuilder().startObject()
|
||||
.field("field1", "value")
|
||||
.field("field2", currentTime[0])
|
||||
.endObject());
|
||||
queryBuilder = new PercolateQueryBuilder("query", source, XContentType.JSON);
|
||||
query = queryBuilder.toQuery(queryShardContext);
|
||||
assertThat(indexSearcher.count(query), equalTo(3));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -124,4 +124,15 @@ public class QueryRewriteContext {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In pre-processing contexts that happen at index time 'now' date ranges should be replaced by a {@link MatchAllQueryBuilder}.
|
||||
* Otherwise documents that should match at query time would never match and the document that have fallen outside the
|
||||
* date range would continue to match.
|
||||
*
|
||||
* @return indicates whether range queries with date ranges using 'now' are rewritten to a {@link MatchAllQueryBuilder}.
|
||||
*/
|
||||
public boolean convertNowRangeToMatchAll() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -459,6 +459,16 @@ public class RangeQueryBuilder extends AbstractQueryBuilder<RangeQueryBuilder> i
|
||||
|
||||
@Override
|
||||
protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
|
||||
// Percolator queries get rewritten and pre-processed at index time.
|
||||
// If a range query has a date range using 'now' and 'now' gets resolved at index time then
|
||||
// the pre-processing uses that to pre-process. This can then lead to mismatches at query time.
|
||||
if (queryRewriteContext.convertNowRangeToMatchAll()) {
|
||||
if ((from() != null && from().toString().contains("now")) ||
|
||||
(to() != null && to().toString().contains("now"))) {
|
||||
return new MatchAllQueryBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
final MappedFieldType.Relation relation = getRelation(queryRewriteContext);
|
||||
switch (relation) {
|
||||
case DISJOINT:
|
||||
|
@ -563,4 +563,33 @@ public class RangeQueryBuilderTests extends AbstractQueryTestCase<RangeQueryBuil
|
||||
builder.relation("intersects");
|
||||
assertEquals(ShapeRelation.INTERSECTS, builder.relation());
|
||||
}
|
||||
|
||||
public void testConvertNowRangeToMatchAll() throws IOException {
|
||||
RangeQueryBuilder query = new RangeQueryBuilder(DATE_FIELD_NAME);
|
||||
DateTime queryFromValue = new DateTime(2019, 1, 1, 0, 0, 0, ISOChronology.getInstanceUTC());
|
||||
DateTime queryToValue = new DateTime(2020, 1, 1, 0, 0, 0, ISOChronology.getInstanceUTC());
|
||||
if (randomBoolean()) {
|
||||
query.from("now");
|
||||
query.to(queryToValue);
|
||||
} else if (randomBoolean()) {
|
||||
query.from(queryFromValue);
|
||||
query.to("now");
|
||||
} else {
|
||||
query.from("now");
|
||||
query.to("now+1h");
|
||||
}
|
||||
QueryShardContext queryShardContext = createShardContext();
|
||||
QueryBuilder rewritten = query.rewrite(queryShardContext);
|
||||
assertThat(rewritten, instanceOf(RangeQueryBuilder.class));
|
||||
|
||||
queryShardContext = new QueryShardContext(queryShardContext) {
|
||||
|
||||
@Override
|
||||
public boolean convertNowRangeToMatchAll() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
rewritten = query.rewrite(queryShardContext);
|
||||
assertThat(rewritten, instanceOf(MatchAllQueryBuilder.class));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user