org.opensearch.index.query.InnerHitBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of opensearch Show documentation
Show all versions of opensearch Show documentation
OpenSearch subproject :server
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/
/*
* 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.
*/
/*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/
package org.opensearch.index.query;
import org.opensearch.LegacyESVersion;
import org.opensearch.common.Nullable;
import org.opensearch.common.annotation.PublicApi;
import org.opensearch.core.ParseField;
import org.opensearch.core.common.ParsingException;
import org.opensearch.core.common.Strings;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.core.common.io.stream.Writeable;
import org.opensearch.core.xcontent.MediaTypeRegistry;
import org.opensearch.core.xcontent.ObjectParser;
import org.opensearch.core.xcontent.ToXContentObject;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.core.xcontent.XContentParser;
import org.opensearch.script.Script;
import org.opensearch.search.builder.SearchSourceBuilder;
import org.opensearch.search.builder.SearchSourceBuilder.ScriptField;
import org.opensearch.search.collapse.CollapseBuilder;
import org.opensearch.search.fetch.StoredFieldsContext;
import org.opensearch.search.fetch.subphase.FetchSourceContext;
import org.opensearch.search.fetch.subphase.FieldAndFormat;
import org.opensearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.opensearch.search.sort.SortBuilder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static org.opensearch.core.xcontent.XContentParser.Token.END_OBJECT;
/**
* Query builder for inner hits
*
* @opensearch.api
*/
@PublicApi(since = "1.0.0")
public final class InnerHitBuilder implements Writeable, ToXContentObject {
public static final ParseField NAME_FIELD = new ParseField("name");
public static final ParseField IGNORE_UNMAPPED = new ParseField("ignore_unmapped");
public static final QueryBuilder DEFAULT_INNER_HIT_QUERY = new MatchAllQueryBuilder();
public static final ParseField COLLAPSE_FIELD = new ParseField("collapse");
public static final ParseField FIELD_FIELD = new ParseField("field");
private static final ObjectParser 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::setSeqNoAndPrimaryTerm, SearchSourceBuilder.SEQ_NO_PRIMARY_TERM_FIELD);
PARSER.declareBoolean(InnerHitBuilder::setTrackScores, SearchSourceBuilder.TRACK_SCORES_FIELD);
PARSER.declareStringArray(InnerHitBuilder::setStoredFieldNames, SearchSourceBuilder.STORED_FIELDS_FIELD);
PARSER.declareObjectArray(
InnerHitBuilder::setDocValueFields,
(p, c) -> FieldAndFormat.fromXContent(p),
SearchSourceBuilder.DOCVALUE_FIELDS_FIELD
);
PARSER.declareObjectArray(
InnerHitBuilder::setFetchFields,
(p, c) -> FieldAndFormat.fromXContent(p),
SearchSourceBuilder.FETCH_FIELDS_FIELD
);
PARSER.declareField((p, i, c) -> {
try {
Set scriptFields = new HashSet<>();
for (XContentParser.Token token = p.nextToken(); token != END_OBJECT; token = p.nextToken()) {
scriptFields.add(new ScriptField(p));
}
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(p)),
SearchSourceBuilder.SORT_FIELD,
ObjectParser.ValueType.OBJECT_ARRAY
);
PARSER.declareField((p, i, c) -> {
try {
i.setFetchSourceContext(FetchSourceContext.fromXContent(p));
} 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(p),
SearchSourceBuilder.HIGHLIGHT_FIELD
);
PARSER.declareField((parser, builder, context) -> {
Boolean isParsedCorrectly = false;
String field;
if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
if (parser.nextToken() == XContentParser.Token.FIELD_NAME) {
if (FIELD_FIELD.match(parser.currentName(), parser.getDeprecationHandler())) {
if (parser.nextToken() == XContentParser.Token.VALUE_STRING) {
field = parser.text();
if (parser.nextToken() == XContentParser.Token.END_OBJECT) {
isParsedCorrectly = true;
CollapseBuilder cb = new CollapseBuilder(field);
builder.setInnerCollapse(cb);
}
}
}
}
}
if (isParsedCorrectly == false) {
throw new ParsingException(parser.getTokenLocation(), "Invalid token in the inner collapse");
}
}, COLLAPSE_FIELD, ObjectParser.ValueType.OBJECT);
}
private String name;
private boolean ignoreUnmapped;
private int from;
private int size = 3;
private boolean explain;
private boolean version;
private boolean seqNoAndPrimaryTerm;
private boolean trackScores;
private StoredFieldsContext storedFieldsContext;
private QueryBuilder query = DEFAULT_INNER_HIT_QUERY;
private List> sorts;
private List docValueFields;
private Set scriptFields;
private HighlightBuilder highlightBuilder;
private FetchSourceContext fetchSourceContext;
private List fetchFields;
private CollapseBuilder innerCollapseBuilder = null;
public InnerHitBuilder() {
this.name = null;
}
public InnerHitBuilder(String name) {
this.name = name;
}
/**
* Read from a stream.
*/
public InnerHitBuilder(StreamInput in) throws IOException {
name = in.readOptionalString();
ignoreUnmapped = in.readBoolean();
from = in.readVInt();
size = in.readVInt();
explain = in.readBoolean();
version = in.readBoolean();
seqNoAndPrimaryTerm = in.readBoolean();
trackScores = in.readBoolean();
storedFieldsContext = in.readOptionalWriteable(StoredFieldsContext::new);
docValueFields = in.readBoolean() ? in.readList(FieldAndFormat::new) : null;
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);
this.innerCollapseBuilder = in.readOptionalWriteable(CollapseBuilder::new);
if (in.getVersion().onOrAfter(LegacyESVersion.V_7_10_0)) {
if (in.readBoolean()) {
fetchFields = in.readList(FieldAndFormat::new);
}
}
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeOptionalString(name);
out.writeBoolean(ignoreUnmapped);
out.writeVInt(from);
out.writeVInt(size);
out.writeBoolean(explain);
out.writeBoolean(version);
out.writeBoolean(seqNoAndPrimaryTerm);
out.writeBoolean(trackScores);
out.writeOptionalWriteable(storedFieldsContext);
out.writeBoolean(docValueFields != null);
if (docValueFields != null) {
out.writeList(docValueFields);
}
boolean hasScriptFields = scriptFields != null;
out.writeBoolean(hasScriptFields);
if (hasScriptFields) {
out.writeVInt(scriptFields.size());
Iterator iterator = scriptFields.stream().sorted(Comparator.comparing(ScriptField::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.writeOptionalWriteable(innerCollapseBuilder);
if (out.getVersion().onOrAfter(LegacyESVersion.V_7_10_0)) {
out.writeBoolean(fetchFields != null);
if (fetchFields != null) {
out.writeList(fetchFields);
}
}
}
public String getName() {
return name;
}
public InnerHitBuilder setName(String name) {
this.name = Objects.requireNonNull(name);
return this;
}
public InnerHitBuilder setIgnoreUnmapped(boolean value) {
this.ignoreUnmapped = value;
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 isSeqNoAndPrimaryTerm() {
return seqNoAndPrimaryTerm;
}
public InnerHitBuilder setSeqNoAndPrimaryTerm(boolean seqNoAndPrimaryTerm) {
this.seqNoAndPrimaryTerm = seqNoAndPrimaryTerm;
return this;
}
public boolean isTrackScores() {
return trackScores;
}
public InnerHitBuilder setTrackScores(boolean trackScores) {
this.trackScores = trackScores;
return this;
}
/**
* 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 fieldNames) {
if (storedFieldsContext == null) {
storedFieldsContext = StoredFieldsContext.fromList(fieldNames);
} else {
storedFieldsContext.addFieldNames(fieldNames);
}
return this;
}
/**
* Gets the docvalue fields.
*/
public List getDocValueFields() {
return docValueFields;
}
/**
* Sets the stored fields to load from the docvalue and return.
*/
public InnerHitBuilder setDocValueFields(List docValueFields) {
this.docValueFields = docValueFields;
return this;
}
/**
* Adds a field to load from the docvalue and return.
*/
public InnerHitBuilder addDocValueField(String field, String format) {
if (docValueFields == null || docValueFields.isEmpty()) {
docValueFields = new ArrayList<>();
}
docValueFields.add(new FieldAndFormat(field, format));
return this;
}
/**
* Adds a field to load from doc values and return.
*/
public InnerHitBuilder addDocValueField(String field) {
return addDocValueField(field, null);
}
/**
* Gets the fields to load and return as part of the search request.
*/
public List getFetchFields() {
return fetchFields;
}
/**
* Sets the stored fields to load and return as part of the search request.
*/
public InnerHitBuilder setFetchFields(List fetchFields) {
this.fetchFields = fetchFields;
return this;
}
/**
* Adds a field to load and return as part of the search request.
*/
public InnerHitBuilder addFetchField(String name) {
return addFetchField(name, null);
}
/**
* Adds a field to load and return as part of the search request.
* @param name the field name.
* @param format an optional format string used when formatting values, for example a date format.
*/
public InnerHitBuilder addFetchField(String name, @Nullable String format) {
if (fetchFields == null || fetchFields.isEmpty()) {
fetchFields = new ArrayList<>();
}
fetchFields.add(new FieldAndFormat(name, format));
return this;
}
public Set getScriptFields() {
return scriptFields;
}
public InnerHitBuilder setScriptFields(Set 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> getSorts() {
return sorts;
}
public InnerHitBuilder setSorts(List> 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;
}
public InnerHitBuilder setInnerCollapse(CollapseBuilder innerCollapseBuilder) {
this.innerCollapseBuilder = innerCollapseBuilder;
return this;
}
public CollapseBuilder getInnerCollapseBuilder() {
return innerCollapseBuilder;
}
@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.SEQ_NO_PRIMARY_TERM_FIELD.getPreferredName(), seqNoAndPrimaryTerm);
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 (FieldAndFormat docValueField : docValueFields) {
docValueField.toXContent(builder, params);
}
builder.endArray();
}
if (fetchFields != null) {
builder.startArray(SearchSourceBuilder.FETCH_FIELDS_FIELD.getPreferredName());
for (FieldAndFormat docValueField : fetchFields) {
docValueField.toXContent(builder, params);
}
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 (innerCollapseBuilder != null) {
builder.field(COLLAPSE_FIELD.getPreferredName(), innerCollapseBuilder);
}
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 ignoreUnmapped == that.ignoreUnmapped
&& from == that.from
&& size == that.size
&& explain == that.explain
&& version == that.version
&& seqNoAndPrimaryTerm == that.seqNoAndPrimaryTerm
&& trackScores == that.trackScores
&& Objects.equals(name, that.name)
&& Objects.equals(storedFieldsContext, that.storedFieldsContext)
&& Objects.equals(query, that.query)
&& Objects.equals(sorts, that.sorts)
&& Objects.equals(docValueFields, that.docValueFields)
&& Objects.equals(scriptFields, that.scriptFields)
&& Objects.equals(highlightBuilder, that.highlightBuilder)
&& Objects.equals(fetchSourceContext, that.fetchSourceContext)
&& Objects.equals(fetchFields, that.fetchFields)
&& Objects.equals(innerCollapseBuilder, that.innerCollapseBuilder);
}
@Override
public int hashCode() {
return Objects.hash(
name,
ignoreUnmapped,
from,
size,
explain,
version,
seqNoAndPrimaryTerm,
trackScores,
storedFieldsContext,
query,
sorts,
docValueFields,
scriptFields,
highlightBuilder,
fetchSourceContext,
fetchFields,
innerCollapseBuilder
);
}
public static InnerHitBuilder fromXContent(XContentParser parser) throws IOException {
return PARSER.parse(parser, new InnerHitBuilder(), null);
}
@Override
public String toString() {
return Strings.toString(MediaTypeRegistry.JSON, this, true, true);
}
}