From 6c3beaa2ebe4921474addf07ce3aa02a3cb7f915 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Mon, 18 Apr 2016 11:04:25 +0200 Subject: [PATCH] Drop top level inner hits in favour of inner hits defined in the query dsl. Fix a limitation that prevent from hierarchical inner hits be defined in query dsl. Removed the nested_path, parent_child_type and query options from inner hits dsl. These options are only set by ES upon parsing the has_child, has_parent and nested queries are using their respective query builders. These options are still used internally, when these options are set a new private copy is created based on the provided InnerHitBuilder and configuring either nested_path or parent_child_type and the inner query of the query builder being used. Closes #11118 --- .../action/search/SearchRequestBuilder.java | 6 - .../index/query/AbstractQueryBuilder.java | 10 + .../index/query/BoolQueryBuilder.java | 12 + .../index/query/BoostingQueryBuilder.java | 7 + .../query/ConstantScoreQueryBuilder.java | 6 + .../index/query/HasChildQueryBuilder.java | 24 +- .../index/query/HasParentQueryBuilder.java | 25 +- .../query/{support => }/InnerHitBuilder.java | 246 +++++---- .../index/query/NestedQueryBuilder.java | 24 +- .../index/query/QueryShardContext.java | 12 - .../FunctionScoreQueryBuilder.java | 11 +- .../index/query/support/InnerHitsBuilder.java | 126 ----- .../elasticsearch/search/SearchService.java | 34 +- .../search/builder/SearchSourceBuilder.java | 33 +- .../query/HasChildQueryBuilderTests.java | 41 +- .../query/HasParentQueryBuilderTests.java | 23 +- .../{support => }/InnerHitBuilderTests.java | 190 +++++-- .../index/query/NestedQueryBuilderTests.java | 63 ++- .../query/support/InnerHitsBuilderTests.java | 140 ----- .../percolator/PercolatorIT.java | 31 +- .../builder/SearchSourceBuilderTests.java | 10 - .../search/innerhits/InnerHitsIT.java | 492 ++++++------------ .../migration/migrate_5_0/search.asciidoc | 5 +- .../search/request/inner-hits.asciidoc | 76 +-- 24 files changed, 647 insertions(+), 1000 deletions(-) rename core/src/main/java/org/elasticsearch/index/query/{support => }/InnerHitBuilder.java (73%) delete mode 100644 core/src/main/java/org/elasticsearch/index/query/support/InnerHitsBuilder.java rename core/src/test/java/org/elasticsearch/index/query/{support => }/InnerHitBuilderTests.java (55%) delete mode 100644 core/src/test/java/org/elasticsearch/index/query/support/InnerHitsBuilderTests.java diff --git a/core/src/main/java/org/elasticsearch/action/search/SearchRequestBuilder.java b/core/src/main/java/org/elasticsearch/action/search/SearchRequestBuilder.java index 2d3d9553090..fc28addc111 100644 --- a/core/src/main/java/org/elasticsearch/action/search/SearchRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/action/search/SearchRequestBuilder.java @@ -31,7 +31,6 @@ import org.elasticsearch.search.Scroll; import org.elasticsearch.search.aggregations.AggregatorBuilder; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregatorBuilder; import org.elasticsearch.search.builder.SearchSourceBuilder; -import org.elasticsearch.index.query.support.InnerHitsBuilder; import org.elasticsearch.search.highlight.HighlightBuilder; import org.elasticsearch.search.rescore.RescoreBuilder; import org.elasticsearch.search.sort.SortBuilder; @@ -400,11 +399,6 @@ public class SearchRequestBuilder extends ActionRequestBuilder> return this; } + /** + * For internal usage only! + * + * Extracts the inner hits from the query tree. + * While it extracts inner hits, child inner hits are inlined into the inner hit builder they belong to. + */ + protected void extractInnerHitBuilders(Map innerHits) { + } + // Like Objects.requireNotNull(...) but instead throws a IllegalArgumentException protected static T requireValue(T value, String message) { if (value == null) { diff --git a/core/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java index 0561a0f5250..c6de1213a8d 100644 --- a/core/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java @@ -35,6 +35,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.function.Consumer; @@ -495,6 +496,17 @@ public class BoolQueryBuilder extends AbstractQueryBuilder { return this; } + @Override + protected void extractInnerHitBuilders(Map innerHits) { + List> clauses = new ArrayList<>(filter()); + clauses.addAll(must()); + clauses.addAll(should()); + // no need to include must_not (since there will be no hits for it) + for (QueryBuilder clause : clauses) { + InnerHitBuilder.extractInnerHits(clause, innerHits); + } + } + private static boolean rewriteClauses(QueryRewriteContext queryRewriteContext, List> builders, Consumer> consumer) throws IOException { boolean changed = false; diff --git a/core/src/main/java/org/elasticsearch/index/query/BoostingQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/BoostingQueryBuilder.java index 258b8e9813a..fa439bc71d8 100644 --- a/core/src/main/java/org/elasticsearch/index/query/BoostingQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/BoostingQueryBuilder.java @@ -29,6 +29,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; +import java.util.Map; import java.util.Objects; /** @@ -235,4 +236,10 @@ public class BoostingQueryBuilder extends AbstractQueryBuilder innerHits) { + InnerHitBuilder.extractInnerHits(positiveQuery, innerHits); + InnerHitBuilder.extractInnerHits(negativeQuery, innerHits); + } } diff --git a/core/src/main/java/org/elasticsearch/index/query/ConstantScoreQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/ConstantScoreQueryBuilder.java index a762954da2a..8ae2a088f2f 100644 --- a/core/src/main/java/org/elasticsearch/index/query/ConstantScoreQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/ConstantScoreQueryBuilder.java @@ -29,6 +29,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; +import java.util.Map; import java.util.Objects; /** @@ -169,4 +170,9 @@ public class ConstantScoreQueryBuilder extends AbstractQueryBuilder innerHits) { + InnerHitBuilder.extractInnerHits(filterBuilder, innerHits); + } } diff --git a/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java index 6cbea202e6f..61466ab5ea5 100644 --- a/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java @@ -38,10 +38,10 @@ import org.elasticsearch.index.fielddata.IndexParentChildFieldData; import org.elasticsearch.index.fielddata.plain.ParentChildIndexFieldData; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.internal.ParentFieldMapper; -import org.elasticsearch.index.query.support.InnerHitBuilder; import java.io.IOException; import java.util.Locale; +import java.util.Map; import java.util.Objects; /** @@ -151,9 +151,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder innerHits) { + if (innerHitBuilder != null) { + innerHitBuilder.inlineInnerHits(innerHits); + } + } } diff --git a/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java index 3462e8255c7..dc3b4d9c66a 100644 --- a/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java @@ -33,10 +33,10 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.fielddata.plain.ParentChildIndexFieldData; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.internal.ParentFieldMapper; -import org.elasticsearch.index.query.support.InnerHitBuilder; import java.io.IOException; import java.util.HashSet; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -127,9 +127,7 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder childTypes = new HashSet<>(); ParentChildIndexFieldData parentChildIndexFieldData = null; for (DocumentMapper documentMapper : context.getMapperService().docMappers(false)) { @@ -282,8 +276,14 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder innerHits) { + if (innerHit!= null) { + innerHit.inlineInnerHits(innerHits); + } + } } diff --git a/core/src/main/java/org/elasticsearch/index/query/support/InnerHitBuilder.java b/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java similarity index 73% rename from core/src/main/java/org/elasticsearch/index/query/support/InnerHitBuilder.java rename to core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java index 5790e7742c8..2734dbc0e6b 100644 --- a/core/src/main/java/org/elasticsearch/index/query/support/InnerHitBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.index.query.support; +package org.elasticsearch.index.query; import org.apache.lucene.search.Sort; import org.elasticsearch.action.support.ToXContentToBytes; @@ -30,11 +30,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.object.ObjectMapper; -import org.elasticsearch.index.query.MatchAllQueryBuilder; -import org.elasticsearch.index.query.ParsedQuery; -import org.elasticsearch.index.query.QueryBuilder; -import org.elasticsearch.index.query.QueryParseContext; -import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.SearchScript; @@ -62,15 +57,12 @@ import static org.elasticsearch.common.xcontent.XContentParser.Token.END_OBJECT; public final class InnerHitBuilder extends ToXContentToBytes implements Writeable { public static final ParseField NAME_FIELD = new ParseField("name"); - public static final ParseField NESTED_PATH_FIELD = new ParseField("path"); - public static final ParseField PARENT_CHILD_TYPE_FIELD = new ParseField("type"); + public static final ParseField INNER_HITS_FIELD = new ParseField("inner_hits"); private final static ObjectParser PARSER = new ObjectParser<>("inner_hits", InnerHitBuilder::new); static { PARSER.declareString(InnerHitBuilder::setName, NAME_FIELD); - PARSER.declareString(InnerHitBuilder::setNestedPath, NESTED_PATH_FIELD); - PARSER.declareString(InnerHitBuilder::setParentChildType, PARENT_CHILD_TYPE_FIELD); PARSER.declareInt(InnerHitBuilder::setFrom, SearchSourceBuilder.FROM_FIELD); PARSER.declareInt(InnerHitBuilder::setSize, SearchSourceBuilder.SIZE_FIELD); PARSER.declareBoolean(InnerHitBuilder::setExplain, SearchSourceBuilder.EXPLAIN_FIELD); @@ -100,20 +92,30 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl }, SearchSourceBuilder._SOURCE_FIELD, ObjectParser.ValueType.OBJECT_OR_BOOLEAN); PARSER.declareObject(InnerHitBuilder::setHighlightBuilder, (p, c) -> HighlightBuilder.fromXContent(c), SearchSourceBuilder.HIGHLIGHT_FIELD); - PARSER.declareObject(InnerHitBuilder::setQuery, (p, c) ->{ + PARSER.declareObject(InnerHitBuilder::setChildInnerHits, (p, c) -> { try { - return c.parseInnerQueryBuilder(); + Map innerHitBuilders = new HashMap<>(); + String innerHitName = null; + for (XContentParser.Token token = p.nextToken(); token != XContentParser.Token.END_OBJECT; token = p.nextToken()) { + switch (token) { + case START_OBJECT: + InnerHitBuilder innerHitBuilder = InnerHitBuilder.fromXContent(c); + innerHitBuilder.setName(innerHitName); + innerHitBuilders.put(innerHitName, innerHitBuilder); + break; + case FIELD_NAME: + innerHitName = p.currentName(); + break; + default: + throw new ParsingException(p.getTokenLocation(), "Expected [" + XContentParser.Token.START_OBJECT + "] in [" + + p.currentName() + "] but found [" + token + "]", p.getTokenLocation()); + } + } + return innerHitBuilders; } catch (IOException e) { throw new ParsingException(p.getTokenLocation(), "Could not parse inner query definition", e); } - }, SearchSourceBuilder.QUERY_FIELD); - PARSER.declareObject(InnerHitBuilder::setInnerHitsBuilder, (p, c) -> { - try { - return InnerHitsBuilder.fromXContent(c); - } catch (IOException e) { - throw new ParsingException(p.getTokenLocation(), "Could not parse inner query definition", e); - } - }, SearchSourceBuilder.INNER_HITS_FIELD); + }, INNER_HITS_FIELD); } private String name; @@ -132,8 +134,8 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl private List fieldDataFields; private List scriptFields; private HighlightBuilder highlightBuilder; - private InnerHitsBuilder innerHitsBuilder; private FetchSourceContext fetchSourceContext; + private Map childInnerHits; public InnerHitBuilder() { } @@ -165,7 +167,62 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl } highlightBuilder = in.readOptionalWriteable(HighlightBuilder::new); query = in.readNamedWriteable(QueryBuilder.class); - innerHitsBuilder = in.readOptionalWriteable(InnerHitsBuilder::new); + if (in.readBoolean()) { + int size = in.readVInt(); + childInnerHits = new HashMap<>(size); + for (int i = 0; i < size; i++) { + childInnerHits.put(in.readString(), new InnerHitBuilder(in)); + } + } + } + + private InnerHitBuilder(InnerHitBuilder other) { + name = other.name; + from = other.from; + size = other.size; + explain = other.explain; + version = other.version; + trackScores = other.trackScores; + if (other.fieldNames != null) { + fieldNames = new ArrayList<>(other.fieldNames); + } + if (other.fieldDataFields != null) { + fieldDataFields = new ArrayList<>(other.fieldDataFields); + } + if (other.scriptFields != null) { + scriptFields = new ArrayList<>(other.scriptFields); + } + if (other.fetchSourceContext != null) { + fetchSourceContext = new FetchSourceContext( + other.fetchSourceContext.fetchSource(), other.fetchSourceContext.includes(), other.fetchSourceContext.excludes() + ); + } + if (other.sorts != null) { + sorts = new ArrayList<>(other.sorts); + } + highlightBuilder = other.highlightBuilder; + if (other.childInnerHits != null) { + childInnerHits = new HashMap<>(other.childInnerHits); + } + } + + + InnerHitBuilder(InnerHitBuilder other, String nestedPath, QueryBuilder query) { + this(other); + this.query = query; + this.nestedPath = nestedPath; + if (name == null) { + this.name = nestedPath; + } + } + + InnerHitBuilder(InnerHitBuilder other, QueryBuilder query, String parentChildType) { + this(other); + this.query = query; + this.parentChildType = parentChildType; + if (name == null) { + this.name = parentChildType; + } } @Override @@ -196,17 +253,15 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl } out.writeOptionalWriteable(highlightBuilder); out.writeNamedWriteable(query); - out.writeOptionalWriteable(innerHitsBuilder); - } - - public InnerHitBuilder setParentChildType(String parentChildType) { - this.parentChildType = parentChildType; - return this; - } - - public InnerHitBuilder setNestedPath(String nestedPath) { - this.nestedPath = nestedPath; - return this; + boolean hasChildInnerHits = childInnerHits != null; + out.writeBoolean(hasChildInnerHits); + if (hasChildInnerHits) { + out.writeVInt(childInnerHits.size()); + for (Map.Entry entry : childInnerHits.entrySet()) { + out.writeString(entry.getKey()); + entry.getValue().writeTo(out); + } + } } public String getName() { @@ -347,72 +402,53 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl return this; } - public QueryBuilder getQuery() { + QueryBuilder getQuery() { return query; } - public InnerHitBuilder setQuery(QueryBuilder query) { - this.query = Objects.requireNonNull(query); - return this; + void setChildInnerHits(Map childInnerHits) { + this.childInnerHits = childInnerHits; } - public InnerHitBuilder setInnerHitsBuilder(InnerHitsBuilder innerHitsBuilder) { - this.innerHitsBuilder = innerHitsBuilder; - return this; + String getParentChildType() { + return parentChildType; } - public InnerHitsContext.BaseInnerHits buildInline(SearchContext parentSearchContext, QueryShardContext context) throws IOException { - InnerHitsContext.BaseInnerHits innerHitsContext; - if (nestedPath != null) { - ObjectMapper nestedObjectMapper = context.getObjectMapper(nestedPath); - ObjectMapper parentObjectMapper = context.nestedScope().getObjectMapper(); - innerHitsContext = new InnerHitsContext.NestedInnerHits( - name, parentSearchContext, parentObjectMapper, nestedObjectMapper - ); - } else if (parentChildType != null) { - DocumentMapper documentMapper = context.getMapperService().documentMapper(parentChildType); - innerHitsContext = new InnerHitsContext.ParentChildInnerHits( - name, parentSearchContext, context.getMapperService(), documentMapper - ); - } else { - throw new IllegalStateException("Neither a nested or parent/child inner hit"); + String getNestedPath() { + return nestedPath; + } + + void addChildInnerHit(InnerHitBuilder innerHitBuilder) { + if (childInnerHits == null) { + childInnerHits = new HashMap<>(); } - setupInnerHitsContext(context, innerHitsContext); - return innerHitsContext; + this.childInnerHits.put(innerHitBuilder.getName(), innerHitBuilder); } - /** - * Top level inner hits are different than inline inner hits: - * 1) Nesting. Top level inner hits can be hold nested inner hits, that why this method is recursive (via buildChildInnerHits) - * 2) Top level inner hits query is an option, whereas with inline inner hits that is based on the nested, has_child - * or has_parent's inner query. - * - * Because of these changes there are different methods for building inline (which is simpler) and top level inner - * hits. Also top level inner hits will soon be deprecated. - */ - public InnerHitsContext.BaseInnerHits buildTopLevel(SearchContext parentSearchContext, QueryShardContext context, - InnerHitsContext innerHitsContext) throws IOException { + public InnerHitsContext.BaseInnerHits build(SearchContext parentSearchContext, + InnerHitsContext innerHitsContext) throws IOException { + QueryShardContext queryShardContext = parentSearchContext.getQueryShardContext(); if (nestedPath != null) { - ObjectMapper nestedObjectMapper = context.getObjectMapper(nestedPath); - ObjectMapper parentObjectMapper = context.nestedScope().nextLevel(nestedObjectMapper); + ObjectMapper nestedObjectMapper = queryShardContext.getObjectMapper(nestedPath); + ObjectMapper parentObjectMapper = queryShardContext.nestedScope().nextLevel(nestedObjectMapper); InnerHitsContext.NestedInnerHits nestedInnerHits = new InnerHitsContext.NestedInnerHits( name, parentSearchContext, parentObjectMapper, nestedObjectMapper ); - setupInnerHitsContext(context, nestedInnerHits); - if (innerHitsBuilder != null) { - buildChildInnerHits(parentSearchContext, context, nestedInnerHits); + setupInnerHitsContext(queryShardContext, nestedInnerHits); + if (childInnerHits != null) { + buildChildInnerHits(parentSearchContext, nestedInnerHits); } - context.nestedScope().previousLevel(); + queryShardContext.nestedScope().previousLevel(); innerHitsContext.addInnerHitDefinition(nestedInnerHits); return nestedInnerHits; } else if (parentChildType != null) { - DocumentMapper documentMapper = context.getMapperService().documentMapper(parentChildType); + DocumentMapper documentMapper = queryShardContext.getMapperService().documentMapper(parentChildType); InnerHitsContext.ParentChildInnerHits parentChildInnerHits = new InnerHitsContext.ParentChildInnerHits( - name, parentSearchContext, context.getMapperService(), documentMapper + name, parentSearchContext, queryShardContext.getMapperService(), documentMapper ); - setupInnerHitsContext(context, parentChildInnerHits); - if (innerHitsBuilder != null) { - buildChildInnerHits(parentSearchContext, context, parentChildInnerHits); + setupInnerHitsContext(queryShardContext, parentChildInnerHits); + if (childInnerHits != null) { + buildChildInnerHits(parentSearchContext, parentChildInnerHits); } innerHitsContext.addInnerHitDefinition( parentChildInnerHits); return parentChildInnerHits; @@ -421,12 +457,11 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl } } - private void buildChildInnerHits(SearchContext parentSearchContext, QueryShardContext context, - InnerHitsContext.BaseInnerHits innerHits) throws IOException { + private void buildChildInnerHits(SearchContext parentSearchContext, InnerHitsContext.BaseInnerHits innerHits) throws IOException { Map childInnerHits = new HashMap<>(); - for (Map.Entry entry : innerHitsBuilder.getInnerHitsBuilders().entrySet()) { - InnerHitsContext.BaseInnerHits childInnerHit = entry.getValue().buildTopLevel( - parentSearchContext, context, new InnerHitsContext() + for (Map.Entry entry : this.childInnerHits.entrySet()) { + InnerHitsContext.BaseInnerHits childInnerHit = entry.getValue().build( + parentSearchContext, new InnerHitsContext() ); childInnerHits.put(entry.getKey(), childInnerHit); } @@ -480,16 +515,23 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl innerHitsContext.parsedQuery(parsedQuery); } + public void inlineInnerHits(Map innerHits) { + InnerHitBuilder copy = new InnerHitBuilder(this); + copy.parentChildType = this.parentChildType; + copy.nestedPath = this.nestedPath; + copy.query = this.query; + innerHits.put(copy.getName(), copy); + + Map childInnerHits = new HashMap<>(); + extractInnerHits(query, childInnerHits); + if (childInnerHits.size() > 0) { + copy.setChildInnerHits(childInnerHits); + } + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); - - if (nestedPath != null) { - builder.field(NESTED_PATH_FIELD.getPreferredName(), nestedPath); - } - if (parentChildType != null) { - builder.field(PARENT_CHILD_TYPE_FIELD.getPreferredName(), parentChildType); - } if (name != null) { builder.field(NAME_FIELD.getPreferredName(), name); } @@ -536,9 +578,12 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl if (highlightBuilder != null) { builder.field(SearchSourceBuilder.HIGHLIGHT_FIELD.getPreferredName(), highlightBuilder, params); } - builder.field(SearchSourceBuilder.QUERY_FIELD.getPreferredName(), query, params); - if (innerHitsBuilder != null) { - builder.field(SearchSourceBuilder.INNER_HITS_FIELD.getPreferredName(), innerHitsBuilder, params); + if (childInnerHits != null) { + builder.startObject(INNER_HITS_FIELD.getPreferredName()); + for (Map.Entry entry : childInnerHits.entrySet()) { + builder.field(entry.getKey(), entry.getValue(), params); + } + builder.endObject(); } builder.endObject(); return builder; @@ -565,17 +610,26 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl Objects.equals(sorts, that.sorts) && Objects.equals(highlightBuilder, that.highlightBuilder) && Objects.equals(query, that.query) && - Objects.equals(innerHitsBuilder, that.innerHitsBuilder); + Objects.equals(childInnerHits, that.childInnerHits); } @Override public int hashCode() { return Objects.hash(name, nestedPath, parentChildType, from, size, explain, version, trackScores, fieldNames, - fieldDataFields, scriptFields, fetchSourceContext, sorts, highlightBuilder, query, innerHitsBuilder); + fieldDataFields, scriptFields, fetchSourceContext, sorts, highlightBuilder, query, childInnerHits); } public static InnerHitBuilder fromXContent(QueryParseContext context) throws IOException { return PARSER.parse(context.parser(), new InnerHitBuilder(), context); } + public static void extractInnerHits(QueryBuilder query, Map innerHitBuilders) { + if (query instanceof AbstractQueryBuilder) { + ((AbstractQueryBuilder) query).extractInnerHitBuilders(innerHitBuilders); + } else { + throw new IllegalStateException("provided query builder [" + query.getClass() + + "] class should inherit from AbstractQueryBuilder, but it doesn't"); + } + } + } diff --git a/core/src/main/java/org/elasticsearch/index/query/NestedQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/NestedQueryBuilder.java index 8968d7850ab..bba0365d0ae 100644 --- a/core/src/main/java/org/elasticsearch/index/query/NestedQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/NestedQueryBuilder.java @@ -32,9 +32,9 @@ import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.mapper.object.ObjectMapper; -import org.elasticsearch.index.query.support.InnerHitBuilder; import java.io.IOException; +import java.util.Map; import java.util.Objects; public class NestedQueryBuilder extends AbstractQueryBuilder { @@ -109,9 +109,7 @@ public class NestedQueryBuilder extends AbstractQueryBuilder } public NestedQueryBuilder innerHit(InnerHitBuilder innerHit) { - innerHit.setNestedPath(path); - innerHit.setQuery(query); - this.innerHitBuilder = innerHit; + this.innerHitBuilder = new InnerHitBuilder(innerHit, path, query); return this; } @@ -196,8 +194,14 @@ public class NestedQueryBuilder extends AbstractQueryBuilder } } } - return new NestedQueryBuilder(path, query, scoreMode, innerHitBuilder).ignoreUnmapped(ignoreUnmapped).queryName(queryName) + NestedQueryBuilder queryBuilder = new NestedQueryBuilder(path, query, scoreMode) + .ignoreUnmapped(ignoreUnmapped) + .queryName(queryName) .boost(boost); + if (innerHitBuilder != null) { + queryBuilder.innerHit(innerHitBuilder); + } + return queryBuilder; } @Override @@ -236,9 +240,6 @@ public class NestedQueryBuilder extends AbstractQueryBuilder final Query childFilter; final Query innerQuery; ObjectMapper objectMapper = context.nestedScope().getObjectMapper(); - if (innerHitBuilder != null) { - context.addInnerHit(innerHitBuilder); - } if (objectMapper == null) { parentFilter = context.bitsetFilter(Queries.newNonNestedFilter()); } else { @@ -265,4 +266,11 @@ public class NestedQueryBuilder extends AbstractQueryBuilder } return this; } + + @Override + protected void extractInnerHitBuilders(Map innerHits) { + if (innerHitBuilder != null) { + innerHitBuilder.inlineInnerHits(innerHits); + } + } } diff --git a/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java b/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java index 04dfe78b2d3..2c0b90f059c 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java @@ -57,12 +57,10 @@ import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.core.TextFieldMapper; import org.elasticsearch.index.mapper.object.ObjectMapper; import org.elasticsearch.index.percolator.PercolatorQueryCache; -import org.elasticsearch.index.query.support.InnerHitBuilder; import org.elasticsearch.index.query.support.NestedScope; import org.elasticsearch.index.similarity.SimilarityService; import org.elasticsearch.indices.query.IndicesQueriesRegistry; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.search.fetch.innerhits.InnerHitsContext; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.lookup.SearchLookup; @@ -185,16 +183,6 @@ public class QueryShardContext extends QueryRewriteContext { return isFilter; } - public void addInnerHit(InnerHitBuilder innerHitBuilder) throws IOException { - SearchContext sc = SearchContext.current(); - if (sc == null) { - throw new QueryShardException(this, "inner_hits unsupported"); - } - - InnerHitsContext innerHitsContext = sc.innerHits(); - innerHitsContext.addInnerHitDefinition(innerHitBuilder.buildInline(sc, this)); - } - public Collection simpleMatchToIndexNames(String pattern) { return mapperService.simpleMatchToIndexNames(pattern); } diff --git a/core/src/main/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilder.java index bf5b8baff81..0dffa6d714a 100644 --- a/core/src/main/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilder.java @@ -42,12 +42,14 @@ import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.query.InnerHitBuilder; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Objects; /** @@ -429,8 +431,15 @@ public class FunctionScoreQueryBuilder extends AbstractQueryBuilder innerHits) { + InnerHitBuilder.extractInnerHits(query(), innerHits); + } + public static FunctionScoreQueryBuilder fromXContent(ParseFieldRegistry> scoreFunctionsRegistry, - QueryParseContext parseContext) throws IOException { + QueryParseContext parseContext) throws IOException { XContentParser parser = parseContext.parser(); QueryBuilder query = null; diff --git a/core/src/main/java/org/elasticsearch/index/query/support/InnerHitsBuilder.java b/core/src/main/java/org/elasticsearch/index/query/support/InnerHitsBuilder.java deleted file mode 100644 index 64355083906..00000000000 --- a/core/src/main/java/org/elasticsearch/index/query/support/InnerHitsBuilder.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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.index.query.support; - -import org.elasticsearch.action.support.ToXContentToBytes; -import org.elasticsearch.common.ParsingException; -import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.XContentParser.Token; -import org.elasticsearch.index.query.QueryParseContext; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -public final class InnerHitsBuilder extends ToXContentToBytes implements Writeable { - private final Map innerHitsBuilders; - - public InnerHitsBuilder() { - this.innerHitsBuilders = new HashMap<>(); - } - - public InnerHitsBuilder(Map innerHitsBuilders) { - this.innerHitsBuilders = Objects.requireNonNull(innerHitsBuilders); - } - - /** - * Read from a stream. - */ - public InnerHitsBuilder(StreamInput in) throws IOException { - int size = in.readVInt(); - innerHitsBuilders = new HashMap<>(size); - for (int i = 0; i < size; i++) { - innerHitsBuilders.put(in.readString(), new InnerHitBuilder(in)); - } - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(innerHitsBuilders.size()); - for (Map.Entry entry : innerHitsBuilders.entrySet()) { - out.writeString(entry.getKey()); - entry.getValue().writeTo(out); - } - } - - public InnerHitsBuilder addInnerHit(String name, InnerHitBuilder builder) { - Objects.requireNonNull(name); - Objects.requireNonNull(builder); - this.innerHitsBuilders.put(name, builder.setName(name)); - return this; - } - - public Map getInnerHitsBuilders() { - return innerHitsBuilders; - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); - for (Map.Entry entry : innerHitsBuilders.entrySet()) { - builder.field(entry.getKey(), entry.getValue(), params); - } - builder.endObject(); - return builder; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - InnerHitsBuilder that = (InnerHitsBuilder) o; - return innerHitsBuilders.equals(that.innerHitsBuilders); - - } - - @Override - public int hashCode() { - return innerHitsBuilders.hashCode(); - } - - public static InnerHitsBuilder fromXContent(QueryParseContext context) throws IOException { - Map innerHitBuilders = new HashMap<>(); - String innerHitName = null; - XContentParser parser = context.parser(); - for (Token token = parser.nextToken(); token != Token.END_OBJECT; token = parser.nextToken()) { - switch (token) { - case START_OBJECT: - InnerHitBuilder innerHitBuilder = InnerHitBuilder.fromXContent(context); - innerHitBuilder.setName(innerHitName); - innerHitBuilders.put(innerHitName, innerHitBuilder); - break; - case FIELD_NAME: - innerHitName = parser.currentName(); - break; - default: - throw new ParsingException(parser.getTokenLocation(), "Expected [" + XContentParser.Token.START_OBJECT + "] in [" - + parser.currentName() + "] but found [" + token + "]", parser.getTokenLocation()); - } - } - return new InnerHitsBuilder(innerHitBuilders); - } - - -} diff --git a/core/src/main/java/org/elasticsearch/search/SearchService.java b/core/src/main/java/org/elasticsearch/search/SearchService.java index 880e6f0e916..24746431949 100644 --- a/core/src/main/java/org/elasticsearch/search/SearchService.java +++ b/core/src/main/java/org/elasticsearch/search/SearchService.java @@ -62,7 +62,7 @@ import org.elasticsearch.index.IndexService; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryShardContext; -import org.elasticsearch.index.query.support.InnerHitBuilder; +import org.elasticsearch.index.query.InnerHitBuilder; import org.elasticsearch.index.search.stats.StatsGroupsParseElement; import org.elasticsearch.index.shard.IndexEventListener; import org.elasticsearch.index.shard.IndexShard; @@ -88,7 +88,6 @@ import org.elasticsearch.search.fetch.ShardFetchRequest; import org.elasticsearch.search.fetch.fielddata.FieldDataFieldsContext; import org.elasticsearch.search.fetch.fielddata.FieldDataFieldsContext.FieldDataField; import org.elasticsearch.search.fetch.fielddata.FieldDataFieldsFetchSubPhase; -import org.elasticsearch.search.fetch.innerhits.InnerHitsContext; import org.elasticsearch.search.fetch.script.ScriptFieldsContext.ScriptField; import org.elasticsearch.search.highlight.HighlightBuilder; import org.elasticsearch.search.internal.DefaultSearchContext; @@ -679,12 +678,24 @@ public class SearchService extends AbstractLifecycleComponent imp context.queryBoost(indexBoost); } } + Map innerHitBuilders = new HashMap<>(); if (source.query() != null) { + InnerHitBuilder.extractInnerHits(source.query(), innerHitBuilders); context.parsedQuery(queryShardContext.toQuery(source.query())); } if (source.postFilter() != null) { + InnerHitBuilder.extractInnerHits(source.postFilter(), innerHitBuilders); context.parsedPostFilter(queryShardContext.toQuery(source.postFilter())); } + if (innerHitBuilders.size() > 0) { + for (Map.Entry entry : innerHitBuilders.entrySet()) { + try { + entry.getValue().build(context, context.innerHits()); + } catch (IOException e) { + throw new SearchContextException(context, "failed to build inner_hits", e); + } + } + } if (source.sorts() != null) { try { Optional optionalSort = SortBuilder.buildSort(source.sorts(), context.getQueryShardContext()); @@ -754,25 +765,6 @@ public class SearchService extends AbstractLifecycleComponent imp throw new SearchContextException(context, "failed to create SearchContextHighlighter", e); } } - if (source.innerHits() != null) { - for (Map.Entry entry : source.innerHits().getInnerHitsBuilders().entrySet()) { - try { - // This is the same logic in QueryShardContext#toQuery() where we reset also twice. - // Personally I think a reset at the end is sufficient, but I kept the logic consistent with this method. - - // The reason we need to invoke reset at all here is because inner hits may modify the QueryShardContext#nestedScope, - // so we need to reset at the end. - queryShardContext.reset(); - InnerHitBuilder innerHitBuilder = entry.getValue(); - InnerHitsContext innerHitsContext = context.innerHits(); - innerHitBuilder.buildTopLevel(context, queryShardContext, innerHitsContext); - } catch (IOException e) { - throw new SearchContextException(context, "failed to create InnerHitsContext", e); - } finally { - queryShardContext.reset(); - } - } - } if (source.scriptFields() != null) { for (org.elasticsearch.search.builder.SearchSourceBuilder.ScriptField field : source.scriptFields()) { SearchScript searchScript = context.scriptService().search(context.lookup(), field.script(), ScriptContext.Standard.SEARCH, diff --git a/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java b/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java index 853a01abb37..b656df4e154 100644 --- a/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java @@ -40,7 +40,6 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryShardContext; -import org.elasticsearch.index.query.support.InnerHitsBuilder; import org.elasticsearch.script.Script; import org.elasticsearch.search.aggregations.AggregatorBuilder; import org.elasticsearch.search.aggregations.AggregatorFactories; @@ -93,7 +92,6 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ public static final ParseField INDICES_BOOST_FIELD = new ParseField("indices_boost"); public static final ParseField AGGREGATIONS_FIELD = new ParseField("aggregations", "aggs"); public static final ParseField HIGHLIGHT_FIELD = new ParseField("highlight"); - public static final ParseField INNER_HITS_FIELD = new ParseField("inner_hits"); public static final ParseField SUGGEST_FIELD = new ParseField("suggest"); public static final ParseField RESCORE_FIELD = new ParseField("rescore"); public static final ParseField STATS_FIELD = new ParseField("stats"); @@ -156,8 +154,6 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ private SuggestBuilder suggestBuilder; - private InnerHitsBuilder innerHitsBuilder; - private List> rescoreBuilders; private ObjectFloatHashMap indexBoost = null; @@ -205,14 +201,11 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ boolean hasIndexBoost = in.readBoolean(); if (hasIndexBoost) { int size = in.readVInt(); - indexBoost = new ObjectFloatHashMap(size); + indexBoost = new ObjectFloatHashMap<>(size); for (int i = 0; i < size; i++) { indexBoost.put(in.readString(), in.readFloat()); } } - if (in.readBoolean()) { - innerHitsBuilder = new InnerHitsBuilder(in); - } if (in.readBoolean()) { minScore = in.readFloat(); } @@ -303,11 +296,6 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ out.writeFloat(indexBoost.get(key.value)); } } - boolean hasInnerHitsBuilder = innerHitsBuilder != null; - out.writeBoolean(hasInnerHitsBuilder); - if (hasInnerHitsBuilder) { - innerHitsBuilder.writeTo(out); - } boolean hasMinScore = minScore != null; out.writeBoolean(hasMinScore); if (hasMinScore) { @@ -653,15 +641,6 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ return highlightBuilder; } - public SearchSourceBuilder innerHits(InnerHitsBuilder innerHitsBuilder) { - this.innerHitsBuilder = innerHitsBuilder; - return this; - } - - public InnerHitsBuilder innerHits() { - return innerHitsBuilder; - } - public SearchSourceBuilder suggest(SuggestBuilder suggestBuilder) { this.suggestBuilder = suggestBuilder; return this; @@ -957,7 +936,6 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ rewrittenBuilder.from = from; rewrittenBuilder.highlightBuilder = highlightBuilder; rewrittenBuilder.indexBoost = indexBoost; - rewrittenBuilder.innerHitsBuilder = innerHitsBuilder; rewrittenBuilder.minScore = minScore; rewrittenBuilder.postQueryBuilder = postQueryBuilder; rewrittenBuilder.profile = profile; @@ -1051,8 +1029,6 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ aggregations = aggParsers.parseAggregators(context); } else if (context.getParseFieldMatcher().match(currentFieldName, HIGHLIGHT_FIELD)) { highlightBuilder = HighlightBuilder.fromXContent(context); - } else if (context.getParseFieldMatcher().match(currentFieldName, INNER_HITS_FIELD)) { - innerHitsBuilder = InnerHitsBuilder.fromXContent(context); } else if (context.getParseFieldMatcher().match(currentFieldName, SUGGEST_FIELD)) { suggestBuilder = SuggestBuilder.fromXContent(context, suggesters); } else if (context.getParseFieldMatcher().match(currentFieldName, SORT_FIELD)) { @@ -1235,10 +1211,6 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ builder.field(HIGHLIGHT_FIELD.getPreferredName(), highlightBuilder); } - if (innerHitsBuilder != null) { - builder.field(INNER_HITS_FIELD.getPreferredName(), innerHitsBuilder, params); - } - if (suggestBuilder != null) { builder.field(SUGGEST_FIELD.getPreferredName(), suggestBuilder); } @@ -1379,7 +1351,7 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ @Override public int hashCode() { return Objects.hash(aggregations, explain, fetchSourceContext, fieldDataFields, fieldNames, from, - highlightBuilder, indexBoost, innerHitsBuilder, minScore, postQueryBuilder, queryBuilder, rescoreBuilders, scriptFields, + highlightBuilder, indexBoost, minScore, postQueryBuilder, queryBuilder, rescoreBuilders, scriptFields, size, sorts, searchAfterBuilder, stats, suggestBuilder, terminateAfter, timeoutInMillis, trackScores, version, profile); } @@ -1400,7 +1372,6 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ && Objects.equals(from, other.from) && Objects.equals(highlightBuilder, other.highlightBuilder) && Objects.equals(indexBoost, other.indexBoost) - && Objects.equals(innerHitsBuilder, other.innerHitsBuilder) && Objects.equals(minScore, other.minScore) && Objects.equals(postQueryBuilder, other.postQueryBuilder) && Objects.equals(queryBuilder, other.queryBuilder) diff --git a/core/src/test/java/org/elasticsearch/index/query/HasChildQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/HasChildQueryBuilderTests.java index 538f39ac44c..36a135a9fb1 100644 --- a/core/src/test/java/org/elasticsearch/index/query/HasChildQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/HasChildQueryBuilderTests.java @@ -42,7 +42,6 @@ import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.Uid; import org.elasticsearch.index.mapper.internal.TypeFieldMapper; import org.elasticsearch.index.mapper.internal.UidFieldMapper; -import org.elasticsearch.index.query.support.InnerHitBuilder; import org.elasticsearch.index.similarity.SimilarityService; import org.elasticsearch.script.Script.ScriptParseException; import org.elasticsearch.search.fetch.innerhits.InnerHitsContext; @@ -53,6 +52,8 @@ import org.junit.BeforeClass; import java.io.IOException; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; @@ -125,18 +126,24 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase innerHitBuilders = new HashMap<>(); + InnerHitBuilder.extractInnerHits(queryBuilder, innerHitBuilders); + for (InnerHitBuilder builder : innerHitBuilders.values()) { + builder.build(searchContext, searchContext.innerHits()); + } + assertNotNull(searchContext.innerHits()); + assertEquals(1, searchContext.innerHits().getInnerHits().size()); + assertTrue(searchContext.innerHits().getInnerHits().containsKey(queryBuilder.innerHit().getName())); InnerHitsContext.BaseInnerHits innerHits = - SearchContext.current().innerHits().getInnerHits().get(queryBuilder.innerHit().getName()); + searchContext.innerHits().getInnerHits().get(queryBuilder.innerHit().getName()); assertEquals(innerHits.size(), queryBuilder.innerHit().getSize()); assertEquals(innerHits.sort().getSort().length, 1); assertEquals(innerHits.sort().getSort()[0].getField(), STRING_FIELD_NAME_2); } else { - assertThat(SearchContext.current().innerHits().getInnerHits().size(), equalTo(0)); + assertThat(searchContext.innerHits().getInnerHits().size(), equalTo(0)); } } } @@ -188,7 +195,6 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase innerHitBuilders = new HashMap<>(); + InnerHitBuilder.extractInnerHits(queryBuilder, innerHitBuilders); + for (InnerHitBuilder builder : innerHitBuilders.values()) { + builder.build(searchContext, searchContext.innerHits()); + } + assertNotNull(searchContext.innerHits()); + assertEquals(1, searchContext.innerHits().getInnerHits().size()); + assertTrue(searchContext.innerHits().getInnerHits().containsKey(queryBuilder.innerHit().getName())); + InnerHitsContext.BaseInnerHits innerHits = searchContext.innerHits() .getInnerHits().get(queryBuilder.innerHit().getName()); assertEquals(innerHits.size(), queryBuilder.innerHit().getSize()); assertEquals(innerHits.sort().getSort().length, 1); assertEquals(innerHits.sort().getSort()[0].getField(), STRING_FIELD_NAME_2); } else { - assertThat(SearchContext.current().innerHits().getInnerHits().size(), equalTo(0)); + assertThat(searchContext.innerHits().getInnerHits().size(), equalTo(0)); } } } diff --git a/core/src/test/java/org/elasticsearch/index/query/support/InnerHitBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java similarity index 55% rename from core/src/test/java/org/elasticsearch/index/query/support/InnerHitBuilderTests.java rename to core/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java index e85baee2324..a05093761d2 100644 --- a/core/src/test/java/org/elasticsearch/index/query/support/InnerHitBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java @@ -16,11 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.index.query.support; +package org.elasticsearch.index.query; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.sameInstance; +import static org.hamcrest.Matchers.nullValue; import java.io.IOException; import java.util.ArrayList; @@ -29,6 +31,7 @@ import java.util.List; import java.util.Map; import java.util.function.Supplier; +import org.apache.lucene.search.join.ScoreMode; import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput; @@ -41,8 +44,7 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; -import org.elasticsearch.index.query.MatchQueryBuilder; -import org.elasticsearch.index.query.QueryParseContext; +import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; import org.elasticsearch.indices.query.IndicesQueriesRegistry; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptService; @@ -87,7 +89,7 @@ public class InnerHitBuilderTests extends ESTestCase { public void testFromAndToXContent() throws Exception { for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) { - InnerHitBuilder innerHit = randomInnerHits(); + InnerHitBuilder innerHit = randomInnerHits(true, false); XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values())); if (randomBoolean()) { builder.prettyPrint(); @@ -111,7 +113,7 @@ public class InnerHitBuilderTests extends ESTestCase { assertTrue("inner it is not equal to self", firstInnerHit.equals(firstInnerHit)); assertThat("same inner hit's hashcode returns different values if called multiple times", firstInnerHit.hashCode(), equalTo(firstInnerHit.hashCode())); - assertThat("different inner hits should not be equal", mutate(firstInnerHit), not(equalTo(firstInnerHit))); + assertThat("different inner hits should not be equal", mutate(serializedCopy(firstInnerHit)), not(equalTo(firstInnerHit))); InnerHitBuilder secondBuilder = serializedCopy(firstInnerHit); assertTrue("inner hit is not equal to self", secondBuilder.equals(secondBuilder)); @@ -133,18 +135,83 @@ public class InnerHitBuilderTests extends ESTestCase { } } - public static InnerHitBuilder randomInnerHits() { - return randomInnerHits(true); + public void testInlineLeafInnerHitsNestedQuery() throws Exception { + InnerHitBuilder leafInnerHits = randomInnerHits(); + NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("path", new MatchAllQueryBuilder(), ScoreMode.None); + nestedQueryBuilder.innerHit(leafInnerHits); + Map innerHitBuilders = new HashMap<>(); + nestedQueryBuilder.extractInnerHitBuilders(innerHitBuilders); + assertThat(innerHitBuilders.get(leafInnerHits.getName()), notNullValue()); } - public static InnerHitBuilder randomInnerHits(boolean recursive) { - InnerHitBuilder innerHits = new InnerHitBuilder(); - if (randomBoolean()) { - innerHits.setNestedPath(randomAsciiOfLengthBetween(1, 16)); - } else { - innerHits.setParentChildType(randomAsciiOfLengthBetween(1, 16)); - } + public void testInlineLeafInnerHitsHasChildQuery() throws Exception { + InnerHitBuilder leafInnerHits = randomInnerHits(); + HasChildQueryBuilder hasChildQueryBuilder = new HasChildQueryBuilder("type", new MatchAllQueryBuilder(), ScoreMode.None) + .innerHit(leafInnerHits); + Map innerHitBuilders = new HashMap<>(); + hasChildQueryBuilder.extractInnerHitBuilders(innerHitBuilders); + assertThat(innerHitBuilders.get(leafInnerHits.getName()), notNullValue()); + } + public void testInlineLeafInnerHitsHasParentQuery() throws Exception { + InnerHitBuilder leafInnerHits = randomInnerHits(); + HasParentQueryBuilder hasParentQueryBuilder = new HasParentQueryBuilder("type", new MatchAllQueryBuilder(), false) + .innerHit(leafInnerHits); + Map innerHitBuilders = new HashMap<>(); + hasParentQueryBuilder.extractInnerHitBuilders(innerHitBuilders); + assertThat(innerHitBuilders.get(leafInnerHits.getName()), notNullValue()); + } + + public void testInlineLeafInnerHitsNestedQueryViaBoolQuery() { + InnerHitBuilder leafInnerHits = randomInnerHits(); + NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("path", new MatchAllQueryBuilder(), ScoreMode.None) + .innerHit(leafInnerHits); + BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder().should(nestedQueryBuilder); + Map innerHitBuilders = new HashMap<>(); + boolQueryBuilder.extractInnerHitBuilders(innerHitBuilders); + assertThat(innerHitBuilders.get(leafInnerHits.getName()), notNullValue()); + } + + public void testInlineLeafInnerHitsNestedQueryViaConstantScoreQuery() { + InnerHitBuilder leafInnerHits = randomInnerHits(); + NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("path", new MatchAllQueryBuilder(), ScoreMode.None) + .innerHit(leafInnerHits); + ConstantScoreQueryBuilder constantScoreQueryBuilder = new ConstantScoreQueryBuilder(nestedQueryBuilder); + Map innerHitBuilders = new HashMap<>(); + constantScoreQueryBuilder.extractInnerHitBuilders(innerHitBuilders); + assertThat(innerHitBuilders.get(leafInnerHits.getName()), notNullValue()); + } + + public void testInlineLeafInnerHitsNestedQueryViaBoostingQuery() { + InnerHitBuilder leafInnerHits1 = randomInnerHits(); + NestedQueryBuilder nestedQueryBuilder1 = new NestedQueryBuilder("path", new MatchAllQueryBuilder(), ScoreMode.None) + .innerHit(leafInnerHits1); + InnerHitBuilder leafInnerHits2 = randomInnerHits(); + NestedQueryBuilder nestedQueryBuilder2 = new NestedQueryBuilder("path", new MatchAllQueryBuilder(), ScoreMode.None) + .innerHit(leafInnerHits2); + BoostingQueryBuilder constantScoreQueryBuilder = new BoostingQueryBuilder(nestedQueryBuilder1, nestedQueryBuilder2); + Map innerHitBuilders = new HashMap<>(); + constantScoreQueryBuilder.extractInnerHitBuilders(innerHitBuilders); + assertThat(innerHitBuilders.get(leafInnerHits1.getName()), notNullValue()); + assertThat(innerHitBuilders.get(leafInnerHits2.getName()), notNullValue()); + } + + public void testInlineLeafInnerHitsNestedQueryViaFunctionScoreQuery() { + InnerHitBuilder leafInnerHits = randomInnerHits(); + NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("path", new MatchAllQueryBuilder(), ScoreMode.None) + .innerHit(leafInnerHits); + FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(nestedQueryBuilder); + Map innerHitBuilders = new HashMap<>(); + ((AbstractQueryBuilder) functionScoreQueryBuilder).extractInnerHitBuilders(innerHitBuilders); + assertThat(innerHitBuilders.get(leafInnerHits.getName()), notNullValue()); + } + + public static InnerHitBuilder randomInnerHits() { + return randomInnerHits(true, true); + } + + public static InnerHitBuilder randomInnerHits(boolean recursive, boolean includeQueryTypeOrPath) { + InnerHitBuilder innerHits = new InnerHitBuilder(); innerHits.setName(randomAsciiOfLengthBetween(1, 16)); innerHits.setFrom(randomIntBetween(0, 128)); innerHits.setSize(randomIntBetween(0, 128)); @@ -170,54 +237,76 @@ public class InnerHitBuilderTests extends ESTestCase { ); } innerHits.setHighlightBuilder(HighlightBuilderTests.randomHighlighterBuilder()); - if (randomBoolean()) { - innerHits.setQuery(new MatchQueryBuilder(randomAsciiOfLengthBetween(1, 16), randomAsciiOfLengthBetween(1, 16))); - } if (recursive && randomBoolean()) { - InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder(); int size = randomIntBetween(1, 16); for (int i = 0; i < size; i++) { - innerHitsBuilder.addInnerHit(randomAsciiOfLengthBetween(1, 16), randomInnerHits(false)); + innerHits.addChildInnerHit(randomInnerHits(false, includeQueryTypeOrPath)); } - innerHits.setInnerHitsBuilder(innerHitsBuilder); } - return innerHits; + if (includeQueryTypeOrPath) { + QueryBuilder query = new MatchQueryBuilder(randomAsciiOfLengthBetween(1, 16), randomAsciiOfLengthBetween(1, 16)); + if (randomBoolean()) { + return new InnerHitBuilder(innerHits, randomAsciiOfLength(8), query); + } else { + return new InnerHitBuilder(innerHits, query, randomAsciiOfLength(8)); + } + } else { + return innerHits; + } } - static InnerHitBuilder mutate(InnerHitBuilder innerHits) throws IOException { - InnerHitBuilder copy = serializedCopy(innerHits); - int surprise = randomIntBetween(0, 10); + public void testCopyConstructor() throws Exception { + InnerHitBuilder original = randomInnerHits(); + InnerHitBuilder copy = original.getNestedPath() != null ? + new InnerHitBuilder(original, original.getNestedPath(), original.getQuery()) : + new InnerHitBuilder(original, original.getQuery(), original.getParentChildType()); + assertThat(copy, equalTo(original)); + copy = mutate(copy); + assertThat(copy, not(equalTo(original))); + } + + static InnerHitBuilder mutate(InnerHitBuilder instance) throws IOException { + int surprise = randomIntBetween(0, 11); switch (surprise) { case 0: - copy.setFrom(randomValueOtherThan(innerHits.getFrom(), () -> randomIntBetween(0, 128))); + instance.setFrom(randomValueOtherThan(instance.getFrom(), () -> randomIntBetween(0, 128))); break; case 1: - copy.setSize(randomValueOtherThan(innerHits.getSize(), () -> randomIntBetween(0, 128))); + instance.setSize(randomValueOtherThan(instance.getSize(), () -> randomIntBetween(0, 128))); break; case 2: - copy.setExplain(!copy.isExplain()); + instance.setExplain(!instance.isExplain()); break; case 3: - copy.setVersion(!copy.isVersion()); + instance.setVersion(!instance.isVersion()); break; case 4: - copy.setTrackScores(!copy.isTrackScores()); + instance.setTrackScores(!instance.isTrackScores()); break; case 5: - copy.setName(randomValueOtherThan(innerHits.getName(), () -> randomAsciiOfLengthBetween(1, 16))); + instance.setName(randomValueOtherThan(instance.getName(), () -> randomAsciiOfLengthBetween(1, 16))); break; case 6: - copy.setFieldDataFields(randomValueOtherThan(copy.getFieldDataFields(), () -> { - return randomListStuff(16, () -> randomAsciiOfLengthBetween(1, 16)); - })); + if (randomBoolean()) { + instance.setFieldDataFields(randomValueOtherThan(instance.getFieldDataFields(), () -> { + return randomListStuff(16, () -> randomAsciiOfLengthBetween(1, 16)); + })); + } else { + instance.addFieldDataField(randomAsciiOfLengthBetween(1, 16)); + } break; case 7: - copy.setScriptFields(randomValueOtherThan(copy.getScriptFields(), () -> { - return randomListStuff(16, InnerHitBuilderTests::randomScript);})); + if (randomBoolean()) { + instance.setScriptFields(randomValueOtherThan(instance.getScriptFields(), () -> { + return randomListStuff(16, InnerHitBuilderTests::randomScript);})); + } else { + SearchSourceBuilder.ScriptField script = randomScript(); + instance.addScriptField(script.fieldName(), script.script()); + } break; case 8: - copy.setFetchSourceContext(randomValueOtherThan(copy.getFetchSourceContext(), () -> { + instance.setFetchSourceContext(randomValueOtherThan(instance.getFetchSourceContext(), () -> { FetchSourceContext randomFetchSourceContext; if (randomBoolean()) { randomFetchSourceContext = new FetchSourceContext(randomBoolean()); @@ -231,21 +320,34 @@ public class InnerHitBuilderTests extends ESTestCase { })); break; case 9: - final List> sortBuilders = randomValueOtherThan(copy.getSorts(), () -> { - List> builders = randomListStuff(16, - () -> SortBuilders.fieldSort(randomAsciiOfLengthBetween(5, 20)).order(randomFrom(SortOrder.values()))); - return builders; - }); - copy.setSorts(sortBuilders); + if (randomBoolean()) { + final List> sortBuilders = randomValueOtherThan(instance.getSorts(), () -> { + List> builders = randomListStuff(16, + () -> SortBuilders.fieldSort(randomAsciiOfLengthBetween(5, 20)).order(randomFrom(SortOrder.values()))); + return builders; + }); + instance.setSorts(sortBuilders); + } else { + instance.addSort(SortBuilders.fieldSort(randomAsciiOfLengthBetween(5, 20))); + } break; case 10: - copy.setHighlightBuilder(randomValueOtherThan(copy.getHighlightBuilder(), + instance.setHighlightBuilder(randomValueOtherThan(instance.getHighlightBuilder(), HighlightBuilderTests::randomHighlighterBuilder)); break; + case 11: + if (instance.getFieldNames() == null || randomBoolean()) { + instance.setFieldNames(randomValueOtherThan(instance.getFieldNames(), () -> { + return randomListStuff(16, () -> randomAsciiOfLengthBetween(1, 16)); + })); + } else { + instance.getFieldNames().add(randomAsciiOfLengthBetween(1, 16)); + } + break; default: throw new IllegalStateException("unexpected surprise [" + surprise + "]"); } - return copy; + return instance; } static SearchSourceBuilder.ScriptField randomScript() { diff --git a/core/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java index 137778a45e9..b35201f47a1 100644 --- a/core/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java @@ -21,20 +21,25 @@ package org.elasticsearch.index.query; import com.carrotsearch.randomizedtesting.generators.RandomPicks; +import com.fasterxml.jackson.core.JsonParseException; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.search.join.ToParentBlockJoinQuery; +import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; +import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.index.mapper.MapperService; -import org.elasticsearch.index.query.support.InnerHitBuilder; +import org.elasticsearch.script.Script; import org.elasticsearch.search.fetch.innerhits.InnerHitsContext; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.SortOrder; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; @@ -66,11 +71,11 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase innerHitBuilders = new HashMap<>(); + InnerHitBuilder.extractInnerHits(queryBuilder, innerHitBuilders); + for (InnerHitBuilder builder : innerHitBuilders.values()) { + builder.build(searchContext, searchContext.innerHits()); + } + assertNotNull(searchContext.innerHits()); + assertEquals(1, searchContext.innerHits().getInnerHits().size()); + assertTrue(searchContext.innerHits().getInnerHits().containsKey(queryBuilder.innerHit().getName())); + InnerHitsContext.BaseInnerHits innerHits = searchContext.innerHits().getInnerHits().get(queryBuilder.innerHit().getName()); + assertEquals(innerHits.size(), queryBuilder.innerHit().getSize()); assertEquals(innerHits.sort().getSort().length, 1); - assertEquals(innerHits.sort().getSort()[0].getField(), STRING_FIELD_NAME); + assertEquals(innerHits.sort().getSort()[0].getField(), INT_FIELD_NAME); } else { - assertThat(SearchContext.current().innerHits().getInnerHits().size(), equalTo(0)); + assertThat(searchContext.innerHits().getInnerHits().size(), equalTo(0)); } } } @@ -163,6 +174,36 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase> nodePlugins() { @@ -112,105 +107,62 @@ public class InnerHitsIT extends ESIntegTestCase { .endObject())); indexRandom(true, requests); - InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder(); - innerHitsBuilder.addInnerHit("comment", new InnerHitBuilder() - .setNestedPath("comments") - .setQuery(matchQuery("comments.message", "fox")) - ); - // Inner hits can be defined in two ways: 1) with the query 2) as separate inner_hit definition - SearchRequest[] searchRequests = new SearchRequest[]{ - client().prepareSearch("articles").setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit( - new InnerHitBuilder().setName("comment"))).request(), - client().prepareSearch("articles").setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg)) - .innerHits(innerHitsBuilder).request() - }; - for (SearchRequest searchRequest : searchRequests) { - SearchResponse response = client().search(searchRequest).actionGet(); - 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.totalHits(), 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)); - } + SearchResponse response = client().prepareSearch("articles") + .setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg) + .innerHit(new InnerHitBuilder().setName("comment")) + ).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.totalHits(), 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)); - innerHitsBuilder = new InnerHitsBuilder(); - innerHitsBuilder.addInnerHit("comment", new InnerHitBuilder() - .setQuery(matchQuery("comments.message", "elephant")).setNestedPath("comments") - ); - // Inner hits can be defined in two ways: 1) with the query 2) as - // separate inner_hit definition - searchRequests = new SearchRequest[] { - client().prepareSearch("articles") - .setQuery(nestedQuery("comments", matchQuery("comments.message", "elephant"), ScoreMode.Avg)) - .innerHits(innerHitsBuilder).request(), - client().prepareSearch("articles") - .setQuery(nestedQuery("comments", matchQuery("comments.message", "elephant"), ScoreMode.Avg).innerHit(new InnerHitBuilder().setName("comment"))).request(), - client().prepareSearch("articles") - .setQuery(nestedQuery("comments", matchQuery("comments.message", "elephant"), ScoreMode.Avg).innerHit(new InnerHitBuilder().setName("comment").addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC)))).request() - }; - for (SearchRequest searchRequest : searchRequests) { - SearchResponse response = client().search(searchRequest).actionGet(); - 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)); - SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.totalHits(), 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)); - } - InnerHitBuilder innerHit = new InnerHitBuilder(); - innerHit.setNestedPath("comments"); - innerHit.setQuery(matchQuery("comments.message", "fox")); - innerHit.setHighlightBuilder(new HighlightBuilder().field("comments.message")); - innerHit.setExplain(true); - innerHit.addFieldDataField("comments.message"); - innerHit.addScriptField("script", new Script("5", ScriptService.ScriptType.INLINE, MockScriptEngine.NAME, Collections.emptyMap())); - innerHit.setSize(1); - innerHitsBuilder = new InnerHitsBuilder(); - innerHitsBuilder.addInnerHit("comments", innerHit); - searchRequests = new SearchRequest[] { - client().prepareSearch("articles") - .setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg)) - .innerHits(innerHitsBuilder).request(), - client().prepareSearch("articles") - .setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit( - new InnerHitBuilder().setHighlightBuilder(new HighlightBuilder().field("comments.message")) - .setExplain(true) - .addFieldDataField("comments.message") - .addScriptField("script", new Script("5", ScriptService.ScriptType.INLINE, MockScriptEngine.NAME, Collections.emptyMap())) - .setSize(1) - )).request() - }; + response = client().prepareSearch("articles") + .setQuery(nestedQuery("comments", matchQuery("comments.message", "elephant"), ScoreMode.Avg) + .innerHit(new InnerHitBuilder().setName("comment")) + ).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.totalHits(), 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)); - for (SearchRequest searchRequest : searchRequests) { - SearchResponse response = client().search(searchRequest).actionGet(); - assertNoFailures(response); - SearchHits 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("fox eat quick")); - assertThat(innerHits.getAt(0).explanation().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")); - } + response = client().prepareSearch("articles") + .setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit( + new InnerHitBuilder().setHighlightBuilder(new HighlightBuilder().field("comments.message")) + .setExplain(true) + .addFieldDataField("comments.message") + .addScriptField("script", new Script("5", ScriptService.ScriptType.INLINE, MockScriptEngine.NAME, Collections.emptyMap())) + .setSize(1) + )).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("fox eat quick")); + assertThat(innerHits.getAt(0).explanation().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 { @@ -237,38 +189,16 @@ public class InnerHitsIT extends ESIntegTestCase { indexRandom(true, requestBuilders); int size = randomIntBetween(0, numDocs); - SearchResponse searchResponse; - if (randomBoolean()) { - InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder(); - innerHitsBuilder.addInnerHit("a", new InnerHitBuilder().setNestedPath("field1") - // Sort order is DESC, because we reverse the inner objects during indexing! - .addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC)).setSize(size)); - innerHitsBuilder.addInnerHit("b", new InnerHitBuilder().setNestedPath("field2") - .addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC)).setSize(size)); - searchResponse = client().prepareSearch("idx") - .setSize(numDocs) - .addSort("_uid", SortOrder.ASC) - .innerHits(innerHitsBuilder) - .get(); - } else { - BoolQueryBuilder boolQuery = new BoolQueryBuilder(); - if (randomBoolean()) { - boolQuery.should(nestedQuery("field1", matchAllQuery(), ScoreMode.Avg).innerHit(new InnerHitBuilder().setName("a").setSize(size) - .addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC)))); - boolQuery.should(nestedQuery("field2", matchAllQuery(), ScoreMode.Avg).innerHit(new InnerHitBuilder().setName("b") - .addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC)).setSize(size))); - } else { - boolQuery.should(constantScoreQuery(nestedQuery("field1", matchAllQuery(), ScoreMode.Avg).innerHit(new InnerHitBuilder().setName("a") - .setSize(size).addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC))))); - boolQuery.should(constantScoreQuery(nestedQuery("field2", matchAllQuery(), ScoreMode.Avg).innerHit(new InnerHitBuilder().setName("b") - .setSize(size).addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC))))); - } - searchResponse = client().prepareSearch("idx") - .setQuery(boolQuery) - .setSize(numDocs) - .addSort("_uid", SortOrder.ASC) - .get(); - } + 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)))); + boolQuery.should(nestedQuery("field2", matchAllQuery(), ScoreMode.Avg).innerHit(new InnerHitBuilder().setName("b") + .addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC)).setSize(size))); + SearchResponse searchResponse = client().prepareSearch("idx") + .setQuery(boolQuery) + .setSize(numDocs) + .addSort("_uid", SortOrder.ASC) + .get(); assertNoFailures(searchResponse); assertHitCount(searchResponse, numDocs); @@ -313,102 +243,59 @@ public class InnerHitsIT extends ESIntegTestCase { requests.add(client().prepareIndex("articles", "comment", "6").setParent("2").setSource("message", "elephant scared by mice x y")); indexRandom(true, requests); - InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder(); - innerHitsBuilder.addInnerHit("comment", new InnerHitBuilder().setParentChildType("comment") - .setQuery(matchQuery("message", "fox"))); - SearchRequest[] searchRequests = new SearchRequest[]{ - client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None)) - .innerHits(innerHitsBuilder) - .request(), - client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit(new InnerHitBuilder().setName("comment"))) - .request() - }; - for (SearchRequest searchRequest : searchRequests) { - SearchResponse response = client().search(searchRequest).actionGet(); - assertNoFailures(response); - assertHitCount(response, 1); - assertSearchHit(response, 1, hasId("1")); - assertThat(response.getHits().getAt(0).getShard(), notNullValue()); + SearchResponse response = client().prepareSearch("articles") + .setQuery(hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit(new InnerHitBuilder())) + .get(); + assertNoFailures(response); + assertHitCount(response, 1); + assertSearchHit(response, 1, hasId("1")); + assertThat(response.getHits().getAt(0).getShard(), notNullValue()); - assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); - SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.totalHits(), equalTo(2L)); + assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); + SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.totalHits(), equalTo(2L)); - assertThat(innerHits.getAt(0).getId(), equalTo("1")); - assertThat(innerHits.getAt(0).type(), equalTo("comment")); - assertThat(innerHits.getAt(1).getId(), equalTo("2")); - assertThat(innerHits.getAt(1).type(), equalTo("comment")); - } + assertThat(innerHits.getAt(0).getId(), equalTo("1")); + assertThat(innerHits.getAt(0).type(), equalTo("comment")); + assertThat(innerHits.getAt(1).getId(), equalTo("2")); + assertThat(innerHits.getAt(1).type(), equalTo("comment")); - innerHitsBuilder = new InnerHitsBuilder(); - innerHitsBuilder.addInnerHit("comment", new InnerHitBuilder().setParentChildType("comment") - .setQuery(matchQuery("message", "elephant"))); - searchRequests = new SearchRequest[] { - client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", matchQuery("message", "elephant"), ScoreMode.None)) - .innerHits(innerHitsBuilder) - .request(), - client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", matchQuery("message", "elephant"), ScoreMode.None).innerHit(new InnerHitBuilder())) - .request() - }; - for (SearchRequest searchRequest : searchRequests) { - SearchResponse response = client().search(searchRequest).actionGet(); - assertNoFailures(response); - assertHitCount(response, 1); - assertSearchHit(response, 1, hasId("2")); + response = client().prepareSearch("articles") + .setQuery(hasChildQuery("comment", matchQuery("message", "elephant"), ScoreMode.None).innerHit(new InnerHitBuilder())) + .get(); + assertNoFailures(response); + assertHitCount(response, 1); + assertSearchHit(response, 1, hasId("2")); - assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); - SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.totalHits(), equalTo(3L)); + assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); + innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.totalHits(), equalTo(3L)); - assertThat(innerHits.getAt(0).getId(), equalTo("4")); - assertThat(innerHits.getAt(0).type(), equalTo("comment")); - assertThat(innerHits.getAt(1).getId(), equalTo("5")); - assertThat(innerHits.getAt(1).type(), equalTo("comment")); - assertThat(innerHits.getAt(2).getId(), equalTo("6")); - assertThat(innerHits.getAt(2).type(), equalTo("comment")); - } - InnerHitBuilder innerHit = new InnerHitBuilder(); - innerHit.setQuery(matchQuery("message", "fox")); - innerHit.setParentChildType("comment"); - innerHit.setHighlightBuilder(new HighlightBuilder().field("message")); - innerHit.setExplain(true); - innerHit.addFieldDataField("message"); - innerHit.addScriptField("script", new Script("5", ScriptService.ScriptType.INLINE, MockScriptEngine.NAME, Collections.emptyMap())); - innerHit.setSize(1); - innerHitsBuilder = new InnerHitsBuilder(); - innerHitsBuilder.addInnerHit("comment", innerHit); - searchRequests = new SearchRequest[] { - client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None)) - .innerHits(innerHitsBuilder) - .request(), + assertThat(innerHits.getAt(0).getId(), equalTo("4")); + assertThat(innerHits.getAt(0).type(), equalTo("comment")); + assertThat(innerHits.getAt(1).getId(), equalTo("5")); + assertThat(innerHits.getAt(1).type(), equalTo("comment")); + assertThat(innerHits.getAt(2).getId(), equalTo("6")); + assertThat(innerHits.getAt(2).type(), equalTo("comment")); - client().prepareSearch("articles") - .setQuery( - hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit( - new InnerHitBuilder() - .addFieldDataField("message") - .setHighlightBuilder(new HighlightBuilder().field("message")) - .setExplain(true).setSize(1) - .addScriptField("script", new Script("5", ScriptService.ScriptType.INLINE, - MockScriptEngine.NAME, Collections.emptyMap())) - ) - ).request() }; - - for (SearchRequest searchRequest : searchRequests) { - SearchResponse response = client().search(searchRequest).actionGet(); - assertNoFailures(response); - SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.getHits().length, equalTo(1)); - assertThat(innerHits.getAt(0).getHighlightFields().get("message").getFragments()[0].string(), equalTo("fox eat quick")); - assertThat(innerHits.getAt(0).explanation().toString(), containsString("weight(message:fox")); - assertThat(innerHits.getAt(0).getFields().get("message").getValue().toString(), equalTo("eat")); - assertThat(innerHits.getAt(0).getFields().get("script").getValue().toString(), equalTo("5")); - } + response = client().prepareSearch("articles") + .setQuery( + hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit( + new InnerHitBuilder() + .addFieldDataField("message") + .setHighlightBuilder(new HighlightBuilder().field("message")) + .setExplain(true).setSize(1) + .addScriptField("script", new Script("5", ScriptService.ScriptType.INLINE, + MockScriptEngine.NAME, Collections.emptyMap())) + ) + ).get(); + assertNoFailures(response); + innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getHits().length, equalTo(1)); + assertThat(innerHits.getAt(0).getHighlightFields().get("message").getFragments()[0].string(), equalTo("fox eat quick")); + assertThat(innerHits.getAt(0).explanation().toString(), containsString("weight(message:fox")); + assertThat(innerHits.getAt(0).getFields().get("message").getValue().toString(), equalTo("eat")); + assertThat(innerHits.getAt(0).getFields().get("script").getValue().toString(), equalTo("5")); } public void testRandomParentChild() throws Exception { @@ -442,33 +329,17 @@ public class InnerHitsIT extends ESIntegTestCase { indexRandom(true, requestBuilders); int size = randomIntBetween(0, numDocs); - InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder(); - innerHitsBuilder.addInnerHit("a", new InnerHitBuilder().setParentChildType("child1").addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size)); - innerHitsBuilder.addInnerHit("b", new InnerHitBuilder().setParentChildType("child2").addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size)); - SearchResponse searchResponse; - if (randomBoolean()) { - searchResponse = client().prepareSearch("idx") - .setSize(numDocs) - .setTypes("parent") - .addSort("_uid", SortOrder.ASC) - .innerHits(innerHitsBuilder) - .get(); - } else { - BoolQueryBuilder boolQuery = new BoolQueryBuilder(); - if (randomBoolean()) { - boolQuery.should(hasChildQuery("child1", matchAllQuery(), ScoreMode.None).innerHit(new InnerHitBuilder().setName("a").addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size))); - boolQuery.should(hasChildQuery("child2", matchAllQuery(), ScoreMode.None).innerHit(new InnerHitBuilder().setName("b").addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size))); - } else { - boolQuery.should(constantScoreQuery(hasChildQuery("child1", matchAllQuery(), ScoreMode.None).innerHit(new InnerHitBuilder().setName("a").addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size)))); - boolQuery.should(constantScoreQuery(hasChildQuery("child2", matchAllQuery(), ScoreMode.None).innerHit(new InnerHitBuilder().setName("b").addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size)))); - } - searchResponse = client().prepareSearch("idx") - .setSize(numDocs) - .setTypes("parent") - .addSort("_uid", SortOrder.ASC) - .setQuery(boolQuery) - .get(); - } + BoolQueryBuilder boolQuery = new BoolQueryBuilder(); + boolQuery.should(constantScoreQuery(hasChildQuery("child1", matchAllQuery(), ScoreMode.None) + .innerHit(new InnerHitBuilder().setName("a").addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size)))); + boolQuery.should(constantScoreQuery(hasChildQuery("child2", matchAllQuery(), ScoreMode.None) + .innerHit(new InnerHitBuilder().setName("b").addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size)))); + SearchResponse searchResponse = client().prepareSearch("idx") + .setSize(numDocs) + .setTypes("parent") + .addSort("_uid", SortOrder.ASC) + .setQuery(boolQuery) + .get(); assertNoFailures(searchResponse); assertHitCount(searchResponse, numDocs); @@ -560,19 +431,10 @@ public class InnerHitsIT extends ESIntegTestCase { requests.add(client().prepareIndex("articles", "remark", "2").setParent("2").setRouting("2").setSource("message", "bad")); indexRandom(true, requests); - InnerHitsBuilder innerInnerHitsBuilder = new InnerHitsBuilder(); - innerInnerHitsBuilder.addInnerHit("remark", new InnerHitBuilder() - .setParentChildType("remark") - .setQuery(matchQuery("message", "good")) - ); - InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder(); - innerHitsBuilder.addInnerHit("comment", new InnerHitBuilder() - .setParentChildType("comment") - .setQuery(hasChildQuery("remark", matchQuery("message", "good"), ScoreMode.None)) - .setInnerHitsBuilder(innerInnerHitsBuilder)); SearchResponse response = client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", hasChildQuery("remark", matchQuery("message", "good"), ScoreMode.None), ScoreMode.None)) - .innerHits(innerHitsBuilder) + .setQuery(hasChildQuery("comment", + hasChildQuery("remark", matchQuery("message", "good"), ScoreMode.None).innerHit(new InnerHitBuilder()), + ScoreMode.None).innerHit(new InnerHitBuilder())) .get(); assertNoFailures(response); @@ -590,18 +452,10 @@ public class InnerHitsIT extends ESIntegTestCase { assertThat(innerHits.getAt(0).getId(), equalTo("1")); assertThat(innerHits.getAt(0).type(), equalTo("remark")); - innerInnerHitsBuilder = new InnerHitsBuilder(); - innerInnerHitsBuilder.addInnerHit("remark", new InnerHitBuilder() - .setParentChildType("remark") - .setQuery(matchQuery("message", "bad"))); - innerHitsBuilder = new InnerHitsBuilder(); - innerHitsBuilder.addInnerHit("comment", new InnerHitBuilder() - .setParentChildType("comment") - .setQuery(hasChildQuery("remark", matchQuery("message", "bad"), ScoreMode.None)) - .setInnerHitsBuilder(innerInnerHitsBuilder)); response = client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", hasChildQuery("remark", matchQuery("message", "bad"), ScoreMode.None), ScoreMode.None)) - .innerHits(innerHitsBuilder) + .setQuery(hasChildQuery("comment", + hasChildQuery("remark", matchQuery("message", "bad"), ScoreMode.None).innerHit(new InnerHitBuilder()), + ScoreMode.None).innerHit(new InnerHitBuilder())) .get(); assertNoFailures(response); @@ -662,24 +516,18 @@ public class InnerHitsIT extends ESIntegTestCase { .endObject())); indexRandom(true, requests); - InnerHitsBuilder innerInnerHitsBuilder = new InnerHitsBuilder(); - innerInnerHitsBuilder.addInnerHit("remark", new InnerHitBuilder() - .setNestedPath("comments.remarks") - .setQuery(matchQuery("comments.remarks.message", "good"))); - InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder(); - innerHitsBuilder.addInnerHit("comment", new InnerHitBuilder() - .setNestedPath("comments") - .setQuery(nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "good"), ScoreMode.Avg)) - .setInnerHitsBuilder(innerInnerHitsBuilder) - ); SearchResponse response = client().prepareSearch("articles") - .setQuery(nestedQuery("comments", nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "good"), ScoreMode.Avg), ScoreMode.Avg)) - .innerHits(innerHitsBuilder).get(); + .setQuery( + nestedQuery("comments", + nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "good"), ScoreMode.Avg) + .innerHit(new InnerHitBuilder().setName("remark")), + ScoreMode.Avg).innerHit(new InnerHitBuilder()) + ).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"); + SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comments"); assertThat(innerHits.totalHits(), equalTo(1L)); assertThat(innerHits.getHits().length, equalTo(1)); assertThat(innerHits.getAt(0).getId(), equalTo("1")); @@ -711,24 +559,18 @@ public class InnerHitsIT extends ESIntegTestCase { assertThat(innerHits.getAt(0).getNestedIdentity().getChild().getField().string(), equalTo("remarks")); assertThat(innerHits.getAt(0).getNestedIdentity().getChild().getOffset(), equalTo(0)); - innerInnerHitsBuilder = new InnerHitsBuilder(); - innerInnerHitsBuilder.addInnerHit("remark", new InnerHitBuilder() - .setNestedPath("comments.remarks") - .setQuery(matchQuery("comments.remarks.message", "bad"))); - innerHitsBuilder = new InnerHitsBuilder(); - innerHitsBuilder.addInnerHit("comment", new InnerHitBuilder() - .setNestedPath("comments") - .setQuery(nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "bad"), ScoreMode.Avg)) - .setInnerHitsBuilder(innerInnerHitsBuilder)); response = client().prepareSearch("articles") - .setQuery(nestedQuery("comments", nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "bad"), ScoreMode.Avg), ScoreMode.Avg)) - .innerHits(innerHitsBuilder) - .get(); + .setQuery( + nestedQuery("comments", + nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "bad"), ScoreMode.Avg) + .innerHit(new InnerHitBuilder().setName("remark")), + ScoreMode.Avg).innerHit(new InnerHitBuilder()) + ).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("comment"); + innerHits = response.getHits().getAt(0).getInnerHits().get("comments"); assertThat(innerHits.totalHits(), equalTo(1L)); assertThat(innerHits.getHits().length, equalTo(1)); assertThat(innerHits.getAt(0).getId(), equalTo("2")); @@ -863,22 +705,21 @@ public class InnerHitsIT extends ESIntegTestCase { requests.add(client().prepareIndex("royals", "baron", "baron4").setParent("earl4").setRouting("king").setSource("{}")); indexRandom(true, requests); - InnerHitsBuilder innerInnerHitsBuilder = new InnerHitsBuilder(); - innerInnerHitsBuilder.addInnerHit("barons", new InnerHitBuilder().setParentChildType("baron")); - InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder(); - innerHitsBuilder.addInnerHit("earls", new InnerHitBuilder() - .setParentChildType("earl") - .addSort(SortBuilders.fieldSort("_uid").order(SortOrder.ASC)) - .setSize(4) - .setInnerHitsBuilder(innerInnerHitsBuilder) - ); - innerInnerHitsBuilder = new InnerHitsBuilder(); - innerInnerHitsBuilder.addInnerHit("kings", new InnerHitBuilder().setParentChildType("king")); - innerHitsBuilder.addInnerHit("princes", new InnerHitBuilder().setParentChildType("prince") - .setInnerHitsBuilder(innerInnerHitsBuilder)); SearchResponse response = client().prepareSearch("royals") .setTypes("duke") - .innerHits(innerHitsBuilder) + .setQuery(boolQuery() + .filter(hasParentQuery("prince", + hasParentQuery("king", matchAllQuery(), false).innerHit(new InnerHitBuilder().setName("kings")), + false).innerHit(new InnerHitBuilder().setName("princes")) + ) + .filter(hasChildQuery("earl", + hasChildQuery("baron", matchAllQuery(), ScoreMode.None).innerHit(new InnerHitBuilder().setName("barons")), + ScoreMode.None).innerHit(new InnerHitBuilder() + .addSort(SortBuilders.fieldSort("_uid").order(SortOrder.ASC)) + .setName("earls") + .setSize(4)) + ) + ) .get(); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("duke")); @@ -1086,25 +927,4 @@ public class InnerHitsIT extends ESIntegTestCase { assertHitCount(response, 1); } - public void testTopLevelInnerHitsWithQueryInnerHits() throws Exception { - // top level inner hits shouldn't overwrite query inner hits definitions - - assertAcked(prepareCreate("index1").addMapping("child", "_parent", "type=parent")); - List requests = new ArrayList<>(); - requests.add(client().prepareIndex("index1", "parent", "1").setSource("{}")); - requests.add(client().prepareIndex("index1", "child", "2").setParent("1").setSource("{}")); - indexRandom(true, requests); - - InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder(); - innerHitsBuilder.addInnerHit("my-inner-hit", new InnerHitBuilder().setParentChildType("child")); - SearchResponse response = client().prepareSearch("index1") - .setQuery(hasChildQuery("child", new MatchAllQueryBuilder(), ScoreMode.None).innerHit(new InnerHitBuilder())) - .innerHits(innerHitsBuilder) - .get(); - assertHitCount(response, 1); - assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(2)); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getId(), equalTo("2")); - assertThat(response.getHits().getAt(0).getInnerHits().get("my-inner-hit").getAt(0).getId(), equalTo("2")); - } - } diff --git a/docs/reference/migration/migrate_5_0/search.asciidoc b/docs/reference/migration/migrate_5_0/search.asciidoc index 2d22ee1c1c3..d279ba0525e 100644 --- a/docs/reference/migration/migrate_5_0/search.asciidoc +++ b/docs/reference/migration/migrate_5_0/search.asciidoc @@ -151,5 +151,6 @@ specifying the sort order with the `order` option. ==== Inner hits -* The format of top level inner hits has been changed to be more readable. All options are now set on the same level. - So the `path` and `type` options are specified on the same level where `query` and other options are specified. +* Top level inner hits syntax has been removed. Inner hits can now only be specified as part of the `nested`, +`has_child` and `has_parent` queries. Use cases previously only possible with top level inner hits can now be done +with inner hits defined inside the query dsl. diff --git a/docs/reference/search/request/inner-hits.asciidoc b/docs/reference/search/request/inner-hits.asciidoc index b79f2dbd570..7fe200292a9 100644 --- a/docs/reference/search/request/inner-hits.asciidoc +++ b/docs/reference/search/request/inner-hits.asciidoc @@ -226,78 +226,4 @@ An example of a response snippet that could be generated from the above search r } }, ... --------------------------------------------------- - -[[top-level-inner-hits]] -==== top level inner hits - -Besides defining inner hits on query and filters, inner hits can also be defined as a top level construct alongside the -`query` and `aggregations` definition. The main reason for using the top level inner hits definition is to let the -inner hits return documents that don't match with the main query. Also inner hits definitions can be nested via the -top level notation. Other than that, the inner hit definition inside the query should be used because that is the most -compact way for defining inner hits. - -The following snippet explains the basic structure of inner hits defined at the top level of the search request body: - -[source,js] --------------------------------------------------- -"inner_hits" : { - "" : { - "" : { - "" : { - - [,"inner_hits" : { []+ } ]? - } - } - } - [,"" : { ... } ]* -} --------------------------------------------------- - -Inside the `inner_hits` definition, first the name of the inner hit is defined then whether the inner_hit -is a nested by defining `path` or a parent/child based definition by defining `type`. The next object layer contains -the name of the nested object field if the inner_hits is nested or the parent or child type if the inner_hit definition -is parent/child based. - -Multiple inner hit definitions can be defined in a single request. In the `` any option for features -that `inner_hits` support can be defined. Optionally another `inner_hits` definition can be defined in the ``. - -An example that shows the use of nested inner hits via the top level notation: - -[source,js] --------------------------------------------------- -{ - "query" : { - "nested" : { - "path" : "comments", - "query" : { - "match" : {"comments.message" : "[actual query]"} - } - } - }, - "inner_hits" : { - "comment" : { <1> - "path" : "comments", <2> - "query" : { - "match" : {"comments.message" : "[different query]"} <3> - } - } - } -} --------------------------------------------------- - -<1> The inner hit definition with the name `comment`. -<2> The path option refers to the nested object field `comments` -<3> A query that runs to collect the nested inner documents for each search hit returned. If no query is defined all nested - inner documents will be included belonging to a search hit. This shows that it only make sense to the top level - inner hit definition if no query or a different query is specified. - -Additional options that are only available when using the top level inner hits notation: - -[horizontal] -`path`:: Defines the nested scope where hits will be collected from. -`type`:: Defines the parent or child type score where hits will be collected from. -`query`:: Defines the query that will run in the defined nested, parent or child scope to collect and score hits. By default all document in the scope will be matched. - -Either `path` or `type` must be defined. The `path` or `type` defines the scope from where hits are fetched and -used as inner hits. +-------------------------------------------------- \ No newline at end of file