/*
* 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;
import org.elasticsearch.Version;
import org.elasticsearch.action.support.ToXContentToBytes;
import org.elasticsearch.common.ParseField;
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.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.mapper.ObjectMapper;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder.ScriptField;
import org.elasticsearch.search.fetch.StoredFieldsContext;
import org.elasticsearch.search.fetch.subphase.DocValueFieldsContext;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.fetch.subphase.InnerHitsContext;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.search.sort.SortAndFormats;
import org.elasticsearch.search.sort.SortBuilder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
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 IGNORE_UNMAPPED = new ParseField("ignore_unmapped");
public static final ParseField INNER_HITS_FIELD = new ParseField("inner_hits");
public static final QueryBuilder DEFAULT_INNER_HIT_QUERY = new MatchAllQueryBuilder();
private static final ObjectParser<InnerHitBuilder, QueryParseContext> PARSER = new ObjectParser<>("inner_hits", InnerHitBuilder::new);
static {
PARSER.declareString(InnerHitBuilder::setName, NAME_FIELD);
PARSER.declareBoolean((innerHitBuilder, value) -> innerHitBuilder.ignoreUnmapped = value, IGNORE_UNMAPPED);
PARSER.declareInt(InnerHitBuilder::setFrom, SearchSourceBuilder.FROM_FIELD);
PARSER.declareInt(InnerHitBuilder::setSize, SearchSourceBuilder.SIZE_FIELD);
PARSER.declareBoolean(InnerHitBuilder::setExplain, SearchSourceBuilder.EXPLAIN_FIELD);
PARSER.declareBoolean(InnerHitBuilder::setVersion, SearchSourceBuilder.VERSION_FIELD);
PARSER.declareBoolean(InnerHitBuilder::setTrackScores, SearchSourceBuilder.TRACK_SCORES_FIELD);
PARSER.declareStringArray(InnerHitBuilder::setStoredFieldNames, SearchSourceBuilder.STORED_FIELDS_FIELD);
PARSER.declareField((p, i, c) -> {
throw new ParsingException(p.getTokenLocation(), "The field [" +
SearchSourceBuilder.FIELDS_FIELD + "] is no longer supported, please use [" +
SearchSourceBuilder.STORED_FIELDS_FIELD + "] to retrieve stored fields or _source filtering " +
"if the field is not stored");
}, SearchSourceBuilder.FIELDS_FIELD, ObjectParser.ValueType.STRING_ARRAY);
PARSER.declareStringArray(InnerHitBuilder::setDocValueFields, SearchSourceBuilder.DOCVALUE_FIELDS_FIELD);
PARSER.declareField((p, i, c) -> {
try {
Set<ScriptField> scriptFields = new HashSet<>();
for (XContentParser.Token token = p.nextToken(); token != END_OBJECT; token = p.nextToken()) {
scriptFields.add(new ScriptField(c));
}
i.setScriptFields(scriptFields);
} catch (IOException e) {
throw new ParsingException(p.getTokenLocation(), "Could not parse inner script definition", e);
}
}, SearchSourceBuilder.SCRIPT_FIELDS_FIELD, ObjectParser.ValueType.OBJECT);
PARSER.declareField((p, i, c) -> i.setSorts(SortBuilder.fromXContent(c)), SearchSourceBuilder.SORT_FIELD,
ObjectParser.ValueType.OBJECT_ARRAY);
PARSER.declareField((p, i, c) -> {
try {
i.setFetchSourceContext(FetchSourceContext.fromXContent(c.parser()));
} catch (IOException e) {
throw new ParsingException(p.getTokenLocation(), "Could not parse inner _source definition", e);
}
}, SearchSourceBuilder._SOURCE_FIELD, ObjectParser.ValueType.OBJECT_ARRAY_BOOLEAN_OR_STRING);
PARSER.declareObject(InnerHitBuilder::setHighlightBuilder, (p, c) -> HighlightBuilder.fromXContent(c),
SearchSourceBuilder.HIGHLIGHT_FIELD);
PARSER.declareObject(InnerHitBuilder::setChildInnerHits, (p, c) -> {
try {
Map<String, InnerHitBuilder> 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);
}
}, INNER_HITS_FIELD);
}
private String name;
private String nestedPath;
private String parentChildType;
private boolean ignoreUnmapped;
private int from;
private int size = 3;
private boolean explain;
private boolean version;
private boolean trackScores;
private StoredFieldsContext storedFieldsContext;
private QueryBuilder query = DEFAULT_INNER_HIT_QUERY;
private List<SortBuilder<?>> sorts;
private List<String> docValueFields;
private Set<ScriptField> scriptFields;
private HighlightBuilder highlightBuilder;
private FetchSourceContext fetchSourceContext;
private Map<String, InnerHitBuilder> childInnerHits;
public InnerHitBuilder() {
}
private InnerHitBuilder(InnerHitBuilder other) {
name = other.name;
this.ignoreUnmapped = other.ignoreUnmapped;
from = other.from;
size = other.size;
explain = other.explain;
version = other.version;
trackScores = other.trackScores;
if (other.storedFieldsContext != null) {
storedFieldsContext = new StoredFieldsContext(other.storedFieldsContext);
}
if (other.docValueFields != null) {
docValueFields = new ArrayList<> (other.docValueFields);
}
if (other.scriptFields != null) {
scriptFields = new HashSet<> (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, boolean ignoreUnmapped) {
this(other);
this.query = query;
this.nestedPath = nestedPath;
this.ignoreUnmapped = ignoreUnmapped;
if (name == null) {
this.name = nestedPath;
}
}
// NORELEASE Do not use this ctr, it is public for hasChild and hasParent query but this is temporary
public InnerHitBuilder(InnerHitBuilder other, QueryBuilder query, String parentChildType, boolean ignoreUnmapped) {
this(other);
this.query = query;
this.parentChildType = parentChildType;
this.ignoreUnmapped = ignoreUnmapped;
if (name == null) {
this.name = parentChildType;
}
}
/**
* Read from a stream.
*/
public InnerHitBuilder(StreamInput in) throws IOException {
name = in.readOptionalString();
nestedPath = in.readOptionalString();
parentChildType = in.readOptionalString();
if (in.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) {
ignoreUnmapped = in.readBoolean();
}
from = in.readVInt();
size = in.readVInt();
explain = in.readBoolean();
version = in.readBoolean();
trackScores = in.readBoolean();
storedFieldsContext = in.readOptionalWriteable(StoredFieldsContext::new);
docValueFields = (List<String>) in.readGenericValue();
if (in.readBoolean()) {
int size = in.readVInt();
scriptFields = new HashSet<>(size);
for (int i = 0; i < size; i++) {
scriptFields.add(new ScriptField(in));
}
}
fetchSourceContext = in.readOptionalWriteable(FetchSourceContext::new);
if (in.readBoolean()) {
int size = in.readVInt();
sorts = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
sorts.add(in.readNamedWriteable(SortBuilder.class));
}
}
highlightBuilder = in.readOptionalWriteable(HighlightBuilder::new);
query = in.readNamedWriteable(QueryBuilder.class);
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));
}
}
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeOptionalString(name);
out.writeOptionalString(nestedPath);
out.writeOptionalString(parentChildType);
if (out.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) {
out.writeBoolean(ignoreUnmapped);
}
out.writeVInt(from);
out.writeVInt(size);
out.writeBoolean(explain);
out.writeBoolean(version);
out.writeBoolean(trackScores);
out.writeOptionalWriteable(storedFieldsContext);
out.writeGenericValue(docValueFields);
boolean hasScriptFields = scriptFields != null;
out.writeBoolean(hasScriptFields);
if (hasScriptFields) {
out.writeVInt(scriptFields.size());
Iterator<ScriptField> iterator = scriptFields.stream()
.sorted((a, b) -> a.fieldName().compareTo(b.fieldName())).iterator();
while (iterator.hasNext()) {
iterator.next().writeTo(out);
}
}
out.writeOptionalWriteable(fetchSourceContext);
boolean hasSorts = sorts != null;
out.writeBoolean(hasSorts);
if (hasSorts) {
out.writeVInt(sorts.size());
for (SortBuilder<?> sort : sorts) {
out.writeNamedWriteable(sort);
}
}
out.writeOptionalWriteable(highlightBuilder);
out.writeNamedWriteable(query);
boolean hasChildInnerHits = childInnerHits != null;
out.writeBoolean(hasChildInnerHits);
if (hasChildInnerHits) {
out.writeVInt(childInnerHits.size());
Iterator<Map.Entry<String, InnerHitBuilder>> iterator = childInnerHits.entrySet().stream()
.sorted((a, b) -> a.getKey().compareTo(b.getKey())).iterator();
while (iterator.hasNext()) {
Map.Entry<String, InnerHitBuilder> entry = iterator.next();
out.writeString(entry.getKey());
entry.getValue().writeTo(out);
}
}
}
public String getName() {
return name;
}
public InnerHitBuilder setName(String name) {
this.name = Objects.requireNonNull(name);
return this;
}
/**
* Whether to include inner hits in the search response hits if required mappings is missing
*/
public boolean isIgnoreUnmapped() {
return ignoreUnmapped;
}
public int getFrom() {
return from;
}
public InnerHitBuilder setFrom(int from) {
if (from < 0) {
throw new IllegalArgumentException("illegal from value, at least 0 or higher");
}
this.from = from;
return this;
}
public int getSize() {
return size;
}
public InnerHitBuilder setSize(int size) {
if (size < 0) {
throw new IllegalArgumentException("illegal size value, at least 0 or higher");
}
this.size = size;
return this;
}
public boolean isExplain() {
return explain;
}
public InnerHitBuilder setExplain(boolean explain) {
this.explain = explain;
return this;
}
public boolean isVersion() {
return version;
}
public InnerHitBuilder setVersion(boolean version) {
this.version = version;
return this;
}
public boolean isTrackScores() {
return trackScores;
}
public InnerHitBuilder setTrackScores(boolean trackScores) {
this.trackScores = trackScores;
return this;
}
/**
* Gets the stored fields to load and return.
*
* @deprecated Use {@link InnerHitBuilder#getStoredFieldsContext()} instead.
*/
@Deprecated
public List<String> getFieldNames() {
return storedFieldsContext == null ? null : storedFieldsContext.fieldNames();
}
/**
* Sets the stored fields to load and return.
* If none are specified, the source of the document will be returned.
*
* @deprecated Use {@link InnerHitBuilder#setStoredFieldNames(List)} instead.
*/
@Deprecated
public InnerHitBuilder setFieldNames(List<String> fieldNames) {
return setStoredFieldNames(fieldNames);
}
/**
* Gets the stored fields context.
*/
public StoredFieldsContext getStoredFieldsContext() {
return storedFieldsContext;
}
/**
* Sets the stored fields to load and return.
* If none are specified, the source of the document will be returned.
*/
public InnerHitBuilder setStoredFieldNames(List<String> fieldNames) {
if (storedFieldsContext == null) {
storedFieldsContext = StoredFieldsContext.fromList(fieldNames);
} else {
storedFieldsContext.addFieldNames(fieldNames);
}
return this;
}
/**
* Gets the docvalue fields.
*
* @deprecated Use {@link InnerHitBuilder#getDocValueFields()} instead.
*/
@Deprecated
public List<String> getFieldDataFields() {
return docValueFields;
}
/**
* Sets the stored fields to load from the docvalue and return.
*
* @deprecated Use {@link InnerHitBuilder#setDocValueFields(List)} instead.
*/
@Deprecated
public InnerHitBuilder setFieldDataFields(List<String> fieldDataFields) {
this.docValueFields = fieldDataFields;
return this;
}
/**
* Adds a field to load from the docvalue and return.
*
* @deprecated Use {@link InnerHitBuilder#addDocValueField(String)} instead.
*/
@Deprecated
public InnerHitBuilder addFieldDataField(String field) {
if (docValueFields == null) {
docValueFields = new ArrayList<>();
}
docValueFields.add(field);
return this;
}
/**
* Gets the docvalue fields.
*/
public List<String> getDocValueFields() {
return docValueFields;
}
/**
* Sets the stored fields to load from the docvalue and return.
*/
public InnerHitBuilder setDocValueFields(List<String> docValueFields) {
this.docValueFields = docValueFields;
return this;
}
/**
* Adds a field to load from the docvalue and return.
*/
public InnerHitBuilder addDocValueField(String field) {
if (docValueFields == null) {
docValueFields = new ArrayList<>();
}
docValueFields.add(field);
return this;
}
public Set<ScriptField> getScriptFields() {
return scriptFields;
}
public InnerHitBuilder setScriptFields(Set<ScriptField> scriptFields) {
this.scriptFields = scriptFields;
return this;
}
public InnerHitBuilder addScriptField(String name, Script script) {
if (scriptFields == null) {
scriptFields = new HashSet<>();
}
scriptFields.add(new ScriptField(name, script, false));
return this;
}
public FetchSourceContext getFetchSourceContext() {
return fetchSourceContext;
}
public InnerHitBuilder setFetchSourceContext(FetchSourceContext fetchSourceContext) {
this.fetchSourceContext = fetchSourceContext;
return this;
}
public List<SortBuilder<?>> getSorts() {
return sorts;
}
public InnerHitBuilder setSorts(List<SortBuilder<?>> sorts) {
this.sorts = sorts;
return this;
}
public InnerHitBuilder addSort(SortBuilder<?> sort) {
if (sorts == null) {
sorts = new ArrayList<>();
}
sorts.add(sort);
return this;
}
public HighlightBuilder getHighlightBuilder() {
return highlightBuilder;
}
public InnerHitBuilder setHighlightBuilder(HighlightBuilder highlightBuilder) {
this.highlightBuilder = highlightBuilder;
return this;
}
QueryBuilder getQuery() {
return query;
}
void setChildInnerHits(Map<String, InnerHitBuilder> childInnerHits) {
this.childInnerHits = childInnerHits;
}
String getParentChildType() {
return parentChildType;
}
String getNestedPath() {
return nestedPath;
}
void addChildInnerHit(InnerHitBuilder innerHitBuilder) {
if (childInnerHits == null) {
childInnerHits = new HashMap<>();
}
this.childInnerHits.put(innerHitBuilder.getName(), innerHitBuilder);
}
public InnerHitsContext.BaseInnerHits build(SearchContext parentSearchContext,
InnerHitsContext innerHitsContext) throws IOException {
QueryShardContext queryShardContext = parentSearchContext.getQueryShardContext();
if (nestedPath != null) {
ObjectMapper nestedObjectMapper = queryShardContext.getObjectMapper(nestedPath);
if (nestedObjectMapper == null) {
if (ignoreUnmapped == false) {
throw new IllegalStateException("[" + query.getName() + "] no mapping found for type [" + nestedPath + "]");
} else {
return null;
}
}
ObjectMapper parentObjectMapper = queryShardContext.nestedScope().nextLevel(nestedObjectMapper);
InnerHitsContext.NestedInnerHits nestedInnerHits = new InnerHitsContext.NestedInnerHits(
name, parentSearchContext, parentObjectMapper, nestedObjectMapper
);
setupInnerHitsContext(queryShardContext, nestedInnerHits);
if (childInnerHits != null) {
buildChildInnerHits(parentSearchContext, nestedInnerHits);
}
queryShardContext.nestedScope().previousLevel();
innerHitsContext.addInnerHitDefinition(nestedInnerHits);
return nestedInnerHits;
} else if (parentChildType != null) {
DocumentMapper documentMapper = queryShardContext.documentMapper(parentChildType);
if (documentMapper == null) {
if (ignoreUnmapped == false) {
throw new IllegalStateException("[" + query.getName() + "] no mapping found for type [" + parentChildType + "]");
} else {
return null;
}
}
InnerHitsContext.ParentChildInnerHits parentChildInnerHits = new InnerHitsContext.ParentChildInnerHits(
name, parentSearchContext, queryShardContext.getMapperService(), documentMapper
);
setupInnerHitsContext(queryShardContext, parentChildInnerHits);
if (childInnerHits != null) {
buildChildInnerHits(parentSearchContext, parentChildInnerHits);
}
innerHitsContext.addInnerHitDefinition( parentChildInnerHits);
return parentChildInnerHits;
} else {
throw new IllegalStateException("Neither a nested or parent/child inner hit");
}
}
private void buildChildInnerHits(SearchContext parentSearchContext, InnerHitsContext.BaseInnerHits innerHits) throws IOException {
Map<String, InnerHitsContext.BaseInnerHits> childInnerHits = new HashMap<>();
for (Map.Entry<String, InnerHitBuilder> entry : this.childInnerHits.entrySet()) {
InnerHitsContext.BaseInnerHits childInnerHit = entry.getValue().build(
parentSearchContext, new InnerHitsContext()
);
if (childInnerHit != null) {
childInnerHits.put(entry.getKey(), childInnerHit);
}
}
innerHits.setChildInnerHits(childInnerHits);
}
private void setupInnerHitsContext(QueryShardContext context, InnerHitsContext.BaseInnerHits innerHitsContext) throws IOException {
innerHitsContext.from(from);
innerHitsContext.size(size);
innerHitsContext.explain(explain);
innerHitsContext.version(version);
innerHitsContext.trackScores(trackScores);
if (storedFieldsContext != null) {
innerHitsContext.storedFieldsContext(storedFieldsContext);
}
if (docValueFields != null) {
innerHitsContext.docValueFieldsContext(new DocValueFieldsContext(docValueFields));
}
if (scriptFields != null) {
for (ScriptField field : scriptFields) {
SearchScript searchScript = innerHitsContext.getQueryShardContext().getSearchScript(field.script(),
ScriptContext.Standard.SEARCH);
innerHitsContext.scriptFields().add(new org.elasticsearch.search.fetch.subphase.ScriptFieldsContext.ScriptField(
field.fieldName(), searchScript, field.ignoreFailure()));
}
}
if (fetchSourceContext != null) {
innerHitsContext.fetchSourceContext(fetchSourceContext);
}
if (sorts != null) {
Optional<SortAndFormats> optionalSort = SortBuilder.buildSort(sorts, context);
if (optionalSort.isPresent()) {
innerHitsContext.sort(optionalSort.get());
}
}
if (highlightBuilder != null) {
innerHitsContext.highlight(highlightBuilder.build(context));
}
ParsedQuery parsedQuery = new ParsedQuery(query.toQuery(context), context.copyNamedQueries());
innerHitsContext.parsedQuery(parsedQuery);
}
public void inlineInnerHits(Map<String, InnerHitBuilder> innerHits) {
InnerHitBuilder copy = new InnerHitBuilder(this);
copy.parentChildType = this.parentChildType;
copy.nestedPath = this.nestedPath;
copy.query = this.query;
innerHits.put(copy.getName(), copy);
Map<String, InnerHitBuilder> 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 (name != null) {
builder.field(NAME_FIELD.getPreferredName(), name);
}
builder.field(IGNORE_UNMAPPED.getPreferredName(), ignoreUnmapped);
builder.field(SearchSourceBuilder.FROM_FIELD.getPreferredName(), from);
builder.field(SearchSourceBuilder.SIZE_FIELD.getPreferredName(), size);
builder.field(SearchSourceBuilder.VERSION_FIELD.getPreferredName(), version);
builder.field(SearchSourceBuilder.EXPLAIN_FIELD.getPreferredName(), explain);
builder.field(SearchSourceBuilder.TRACK_SCORES_FIELD.getPreferredName(), trackScores);
if (fetchSourceContext != null) {
builder.field(SearchSourceBuilder._SOURCE_FIELD.getPreferredName(), fetchSourceContext, params);
}
if (storedFieldsContext != null) {
storedFieldsContext.toXContent(SearchSourceBuilder.STORED_FIELDS_FIELD.getPreferredName(), builder);
}
if (docValueFields != null) {
builder.startArray(SearchSourceBuilder.DOCVALUE_FIELDS_FIELD.getPreferredName());
for (String fieldDataField : docValueFields) {
builder.value(fieldDataField);
}
builder.endArray();
}
if (scriptFields != null) {
builder.startObject(SearchSourceBuilder.SCRIPT_FIELDS_FIELD.getPreferredName());
for (ScriptField scriptField : scriptFields) {
scriptField.toXContent(builder, params);
}
builder.endObject();
}
if (sorts != null) {
builder.startArray(SearchSourceBuilder.SORT_FIELD.getPreferredName());
for (SortBuilder<?> sort : sorts) {
sort.toXContent(builder, params);
}
builder.endArray();
}
if (highlightBuilder != null) {
builder.field(SearchSourceBuilder.HIGHLIGHT_FIELD.getPreferredName(), highlightBuilder, params);
}
if (childInnerHits != null) {
builder.startObject(INNER_HITS_FIELD.getPreferredName());
for (Map.Entry<String, InnerHitBuilder> entry : childInnerHits.entrySet()) {
builder.field(entry.getKey(), entry.getValue(), params);
}
builder.endObject();
}
builder.endObject();
return builder;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InnerHitBuilder that = (InnerHitBuilder) o;
return Objects.equals(name, that.name) &&
Objects.equals(nestedPath, that.nestedPath) &&
Objects.equals(parentChildType, that.parentChildType) &&
Objects.equals(ignoreUnmapped, that.ignoreUnmapped) &&
Objects.equals(from, that.from) &&
Objects.equals(size, that.size) &&
Objects.equals(explain, that.explain) &&
Objects.equals(version, that.version) &&
Objects.equals(trackScores, that.trackScores) &&
Objects.equals(storedFieldsContext, that.storedFieldsContext) &&
Objects.equals(docValueFields, that.docValueFields) &&
Objects.equals(scriptFields, that.scriptFields) &&
Objects.equals(fetchSourceContext, that.fetchSourceContext) &&
Objects.equals(sorts, that.sorts) &&
Objects.equals(highlightBuilder, that.highlightBuilder) &&
Objects.equals(query, that.query) &&
Objects.equals(childInnerHits, that.childInnerHits);
}
@Override
public int hashCode() {
return Objects.hash(name, nestedPath, parentChildType, ignoreUnmapped, from, size, explain, version, trackScores,
storedFieldsContext, docValueFields, 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<String, InnerHitBuilder> 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");
}
}
// TODO public for hasParent and hasChild query
public static InnerHitBuilder rewrite(InnerHitBuilder original, QueryBuilder rewrittenQuery) {
if (original == null) {
return null;
}
InnerHitBuilder copy = new InnerHitBuilder(original);
copy.query = rewrittenQuery;
copy.parentChildType = original.parentChildType;
copy.nestedPath = original.nestedPath;
return copy;
}
}