diff --git a/core/src/main/java/org/elasticsearch/index/query/HasChildQueryParser.java b/core/src/main/java/org/elasticsearch/index/query/HasChildQueryParser.java index c14c6c74300..b9609821e4b 100644 --- a/core/src/main/java/org/elasticsearch/index/query/HasChildQueryParser.java +++ b/core/src/main/java/org/elasticsearch/index/query/HasChildQueryParser.java @@ -151,7 +151,8 @@ public class HasChildQueryParser implements QueryParser { } if (innerHits != null) { - InnerHitsContext.ParentChildInnerHits parentChildInnerHits = new InnerHitsContext.ParentChildInnerHits(innerHits.v2(), innerQuery, null, parseContext.mapperService(), childDocMapper); + ParsedQuery parsedQuery = new ParsedQuery(innerQuery, parseContext.copyNamedQueries()); + InnerHitsContext.ParentChildInnerHits parentChildInnerHits = new InnerHitsContext.ParentChildInnerHits(innerHits.v2(), parsedQuery, null, parseContext.mapperService(), childDocMapper); String name = innerHits.v1() != null ? innerHits.v1() : childType; parseContext.addInnerHits(name, parentChildInnerHits); } diff --git a/core/src/main/java/org/elasticsearch/index/query/HasParentQueryParser.java b/core/src/main/java/org/elasticsearch/index/query/HasParentQueryParser.java index 057a2fe8840..fecaba45e5a 100644 --- a/core/src/main/java/org/elasticsearch/index/query/HasParentQueryParser.java +++ b/core/src/main/java/org/elasticsearch/index/query/HasParentQueryParser.java @@ -154,7 +154,8 @@ public class HasParentQueryParser implements QueryParser { } if (innerHits != null) { - InnerHitsContext.ParentChildInnerHits parentChildInnerHits = new InnerHitsContext.ParentChildInnerHits(innerHits.v2(), innerQuery, null, parseContext.mapperService(), parentDocMapper); + ParsedQuery parsedQuery = new ParsedQuery(innerQuery, parseContext.copyNamedQueries()); + InnerHitsContext.ParentChildInnerHits parentChildInnerHits = new InnerHitsContext.ParentChildInnerHits(innerHits.v2(), parsedQuery, null, parseContext.mapperService(), parentDocMapper); String name = innerHits.v1() != null ? innerHits.v1() : parentType; parseContext.addInnerHits(name, parentChildInnerHits); } diff --git a/core/src/main/java/org/elasticsearch/index/query/IndexQueryParserService.java b/core/src/main/java/org/elasticsearch/index/query/IndexQueryParserService.java index 0d1a0e871ee..810504aefd7 100644 --- a/core/src/main/java/org/elasticsearch/index/query/IndexQueryParserService.java +++ b/core/src/main/java/org/elasticsearch/index/query/IndexQueryParserService.java @@ -217,7 +217,7 @@ public class IndexQueryParserService extends AbstractIndexComponent { if (filter == null) { return null; } - return new ParsedQuery(filter, context.copyNamedFilters()); + return new ParsedQuery(filter, context.copyNamedQueries()); } finally { context.reset(null); } @@ -300,7 +300,7 @@ public class IndexQueryParserService extends AbstractIndexComponent { if (query == null) { query = Queries.newMatchNoDocsQuery(); } - return new ParsedQuery(query, parseContext.copyNamedFilters()); + return new ParsedQuery(query, parseContext.copyNamedQueries()); } finally { parseContext.reset(null); } diff --git a/core/src/main/java/org/elasticsearch/index/query/NestedQueryParser.java b/core/src/main/java/org/elasticsearch/index/query/NestedQueryParser.java index 78c7817224c..4dc71f93393 100644 --- a/core/src/main/java/org/elasticsearch/index/query/NestedQueryParser.java +++ b/core/src/main/java/org/elasticsearch/index/query/NestedQueryParser.java @@ -151,7 +151,8 @@ public class NestedQueryParser implements QueryParser { } if (innerHits != null) { - InnerHitsContext.NestedInnerHits nestedInnerHits = new InnerHitsContext.NestedInnerHits(innerHits.v2(), innerQuery, null, getParentObjectMapper(), nestedObjectMapper); + ParsedQuery parsedQuery = new ParsedQuery(innerQuery, parseContext.copyNamedQueries()); + InnerHitsContext.NestedInnerHits nestedInnerHits = new InnerHitsContext.NestedInnerHits(innerHits.v2(), parsedQuery, null, getParentObjectMapper(), nestedObjectMapper); String name = innerHits.v1() != null ? innerHits.v1() : path; parseContext.addInnerHits(name, nestedInnerHits); } diff --git a/core/src/main/java/org/elasticsearch/index/query/QueryParseContext.java b/core/src/main/java/org/elasticsearch/index/query/QueryParseContext.java index bfaf9c3f243..7f4ab98e3a7 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryParseContext.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryParseContext.java @@ -185,11 +185,11 @@ public class QueryParseContext { namedQueries.put(name, query); } - public ImmutableMap copyNamedFilters() { + public ImmutableMap copyNamedQueries() { return ImmutableMap.copyOf(namedQueries); } - public void combineNamedFilters(QueryParseContext context) { + public void combineNamedQueries(QueryParseContext context) { namedQueries.putAll(context.namedQueries); } diff --git a/core/src/main/java/org/elasticsearch/index/query/WrapperQueryParser.java b/core/src/main/java/org/elasticsearch/index/query/WrapperQueryParser.java index f7b98ad3dd5..331ba786b51 100644 --- a/core/src/main/java/org/elasticsearch/index/query/WrapperQueryParser.java +++ b/core/src/main/java/org/elasticsearch/index/query/WrapperQueryParser.java @@ -62,7 +62,7 @@ public class WrapperQueryParser implements QueryParser { context.reset(qSourceParser); Query result = context.parseInnerQuery(); parser.nextToken(); - parseContext.combineNamedFilters(context); + parseContext.combineNamedQueries(context); return result; } } diff --git a/core/src/main/java/org/elasticsearch/search/fetch/innerhits/InnerHitsContext.java b/core/src/main/java/org/elasticsearch/search/fetch/innerhits/InnerHitsContext.java index 12365e9a78a..b639a387238 100644 --- a/core/src/main/java/org/elasticsearch/search/fetch/innerhits/InnerHitsContext.java +++ b/core/src/main/java/org/elasticsearch/search/fetch/innerhits/InnerHitsContext.java @@ -19,34 +19,17 @@ package org.elasticsearch.search.fetch.innerhits; -import com.google.common.collect.ImmutableMap; - import org.apache.lucene.index.LeafReader; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.Term; import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.ConstantScoreScorer; -import org.apache.lucene.search.ConstantScoreWeight; -import org.apache.lucene.search.DocIdSet; -import org.apache.lucene.search.DocIdSetIterator; -import org.apache.lucene.search.Filter; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.TopDocs; -import org.apache.lucene.search.TopDocsCollector; -import org.apache.lucene.search.TopFieldCollector; -import org.apache.lucene.search.TopScoreDocCollector; -import org.apache.lucene.search.Weight; +import org.apache.lucene.search.*; import org.apache.lucene.search.join.BitDocIdSetFilter; import org.apache.lucene.util.BitSet; import org.apache.lucene.util.Bits; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.lucene.search.Queries; -import org.elasticsearch.index.fieldvisitor.SingleFieldsVisitor; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.Uid; @@ -83,10 +66,10 @@ public final class InnerHitsContext { public static abstract class BaseInnerHits extends FilteredSearchContext { - protected final Query query; + protected final ParsedQuery query; private final InnerHitsContext childInnerHits; - protected BaseInnerHits(SearchContext context, Query query, Map childInnerHits) { + protected BaseInnerHits(SearchContext context, ParsedQuery query, Map childInnerHits) { super(context); this.query = query; if (childInnerHits != null && !childInnerHits.isEmpty()) { @@ -98,12 +81,12 @@ public final class InnerHitsContext { @Override public Query query() { - return query; + return query.query(); } @Override public ParsedQuery parsedQuery() { - return new ParsedQuery(query, ImmutableMap.of()); + return query; } public abstract TopDocs topDocs(SearchContext context, FetchSubPhase.HitContext hitContext) throws IOException; @@ -120,7 +103,7 @@ public final class InnerHitsContext { private final ObjectMapper parentObjectMapper; private final ObjectMapper childObjectMapper; - public NestedInnerHits(SearchContext context, Query query, Map childInnerHits, ObjectMapper parentObjectMapper, ObjectMapper childObjectMapper) { + public NestedInnerHits(SearchContext context, ParsedQuery query, Map childInnerHits, ObjectMapper parentObjectMapper, ObjectMapper childObjectMapper) { super(context, query, childInnerHits); this.parentObjectMapper = parentObjectMapper; this.childObjectMapper = childObjectMapper; @@ -136,7 +119,7 @@ public final class InnerHitsContext { } BitDocIdSetFilter parentFilter = context.bitsetFilterCache().getBitDocIdSetFilter(rawParentFilter); Filter childFilter = childObjectMapper.nestedTypeFilter(); - Query q = Queries.filtered(query, new NestedChildrenQuery(parentFilter, childFilter, hitContext)); + Query q = Queries.filtered(query.query(), new NestedChildrenQuery(parentFilter, childFilter, hitContext)); if (size() == 0) { return new TopDocs(context.searcher().count(q), Lucene.EMPTY_SCORE_DOCS, 0); @@ -280,7 +263,7 @@ public final class InnerHitsContext { private final MapperService mapperService; private final DocumentMapper documentMapper; - public ParentChildInnerHits(SearchContext context, Query query, Map childInnerHits, MapperService mapperService, DocumentMapper documentMapper) { + public ParentChildInnerHits(SearchContext context, ParsedQuery query, Map childInnerHits, MapperService mapperService, DocumentMapper documentMapper) { super(context, query, childInnerHits); this.mapperService = mapperService; this.documentMapper = documentMapper; @@ -307,7 +290,7 @@ public final class InnerHitsContext { } BooleanQuery q = new BooleanQuery(); - q.add(query, Occur.MUST); + q.add(query.query(), Occur.MUST); // Only include docs that have the current hit as parent q.add(new TermQuery(new Term(field, term)), Occur.MUST); // Only include docs that have this inner hits type diff --git a/core/src/main/java/org/elasticsearch/search/fetch/innerhits/InnerHitsParseElement.java b/core/src/main/java/org/elasticsearch/search/fetch/innerhits/InnerHitsParseElement.java index 8fe2fba2e0f..c02e2c6d8ed 100644 --- a/core/src/main/java/org/elasticsearch/search/fetch/innerhits/InnerHitsParseElement.java +++ b/core/src/main/java/org/elasticsearch/search/fetch/innerhits/InnerHitsParseElement.java @@ -19,12 +19,11 @@ package org.elasticsearch.search.fetch.innerhits; -import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.mapper.DocumentMapper; -import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.object.ObjectMapper; +import org.elasticsearch.index.query.ParsedQuery; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.search.SearchParseElement; import org.elasticsearch.search.fetch.fielddata.FieldDataFieldsParseElement; @@ -169,7 +168,7 @@ public class InnerHitsParseElement implements SearchParseElement { } private ParseResult parseSubSearchContext(SearchContext searchContext, QueryParseContext parseContext, XContentParser parser) throws Exception { - Query query = null; + ParsedQuery query = null; Map childInnerHits = null; SubSearchContext subSearchContext = new SubSearchContext(searchContext); String fieldName = null; @@ -179,7 +178,8 @@ public class InnerHitsParseElement implements SearchParseElement { fieldName = parser.currentName(); } else if (token == XContentParser.Token.START_OBJECT) { if ("query".equals(fieldName)) { - query = searchContext.queryParserService().parseInnerQuery(parseContext); + Query q = searchContext.queryParserService().parseInnerQuery(parseContext); + query = new ParsedQuery(q, parseContext.copyNamedQueries()); } else if ("inner_hits".equals(fieldName)) { childInnerHits = parseInnerHits(parser, parseContext, searchContext); } else { @@ -191,7 +191,7 @@ public class InnerHitsParseElement implements SearchParseElement { } if (query == null) { - query = new MatchAllDocsQuery(); + query = ParsedQuery.parsedMatchAllQuery(); } return new ParseResult(subSearchContext, query, childInnerHits); } @@ -199,10 +199,10 @@ public class InnerHitsParseElement implements SearchParseElement { private static final class ParseResult { private final SubSearchContext context; - private final Query query; + private final ParsedQuery query; private final Map childInnerHits; - private ParseResult(SubSearchContext context, Query query, Map childInnerHits) { + private ParseResult(SubSearchContext context, ParsedQuery query, Map childInnerHits) { this.context = context; this.query = query; this.childInnerHits = childInnerHits; @@ -212,7 +212,7 @@ public class InnerHitsParseElement implements SearchParseElement { return context; } - public Query query() { + public ParsedQuery query() { return query; } diff --git a/core/src/test/java/org/elasticsearch/nested/SimpleNestedTests.java b/core/src/test/java/org/elasticsearch/nested/SimpleNestedTests.java index 06fac33e672..f0cd668e431 100644 --- a/core/src/test/java/org/elasticsearch/nested/SimpleNestedTests.java +++ b/core/src/test/java/org/elasticsearch/nested/SimpleNestedTests.java @@ -34,6 +34,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.query.support.QueryInnerHitBuilder; import org.elasticsearch.script.Script; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; @@ -46,25 +47,9 @@ import java.util.List; import static org.elasticsearch.common.settings.Settings.settingsBuilder; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; -import static org.elasticsearch.index.query.QueryBuilders.boolQuery; -import static org.elasticsearch.index.query.QueryBuilders.filteredQuery; -import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; -import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; -import static org.elasticsearch.index.query.QueryBuilders.rangeQuery; -import static org.elasticsearch.index.query.QueryBuilders.termQuery; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAllSuccessful; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.hamcrest.Matchers.arrayContaining; -import static org.hamcrest.Matchers.arrayContainingInAnyOrder; -import static org.hamcrest.Matchers.arrayWithSize; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.startsWith; +import static org.elasticsearch.index.query.QueryBuilders.*; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.*; +import static org.hamcrest.Matchers.*; public class SimpleNestedTests extends ElasticsearchIntegrationTest { @@ -178,98 +163,6 @@ public class SimpleNestedTests extends ElasticsearchIntegrationTest { assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); } - @Test @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/10661") - public void simpleNestedMatchQueries() throws Exception { - XContentBuilder builder = jsonBuilder().startObject() - .startObject("type1") - .startObject("properties") - .startObject("nested1") - .field("type", "nested") - .endObject() - .startObject("field1") - .field("type", "long") - .endObject() - .endObject() - .endObject() - .endObject(); - assertAcked(prepareCreate("test").addMapping("type1", builder)); - ensureGreen(); - - List requests = new ArrayList<>(); - int numDocs = randomIntBetween(2, 35); - requests.add(client().prepareIndex("test", "type1", "0").setSource(jsonBuilder().startObject() - .field("field1", 0) - .startArray("nested1") - .startObject() - .field("n_field1", "n_value1_1") - .field("n_field2", "n_value2_1") - .endObject() - .startObject() - .field("n_field1", "n_value1_2") - .field("n_field2", "n_value2_2") - .endObject() - .endArray() - .endObject())); - requests.add(client().prepareIndex("test", "type1", "1").setSource(jsonBuilder().startObject() - .field("field1", 1) - .startArray("nested1") - .startObject() - .field("n_field1", "n_value1_8") - .field("n_field2", "n_value2_5") - .endObject() - .startObject() - .field("n_field1", "n_value1_3") - .field("n_field2", "n_value2_1") - .endObject() - .endArray() - .endObject())); - - for (int i = 2; i < numDocs; i++) { - requests.add(client().prepareIndex("test", "type1", String.valueOf(i)).setSource(jsonBuilder().startObject() - .field("field1", i) - .startArray("nested1") - .startObject() - .field("n_field1", "n_value1_8") - .field("n_field2", "n_value2_5") - .endObject() - .startObject() - .field("n_field1", "n_value1_2") - .field("n_field2", "n_value2_2") - .endObject() - .endArray() - .endObject())); - } - - indexRandom(true, requests); - waitForRelocation(ClusterHealthStatus.GREEN); - - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(nestedQuery("nested1", boolQuery() - .should(termQuery("nested1.n_field1", "n_value1_1").queryName("test1")) - .should(termQuery("nested1.n_field1", "n_value1_3").queryName("test2")) - .should(termQuery("nested1.n_field2", "n_value2_2").queryName("test3")) - )) - .setSize(numDocs) - .addSort("field1", SortOrder.ASC) - .get(); - assertNoFailures(searchResponse); - assertAllSuccessful(searchResponse); - assertThat(searchResponse.getHits().totalHits(), equalTo((long) numDocs)); - assertThat(searchResponse.getHits().getAt(0).id(), equalTo("0")); - assertThat(searchResponse.getHits().getAt(0).matchedQueries(), arrayWithSize(2)); - assertThat(searchResponse.getHits().getAt(0).matchedQueries(), arrayContainingInAnyOrder("test1", "test3")); - - assertThat(searchResponse.getHits().getAt(1).id(), equalTo("1")); - assertThat(searchResponse.getHits().getAt(1).matchedQueries(), arrayWithSize(1)); - assertThat(searchResponse.getHits().getAt(1).matchedQueries(), arrayContaining("test2")); - - for (int i = 2; i < numDocs; i++) { - assertThat(searchResponse.getHits().getAt(i).id(), equalTo(String.valueOf(i))); - assertThat(searchResponse.getHits().getAt(i).matchedQueries(), arrayWithSize(1)); - assertThat(searchResponse.getHits().getAt(i).matchedQueries(), arrayContaining("test3")); - } - } - @Test public void multiNested() throws Exception { assertAcked(prepareCreate("test") diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TopHitsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TopHitsTests.java index 899123447e9..1d62df80c0d 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TopHitsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/TopHitsTests.java @@ -881,7 +881,6 @@ public class TopHitsTests extends ElasticsearchIntegrationTest { long version = searchHit.version(); assertThat(version, equalTo(1l)); - // Can't use named queries for the same reason explain doesn't work: assertThat(searchHit.matchedQueries(), arrayContaining("test")); SearchHitField field = searchHit.field("comments.user"); diff --git a/core/src/test/java/org/elasticsearch/search/innerhits/InnerHitsTests.java b/core/src/test/java/org/elasticsearch/search/innerhits/InnerHitsTests.java index c9f0e4a85a1..caf1331e362 100644 --- a/core/src/test/java/org/elasticsearch/search/innerhits/InnerHitsTests.java +++ b/core/src/test/java/org/elasticsearch/search/innerhits/InnerHitsTests.java @@ -20,13 +20,13 @@ package org.elasticsearch.search.innerhits; import org.elasticsearch.Version; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.query.BoolQueryBuilder; -import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.support.QueryInnerHitBuilder; import org.elasticsearch.script.Script; import org.elasticsearch.search.SearchHit; @@ -42,21 +42,9 @@ import java.util.List; import java.util.Locale; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; -import static org.elasticsearch.index.query.QueryBuilders.boolQuery; -import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasChildQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasParentQuery; -import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; -import static org.elasticsearch.index.query.QueryBuilders.matchQuery; -import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHit; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.nullValue; +import static org.elasticsearch.index.query.QueryBuilders.*; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.*; +import static org.hamcrest.Matchers.*; /** */ @@ -1004,4 +992,139 @@ public class InnerHitsTests extends ElasticsearchIntegrationTest { assertThat(innerInnerHits.getAt(0).getId(), equalTo("king")); } + @Test + public void matchesQueries_nestedInnerHits() throws Exception { + XContentBuilder builder = jsonBuilder().startObject() + .startObject("type1") + .startObject("properties") + .startObject("nested1") + .field("type", "nested") + .endObject() + .startObject("field1") + .field("type", "long") + .endObject() + .endObject() + .endObject() + .endObject(); + assertAcked(prepareCreate("test").addMapping("type1", builder)); + ensureGreen(); + + List requests = new ArrayList<>(); + int numDocs = randomIntBetween(2, 35); + requests.add(client().prepareIndex("test", "type1", "0").setSource(jsonBuilder().startObject() + .field("field1", 0) + .startArray("nested1") + .startObject() + .field("n_field1", "n_value1_1") + .field("n_field2", "n_value2_1") + .endObject() + .startObject() + .field("n_field1", "n_value1_2") + .field("n_field2", "n_value2_2") + .endObject() + .endArray() + .endObject())); + requests.add(client().prepareIndex("test", "type1", "1").setSource(jsonBuilder().startObject() + .field("field1", 1) + .startArray("nested1") + .startObject() + .field("n_field1", "n_value1_8") + .field("n_field2", "n_value2_5") + .endObject() + .startObject() + .field("n_field1", "n_value1_3") + .field("n_field2", "n_value2_1") + .endObject() + .endArray() + .endObject())); + + for (int i = 2; i < numDocs; i++) { + requests.add(client().prepareIndex("test", "type1", String.valueOf(i)).setSource(jsonBuilder().startObject() + .field("field1", i) + .startArray("nested1") + .startObject() + .field("n_field1", "n_value1_8") + .field("n_field2", "n_value2_5") + .endObject() + .startObject() + .field("n_field1", "n_value1_2") + .field("n_field2", "n_value2_2") + .endObject() + .endArray() + .endObject())); + } + + indexRandom(true, requests); + waitForRelocation(ClusterHealthStatus.GREEN); + + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(nestedQuery("nested1", boolQuery() + .should(termQuery("nested1.n_field1", "n_value1_1").queryName("test1")) + .should(termQuery("nested1.n_field1", "n_value1_3").queryName("test2")) + .should(termQuery("nested1.n_field2", "n_value2_2").queryName("test3")) + ).innerHit(new QueryInnerHitBuilder())) + .setSize(numDocs) + .addSort("field1", SortOrder.ASC) + .get(); + assertNoFailures(searchResponse); + assertAllSuccessful(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo((long) numDocs)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("0")); + assertThat(searchResponse.getHits().getAt(0).getInnerHits().get("nested1").getTotalHits(), equalTo(2l)); + assertThat(searchResponse.getHits().getAt(0).getInnerHits().get("nested1").getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(searchResponse.getHits().getAt(0).getInnerHits().get("nested1").getAt(0).getMatchedQueries()[0], equalTo("test1")); + assertThat(searchResponse.getHits().getAt(0).getInnerHits().get("nested1").getAt(1).getMatchedQueries().length, equalTo(1)); + assertThat(searchResponse.getHits().getAt(0).getInnerHits().get("nested1").getAt(1).getMatchedQueries()[0], equalTo("test3")); + + + assertThat(searchResponse.getHits().getAt(1).id(), equalTo("1")); + assertThat(searchResponse.getHits().getAt(1).getInnerHits().get("nested1").getTotalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(1).getInnerHits().get("nested1").getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(searchResponse.getHits().getAt(1).getInnerHits().get("nested1").getAt(0).getMatchedQueries()[0], equalTo("test2")); + + for (int i = 2; i < numDocs; i++) { + assertThat(searchResponse.getHits().getAt(i).id(), equalTo(String.valueOf(i))); + assertThat(searchResponse.getHits().getAt(i).getInnerHits().get("nested1").getTotalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(i).getInnerHits().get("nested1").getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(searchResponse.getHits().getAt(i).getInnerHits().get("nested1").getAt(0).getMatchedQueries()[0], equalTo("test3")); + } + } + + @Test + public void matchesQueries_parentChildInnerHits() throws Exception { + assertAcked(prepareCreate("index").addMapping("child", "_parent", "type=parent")); + List requests = new ArrayList<>(); + requests.add(client().prepareIndex("index", "parent", "1").setSource("{}")); + requests.add(client().prepareIndex("index", "child", "1").setParent("1").setSource("field", "value1")); + requests.add(client().prepareIndex("index", "child", "2").setParent("1").setSource("field", "value2")); + requests.add(client().prepareIndex("index", "parent", "2").setSource("{}")); + requests.add(client().prepareIndex("index", "child", "3").setParent("2").setSource("field", "value1")); + indexRandom(true, requests); + + SearchResponse response = client().prepareSearch("index") + .setQuery(hasChildQuery("child", matchQuery("field", "value1").queryName("_name1")).innerHit(new QueryInnerHitBuilder())) + .addSort("_uid", SortOrder.ASC) + .get(); + assertHitCount(response, 2); + assertThat(response.getHits().getAt(0).id(), equalTo("1")); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits(), equalTo(1l)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name1")); + + assertThat(response.getHits().getAt(1).id(), equalTo("2")); + assertThat(response.getHits().getAt(1).getInnerHits().get("child").getTotalHits(), equalTo(1l)); + assertThat(response.getHits().getAt(1).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(1).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name1")); + + response = client().prepareSearch("index") + .setQuery(hasChildQuery("child", matchQuery("field", "value2").queryName("_name2")).innerHit(new QueryInnerHitBuilder())) + .addSort("_id", SortOrder.ASC) + .get(); + assertHitCount(response, 1); + assertThat(response.getHits().getAt(0).id(), equalTo("1")); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits(), equalTo(1l)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name2")); + } + }