/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.elasticsearch.search.fetch.subphase; import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.util.ArrayUtil; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.InnerHitBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.script.MockScriptEngine; import org.elasticsearch.script.MockScriptPlugin; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.test.ESIntegTestCase; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.function.Function; import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractValue; import static org.elasticsearch.index.query.QueryBuilders.boolQuery; import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery; 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.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.assertSearchHits; 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.notNullValue; import static org.hamcrest.Matchers.nullValue; public class InnerHitsIT extends ESIntegTestCase { @Override protected Collection<Class<? extends Plugin>> nodePlugins() { return Collections.singleton(CustomScriptPlugin.class); } public static class CustomScriptPlugin extends MockScriptPlugin { @Override protected Map<String, Function<Map<String, Object>, Object>> pluginScripts() { return Collections.singletonMap("5", script -> "5"); } } public void testSimpleNested() throws Exception { assertAcked(prepareCreate("articles").addMapping("article", jsonBuilder().startObject().startObject("article") .startObject("properties") .startObject("comments") .field("type", "nested") .startObject("properties") .startObject("message") .field("type", "text") .field("fielddata", true) .endObject() .endObject() .endObject() .startObject("title") .field("type", "text") .endObject() .endObject().endObject().endObject())); List<IndexRequestBuilder> requests = new ArrayList<>(); requests.add(client().prepareIndex("articles", "article", "1").setSource(jsonBuilder().startObject() .field("title", "quick brown fox") .startArray("comments") .startObject().field("message", "fox eat quick").endObject() .startObject().field("message", "fox ate rabbit x y z").endObject() .startObject().field("message", "rabbit got away").endObject() .endArray() .endObject())); requests.add(client().prepareIndex("articles", "article", "2").setSource(jsonBuilder().startObject() .field("title", "big gray elephant") .startArray("comments") .startObject().field("message", "elephant captured").endObject() .startObject().field("message", "mice squashed by elephant x").endObject() .startObject().field("message", "elephant scared by mice x y").endObject() .endArray() .endObject())); indexRandom(true, requests); SearchResponse response = client().prepareSearch("articles") .setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg) .innerHit(new InnerHitBuilder().setName("comment"), false) ).get(); assertNoFailures(response); assertHitCount(response, 1); assertSearchHit(response, 1, hasId("1")); assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); assertThat(innerHits.getTotalHits(), equalTo(2L)); assertThat(innerHits.getHits().length, equalTo(2)); assertThat(innerHits.getAt(0).getId(), equalTo("1")); assertThat(innerHits.getAt(0).getNestedIdentity().getField().string(), equalTo("comments")); assertThat(innerHits.getAt(0).getNestedIdentity().getOffset(), equalTo(0)); assertThat(innerHits.getAt(1).getId(), equalTo("1")); assertThat(innerHits.getAt(1).getNestedIdentity().getField().string(), equalTo("comments")); assertThat(innerHits.getAt(1).getNestedIdentity().getOffset(), equalTo(1)); response = client().prepareSearch("articles") .setQuery(nestedQuery("comments", matchQuery("comments.message", "elephant"), ScoreMode.Avg) .innerHit(new InnerHitBuilder().setName("comment"), false) ).get(); assertNoFailures(response); assertHitCount(response, 1); assertSearchHit(response, 1, hasId("2")); assertThat(response.getHits().getAt(0).getShard(), notNullValue()); assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); assertThat(innerHits.getTotalHits(), equalTo(3L)); assertThat(innerHits.getHits().length, equalTo(3)); assertThat(innerHits.getAt(0).getId(), equalTo("2")); assertThat(innerHits.getAt(0).getNestedIdentity().getField().string(), equalTo("comments")); assertThat(innerHits.getAt(0).getNestedIdentity().getOffset(), equalTo(0)); assertThat(innerHits.getAt(1).getId(), equalTo("2")); assertThat(innerHits.getAt(1).getNestedIdentity().getField().string(), equalTo("comments")); assertThat(innerHits.getAt(1).getNestedIdentity().getOffset(), equalTo(1)); assertThat(innerHits.getAt(2).getId(), equalTo("2")); assertThat(innerHits.getAt(2).getNestedIdentity().getField().string(), equalTo("comments")); assertThat(innerHits.getAt(2).getNestedIdentity().getOffset(), equalTo(2)); response = client().prepareSearch("articles") .setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit( new InnerHitBuilder().setHighlightBuilder(new HighlightBuilder().field("comments.message")) .setExplain(true) .addDocValueField("comments.message") .addScriptField("script", new Script(ScriptType.INLINE, MockScriptEngine.NAME, "5", Collections.emptyMap())) .setSize(1), false)).get(); assertNoFailures(response); innerHits = response.getHits().getAt(0).getInnerHits().get("comments"); assertThat(innerHits.getTotalHits(), equalTo(2L)); assertThat(innerHits.getHits().length, equalTo(1)); assertThat(innerHits.getAt(0).getHighlightFields().get("comments.message").getFragments()[0].string(), equalTo("<em>fox</em> eat quick")); assertThat(innerHits.getAt(0).getExplanation().toString(), containsString("weight(comments.message:fox in")); assertThat(innerHits.getAt(0).getFields().get("comments.message").getValue().toString(), equalTo("eat")); assertThat(innerHits.getAt(0).getFields().get("script").getValue().toString(), equalTo("5")); } public void testRandomNested() throws Exception { assertAcked(prepareCreate("idx").addMapping("type", "field1", "type=nested", "field2", "type=nested")); int numDocs = scaledRandomIntBetween(25, 100); List<IndexRequestBuilder> requestBuilders = new ArrayList<>(); int[] field1InnerObjects = new int[numDocs]; int[] field2InnerObjects = new int[numDocs]; for (int i = 0; i < numDocs; i++) { int numInnerObjects = field1InnerObjects[i] = scaledRandomIntBetween(1, numDocs); XContentBuilder source = jsonBuilder().startObject() .field("foo", i) .startArray("field1"); for (int j = 0; j < numInnerObjects; j++) { source.startObject().field("x", "y").endObject(); } numInnerObjects = field2InnerObjects[i] = scaledRandomIntBetween(1, numDocs); source.endArray().startArray("field2"); for (int j = 0; j < numInnerObjects; j++) { source.startObject().field("x", "y").endObject(); } source.endArray().endObject(); requestBuilders.add(client().prepareIndex("idx", "type", Integer.toString(i)).setSource(source)); } indexRandom(true, requestBuilders); int size = randomIntBetween(0, numDocs); BoolQueryBuilder boolQuery = new BoolQueryBuilder(); boolQuery.should(nestedQuery("field1", matchAllQuery(), ScoreMode.Avg).innerHit(new InnerHitBuilder().setName("a").setSize(size) .addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC)), false)); boolQuery.should(nestedQuery("field2", matchAllQuery(), ScoreMode.Avg).innerHit(new InnerHitBuilder().setName("b") .addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC)).setSize(size), false)); SearchResponse searchResponse = client().prepareSearch("idx") .setQuery(boolQuery) .setSize(numDocs) .addSort("foo", SortOrder.ASC) .get(); assertNoFailures(searchResponse); assertHitCount(searchResponse, numDocs); assertThat(searchResponse.getHits().getHits().length, equalTo(numDocs)); for (int i = 0; i < numDocs; i++) { SearchHit searchHit = searchResponse.getHits().getAt(i); assertThat(searchHit.getShard(), notNullValue()); SearchHits inner = searchHit.getInnerHits().get("a"); assertThat(inner.getTotalHits(), equalTo((long) field1InnerObjects[i])); for (int j = 0; j < field1InnerObjects[i] && j < size; j++) { SearchHit innerHit = inner.getAt(j); assertThat(innerHit.getNestedIdentity().getField().string(), equalTo("field1")); assertThat(innerHit.getNestedIdentity().getOffset(), equalTo(j)); assertThat(innerHit.getNestedIdentity().getChild(), nullValue()); } inner = searchHit.getInnerHits().get("b"); assertThat(inner.getTotalHits(), equalTo((long) field2InnerObjects[i])); for (int j = 0; j < field2InnerObjects[i] && j < size; j++) { SearchHit innerHit = inner.getAt(j); assertThat(innerHit.getNestedIdentity().getField().string(), equalTo("field2")); assertThat(innerHit.getNestedIdentity().getOffset(), equalTo(j)); assertThat(innerHit.getNestedIdentity().getChild(), nullValue()); } } } public void testNestedMultipleLayers() throws Exception { assertAcked(prepareCreate("articles").addMapping("article", jsonBuilder().startObject() .startObject("article").startObject("properties") .startObject("comments") .field("type", "nested") .startObject("properties") .startObject("message") .field("type", "text") .endObject() .startObject("remarks") .field("type", "nested") .startObject("properties") .startObject("message").field("type", "text").endObject() .endObject() .endObject() .endObject() .endObject() .startObject("title") .field("type", "text") .endObject() .endObject().endObject().endObject())); List<IndexRequestBuilder> requests = new ArrayList<>(); requests.add(client().prepareIndex("articles", "article", "1").setSource(jsonBuilder().startObject() .field("title", "quick brown fox") .startArray("comments") .startObject() .field("message", "fox eat quick") .startArray("remarks").startObject().field("message", "good").endObject().endArray() .endObject() .endArray() .endObject())); requests.add(client().prepareIndex("articles", "article", "2").setSource(jsonBuilder().startObject() .field("title", "big gray elephant") .startArray("comments") .startObject() .field("message", "elephant captured") .startArray("remarks").startObject().field("message", "bad").endObject().endArray() .endObject() .endArray() .endObject())); indexRandom(true, requests); SearchResponse response = client().prepareSearch("articles") .setQuery( nestedQuery("comments", nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "good"), ScoreMode.Avg) .innerHit(new InnerHitBuilder().setName("remark"), false), ScoreMode.Avg).innerHit(new InnerHitBuilder(), false) ).get(); assertNoFailures(response); assertHitCount(response, 1); assertSearchHit(response, 1, hasId("1")); assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comments"); assertThat(innerHits.getTotalHits(), equalTo(1L)); assertThat(innerHits.getHits().length, equalTo(1)); assertThat(innerHits.getAt(0).getId(), equalTo("1")); assertThat(innerHits.getAt(0).getNestedIdentity().getField().string(), equalTo("comments")); assertThat(innerHits.getAt(0).getNestedIdentity().getOffset(), equalTo(0)); innerHits = innerHits.getAt(0).getInnerHits().get("remark"); assertThat(innerHits.getTotalHits(), equalTo(1L)); assertThat(innerHits.getHits().length, equalTo(1)); assertThat(innerHits.getAt(0).getId(), equalTo("1")); assertThat(innerHits.getAt(0).getNestedIdentity().getField().string(), equalTo("comments")); assertThat(innerHits.getAt(0).getNestedIdentity().getOffset(), equalTo(0)); assertThat(innerHits.getAt(0).getNestedIdentity().getChild().getField().string(), equalTo("remarks")); assertThat(innerHits.getAt(0).getNestedIdentity().getChild().getOffset(), equalTo(0)); // Directly refer to the second level: response = client().prepareSearch("articles") .setQuery(nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "bad"), ScoreMode.Avg) .innerHit(new InnerHitBuilder(), false)).get(); assertNoFailures(response); assertHitCount(response, 1); assertSearchHit(response, 1, hasId("2")); assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); innerHits = response.getHits().getAt(0).getInnerHits().get("comments.remarks"); assertThat(innerHits.getTotalHits(), equalTo(1L)); assertThat(innerHits.getHits().length, equalTo(1)); assertThat(innerHits.getAt(0).getId(), equalTo("2")); assertThat(innerHits.getAt(0).getNestedIdentity().getField().string(), equalTo("comments")); assertThat(innerHits.getAt(0).getNestedIdentity().getOffset(), equalTo(0)); assertThat(innerHits.getAt(0).getNestedIdentity().getChild().getField().string(), equalTo("remarks")); assertThat(innerHits.getAt(0).getNestedIdentity().getChild().getOffset(), equalTo(0)); response = client().prepareSearch("articles") .setQuery( nestedQuery("comments", nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "bad"), ScoreMode.Avg) .innerHit(new InnerHitBuilder().setName("remark"), false), ScoreMode.Avg).innerHit(new InnerHitBuilder(), false) ).get(); assertNoFailures(response); assertHitCount(response, 1); assertSearchHit(response, 1, hasId("2")); assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); innerHits = response.getHits().getAt(0).getInnerHits().get("comments"); assertThat(innerHits.getTotalHits(), equalTo(1L)); assertThat(innerHits.getHits().length, equalTo(1)); assertThat(innerHits.getAt(0).getId(), equalTo("2")); assertThat(innerHits.getAt(0).getNestedIdentity().getField().string(), equalTo("comments")); assertThat(innerHits.getAt(0).getNestedIdentity().getOffset(), equalTo(0)); innerHits = innerHits.getAt(0).getInnerHits().get("remark"); assertThat(innerHits.getTotalHits(), equalTo(1L)); assertThat(innerHits.getHits().length, equalTo(1)); assertThat(innerHits.getAt(0).getId(), equalTo("2")); assertThat(innerHits.getAt(0).getNestedIdentity().getField().string(), equalTo("comments")); assertThat(innerHits.getAt(0).getNestedIdentity().getOffset(), equalTo(0)); assertThat(innerHits.getAt(0).getNestedIdentity().getChild().getField().string(), equalTo("remarks")); assertThat(innerHits.getAt(0).getNestedIdentity().getChild().getOffset(), equalTo(0)); } // Issue #9723 public void testNestedDefinedAsObject() throws Exception { assertAcked(prepareCreate("articles").addMapping("article", "comments", "type=nested", "title", "type=text")); List<IndexRequestBuilder> requests = new ArrayList<>(); requests.add(client().prepareIndex("articles", "article", "1").setSource(jsonBuilder().startObject() .field("title", "quick brown fox") .startObject("comments").field("message", "fox eat quick").endObject() .endObject())); indexRandom(true, requests); SearchResponse response = client().prepareSearch("articles") .setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg) .innerHit(new InnerHitBuilder(), false)) .get(); assertNoFailures(response); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("1")); assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getTotalHits(), equalTo(1L)); assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getAt(0).getId(), equalTo("1")); assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getAt(0).getNestedIdentity().getField().string(), equalTo("comments")); assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getAt(0).getNestedIdentity().getOffset(), equalTo(0)); assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getAt(0).getNestedIdentity().getChild(), nullValue()); } public void testInnerHitsWithObjectFieldThatHasANestedField() throws Exception { assertAcked(prepareCreate("articles") .addMapping("article", jsonBuilder().startObject() .startObject("properties") .startObject("comments") .field("type", "object") .startObject("properties") .startObject("messages").field("type", "nested").endObject() .endObject() .endObject() .endObject() .endObject() ) ); List<IndexRequestBuilder> requests = new ArrayList<>(); requests.add(client().prepareIndex("articles", "article", "1").setSource(jsonBuilder().startObject() .field("title", "quick brown fox") .startObject("comments") .startArray("messages") .startObject().field("message", "fox eat quick").endObject() .startObject().field("message", "bear eat quick").endObject() .endArray() .endObject() .endObject())); indexRandom(true, requests); SearchResponse response = client().prepareSearch("articles") .setQuery(nestedQuery("comments.messages", matchQuery("comments.messages.message", "fox"), ScoreMode.Avg) .innerHit(new InnerHitBuilder(), false)).get(); assertNoFailures(response); assertHitCount(response, 1); SearchHit hit = response.getHits().getAt(0); assertThat(hit.getId(), equalTo("1")); SearchHits messages = hit.getInnerHits().get("comments.messages"); assertThat(messages.getTotalHits(), equalTo(1L)); assertThat(messages.getAt(0).getId(), equalTo("1")); assertThat(messages.getAt(0).getNestedIdentity().getField().string(), equalTo("comments.messages")); assertThat(messages.getAt(0).getNestedIdentity().getOffset(), equalTo(0)); assertThat(messages.getAt(0).getNestedIdentity().getChild(), nullValue()); response = client().prepareSearch("articles") .setQuery(nestedQuery("comments.messages", matchQuery("comments.messages.message", "bear"), ScoreMode.Avg) .innerHit(new InnerHitBuilder(), false)).get(); assertNoFailures(response); assertHitCount(response, 1); hit = response.getHits().getAt(0); assertThat(hit.getId(), equalTo("1")); messages = hit.getInnerHits().get("comments.messages"); assertThat(messages.getTotalHits(), equalTo(1L)); assertThat(messages.getAt(0).getId(), equalTo("1")); assertThat(messages.getAt(0).getNestedIdentity().getField().string(), equalTo("comments.messages")); assertThat(messages.getAt(0).getNestedIdentity().getOffset(), equalTo(1)); assertThat(messages.getAt(0).getNestedIdentity().getChild(), nullValue()); // index the message in an object form instead of an array requests = new ArrayList<>(); requests.add(client().prepareIndex("articles", "article", "1").setSource(jsonBuilder().startObject() .field("title", "quick brown fox") .startObject("comments").startObject("messages").field("message", "fox eat quick").endObject().endObject() .endObject())); indexRandom(true, requests); response = client().prepareSearch("articles") .setQuery(nestedQuery("comments.messages", matchQuery("comments.messages.message", "fox"), ScoreMode.Avg) .innerHit(new InnerHitBuilder(), false)).get(); assertNoFailures(response); assertHitCount(response, 1); hit = response.getHits().getAt(0);; assertThat(hit.getId(), equalTo("1")); messages = hit.getInnerHits().get("comments.messages"); assertThat(messages.getTotalHits(), equalTo(1L)); assertThat(messages.getAt(0).getId(), equalTo("1")); assertThat(messages.getAt(0).getNestedIdentity().getField().string(), equalTo("comments.messages")); assertThat(messages.getAt(0).getNestedIdentity().getOffset(), equalTo(0)); assertThat(messages.getAt(0).getNestedIdentity().getChild(), nullValue()); } public void testMatchesQueriesNestedInnerHits() throws Exception { XContentBuilder builder = jsonBuilder().startObject() .startObject("type1") .startObject("properties") .startObject("nested1") .field("type", "nested") .startObject("properties") .startObject("n_field1") .field("type", "keyword") .endObject() .endObject() .endObject() .startObject("field1") .field("type", "long") .endObject() .endObject() .endObject() .endObject(); assertAcked(prepareCreate("test").addMapping("type1", builder)); ensureGreen(); List<IndexRequestBuilder> 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); QueryBuilder query = 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")); query = nestedQuery("nested1", query, ScoreMode.Avg).innerHit( new InnerHitBuilder().addSort(new FieldSortBuilder("nested1.n_field1").order(SortOrder.ASC)), false); SearchResponse searchResponse = client().prepareSearch("test") .setQuery(query) .setSize(numDocs) .addSort("field1", SortOrder.ASC) .get(); assertNoFailures(searchResponse); assertAllSuccessful(searchResponse); assertThat(searchResponse.getHits().getTotalHits(), equalTo((long) numDocs)); assertThat(searchResponse.getHits().getAt(0).getId(), 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).getId(), 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).getId(), 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")); } } public void testNestedSourceFiltering() throws Exception { assertAcked(prepareCreate("index1").addMapping("message", "comments", "type=nested")); client().prepareIndex("index1", "message", "1").setSource(jsonBuilder().startObject() .field("message", "quick brown fox") .startArray("comments") .startObject().field("message", "fox eat quick").endObject() .startObject().field("message", "fox ate rabbit x y z").endObject() .startObject().field("message", "rabbit got away").endObject() .endArray() .endObject()).get(); refresh(); // the field name (comments.message) used for source filtering should be the same as when using that field for // other features (like in the query dsl or aggs) in order for consistency: SearchResponse response = client().prepareSearch() .setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.None) .innerHit(new InnerHitBuilder().setFetchSourceContext(new FetchSourceContext(true, new String[]{"comments.message"}, null)), false)) .get(); assertNoFailures(response); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getTotalHits(), equalTo(2L)); assertThat(extractValue("comments.message", response.getHits().getAt(0).getInnerHits().get("comments").getAt(0).getSourceAsMap()), equalTo("fox eat quick")); assertThat(extractValue("comments.message", response.getHits().getAt(0).getInnerHits().get("comments").getAt(1).getSourceAsMap()), equalTo("fox ate rabbit x y z")); } public void testInnerHitsWithIgnoreUnmapped() throws Exception { assertAcked(prepareCreate("index1") .setSettings("index.mapping.single_type", false) .addMapping("parent_type", "nested_type", "type=nested") .addMapping("child_type", "_parent", "type=parent_type") ); createIndex("index2"); client().prepareIndex("index1", "parent_type", "1").setSource("nested_type", Collections.singletonMap("key", "value")).get(); client().prepareIndex("index1", "child_type", "2").setParent("1").setSource("{}", XContentType.JSON).get(); client().prepareIndex("index2", "type", "3").setSource("key", "value").get(); refresh(); SearchResponse response = client().prepareSearch("index1", "index2") .setQuery(boolQuery() .should(nestedQuery("nested_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) .innerHit(new InnerHitBuilder(), true)) .should(termQuery("key", "value")) ) .get(); assertNoFailures(response); assertHitCount(response, 2); assertSearchHits(response, "1", "3"); } }