Handle terms query when detecting if a query can match nested docs (#34072)

When nested objects are present in the mappings, we add a filter in
queries to exclude them if there is no evidence that the query cannot
match in this space. In 6x we visit the query in order to find a mandatory
clause that can match root documents only. If we find one we can omit the
nested documents filter. Currently only `term` and `range` queries are checked,
this change adds the support for `terms` query to effectively remove the nested filter
if a mandatory `terms` clause targets a non-nested field.

Closes #34067
This commit is contained in:
Jim Ferenczi 2018-10-02 09:30:23 +02:00 committed by GitHub
parent 2aff005a69
commit aba4a59d0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 1 deletions

View File

@ -19,6 +19,7 @@
package org.elasticsearch.index.search;
import org.apache.lucene.index.PrefixCodedTerms;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.BoostQuery;
@ -28,6 +29,7 @@ import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.PointRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermInSetQuery;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.BooleanClause.Occur;
import org.elasticsearch.index.mapper.MapperService;
@ -54,9 +56,18 @@ public final class NestedHelper {
} else if (query instanceof MatchNoDocsQuery) {
return false;
} else if (query instanceof TermQuery) {
// We only handle term queries and range queries, which should already
// We only handle term(s) queries and range queries, which should already
// cover a high majority of use-cases
return mightMatchNestedDocs(((TermQuery) query).getTerm().field());
} else if (query instanceof TermInSetQuery) {
PrefixCodedTerms terms = ((TermInSetQuery) query).getTermData();
if (terms.size() > 0) {
PrefixCodedTerms.TermIterator it = terms.iterator();
it.next();
return mightMatchNestedDocs(it.field());
} else {
return false;
}
} else if (query instanceof PointRangeQuery) {
return mightMatchNestedDocs(((PointRangeQuery) query).getField());
} else if (query instanceof IndexOrDocValuesQuery) {
@ -118,6 +129,15 @@ public final class NestedHelper {
return false;
} else if (query instanceof TermQuery) {
return mightMatchNonNestedDocs(((TermQuery) query).getTerm().field(), nestedPath);
} else if (query instanceof TermInSetQuery) {
PrefixCodedTerms terms = ((TermInSetQuery) query).getTermData();
if (terms.size() > 0) {
PrefixCodedTerms.TermIterator it = terms.iterator();
it.next();
return mightMatchNonNestedDocs(it.field(), nestedPath);
} else {
return false;
}
} else if (query instanceof PointRangeQuery) {
return mightMatchNonNestedDocs(((PointRangeQuery) query).getField(), nestedPath);
} else if (query instanceof IndexOrDocValuesQuery) {

View File

@ -40,6 +40,7 @@ import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.test.ESSingleNodeTestCase;
import java.io.IOException;
import java.util.Collections;
public class NestedHelperTests extends ESSingleNodeTestCase {
@ -115,6 +116,36 @@ public class NestedHelperTests extends ESSingleNodeTestCase {
assertFalse(new NestedHelper(mapperService).mightMatchNonNestedDocs(new MatchNoDocsQuery(), "nested_missing"));
}
public void testTermsQuery() {
Query termsQuery = mapperService.fullName("foo").termsQuery(Collections.singletonList("bar"), null);
assertFalse(new NestedHelper(mapperService).mightMatchNestedDocs(termsQuery));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested1"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested2"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested3"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested_missing"));
termsQuery = mapperService.fullName("nested1.foo").termsQuery(Collections.singletonList("bar"), null);
assertTrue(new NestedHelper(mapperService).mightMatchNestedDocs(termsQuery));
assertFalse(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested1"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested2"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested3"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested_missing"));
termsQuery = mapperService.fullName("nested2.foo").termsQuery(Collections.singletonList("bar"), null);
assertTrue(new NestedHelper(mapperService).mightMatchNestedDocs(termsQuery));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested1"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested2"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested3"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested_missing"));
termsQuery = mapperService.fullName("nested3.foo").termsQuery(Collections.singletonList("bar"), null);
assertTrue(new NestedHelper(mapperService).mightMatchNestedDocs(termsQuery));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested1"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested2"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested3"));
assertTrue(new NestedHelper(mapperService).mightMatchNonNestedDocs(termsQuery, "nested_missing"));
}
public void testTermQuery() {
Query termQuery = mapperService.fullName("foo").termQuery("bar", null);
assertFalse(new NestedHelper(mapperService).mightMatchNestedDocs(termQuery));