From 06a825b13c838bf6217efd84e1f49910e2394ca9 Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Wed, 26 Sep 2018 10:57:36 +0200 Subject: [PATCH] Handle terms query when detecting if a query can match nested docs 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 --- .../index/search/NestedHelper.java | 22 ++++++++++++- .../index/search/NestedHelperTests.java | 31 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/index/search/NestedHelper.java b/server/src/main/java/org/elasticsearch/index/search/NestedHelper.java index f5a5c8143bcb0..1c17fa0cb935f 100644 --- a/server/src/main/java/org/elasticsearch/index/search/NestedHelper.java +++ b/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.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 boolean mightMatchNestedDocs(Query query) { } 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 boolean mightMatchNonNestedDocs(Query query, String nestedPath) { 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) { diff --git a/server/src/test/java/org/elasticsearch/index/search/NestedHelperTests.java b/server/src/test/java/org/elasticsearch/index/search/NestedHelperTests.java index e781a3311b383..40a8251fb24ef 100644 --- a/server/src/test/java/org/elasticsearch/index/search/NestedHelperTests.java +++ b/server/src/test/java/org/elasticsearch/index/search/NestedHelperTests.java @@ -40,6 +40,7 @@ import org.elasticsearch.test.ESSingleNodeTestCase; import java.io.IOException; +import java.util.Collections; public class NestedHelperTests extends ESSingleNodeTestCase { @@ -115,6 +116,36 @@ public void testMatchNo() { 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));