Browse Source

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
Jim Ferenczi 7 years ago
parent
commit
aba4a59d0d

+ 21 - 1
server/src/main/java/org/elasticsearch/index/search/NestedHelper.java

@@ -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) {

+ 31 - 0
server/src/test/java/org/elasticsearch/index/search/NestedHelperTests.java

@@ -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));