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
This commit is contained in:
parent
0cf1d16187
commit
6c3beaa2eb
|
@ -31,7 +31,6 @@ import org.elasticsearch.search.Scroll;
|
||||||
import org.elasticsearch.search.aggregations.AggregatorBuilder;
|
import org.elasticsearch.search.aggregations.AggregatorBuilder;
|
||||||
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregatorBuilder;
|
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregatorBuilder;
|
||||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||||
import org.elasticsearch.index.query.support.InnerHitsBuilder;
|
|
||||||
import org.elasticsearch.search.highlight.HighlightBuilder;
|
import org.elasticsearch.search.highlight.HighlightBuilder;
|
||||||
import org.elasticsearch.search.rescore.RescoreBuilder;
|
import org.elasticsearch.search.rescore.RescoreBuilder;
|
||||||
import org.elasticsearch.search.sort.SortBuilder;
|
import org.elasticsearch.search.sort.SortBuilder;
|
||||||
|
@ -400,11 +399,6 @@ public class SearchRequestBuilder extends ActionRequestBuilder<SearchRequest, Se
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchRequestBuilder innerHits(InnerHitsBuilder innerHitsBuilder) {
|
|
||||||
sourceBuilder().innerHits(innerHitsBuilder);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all rescorers on the builder and sets the first one. To use multiple rescore windows use
|
* Clears all rescorers on the builder and sets the first one. To use multiple rescore windows use
|
||||||
* {@link #addRescorer(org.elasticsearch.search.rescore.RescoreBuilder, int)}.
|
* {@link #addRescorer(org.elasticsearch.search.rescore.RescoreBuilder, int)}.
|
||||||
|
|
|
@ -36,6 +36,7 @@ import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -273,6 +274,15 @@ public abstract class AbstractQueryBuilder<QB extends AbstractQueryBuilder<QB>>
|
||||||
return this;
|
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<String, InnerHitBuilder> innerHits) {
|
||||||
|
}
|
||||||
|
|
||||||
// Like Objects.requireNotNull(...) but instead throws a IllegalArgumentException
|
// Like Objects.requireNotNull(...) but instead throws a IllegalArgumentException
|
||||||
protected static <T> T requireValue(T value, String message) {
|
protected static <T> T requireValue(T value, String message) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@ -495,6 +496,17 @@ public class BoolQueryBuilder extends AbstractQueryBuilder<BoolQueryBuilder> {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void extractInnerHitBuilders(Map<String, InnerHitBuilder> innerHits) {
|
||||||
|
List<QueryBuilder<?>> 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<QueryBuilder<?>> builders,
|
private static boolean rewriteClauses(QueryRewriteContext queryRewriteContext, List<QueryBuilder<?>> builders,
|
||||||
Consumer<QueryBuilder<?>> consumer) throws IOException {
|
Consumer<QueryBuilder<?>> consumer) throws IOException {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -235,4 +236,10 @@ public class BoostingQueryBuilder extends AbstractQueryBuilder<BoostingQueryBuil
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void extractInnerHitBuilders(Map<String, InnerHitBuilder> innerHits) {
|
||||||
|
InnerHitBuilder.extractInnerHits(positiveQuery, innerHits);
|
||||||
|
InnerHitBuilder.extractInnerHits(negativeQuery, innerHits);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -169,4 +170,9 @@ public class ConstantScoreQueryBuilder extends AbstractQueryBuilder<ConstantScor
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void extractInnerHitBuilders(Map<String, InnerHitBuilder> innerHits) {
|
||||||
|
InnerHitBuilder.extractInnerHits(filterBuilder, innerHits);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,10 +38,10 @@ import org.elasticsearch.index.fielddata.IndexParentChildFieldData;
|
||||||
import org.elasticsearch.index.fielddata.plain.ParentChildIndexFieldData;
|
import org.elasticsearch.index.fielddata.plain.ParentChildIndexFieldData;
|
||||||
import org.elasticsearch.index.mapper.DocumentMapper;
|
import org.elasticsearch.index.mapper.DocumentMapper;
|
||||||
import org.elasticsearch.index.mapper.internal.ParentFieldMapper;
|
import org.elasticsearch.index.mapper.internal.ParentFieldMapper;
|
||||||
import org.elasticsearch.index.query.support.InnerHitBuilder;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -151,9 +151,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
||||||
}
|
}
|
||||||
|
|
||||||
public HasChildQueryBuilder innerHit(InnerHitBuilder innerHit) {
|
public HasChildQueryBuilder innerHit(InnerHitBuilder innerHit) {
|
||||||
innerHit.setParentChildType(type);
|
this.innerHitBuilder = new InnerHitBuilder(Objects.requireNonNull(innerHit), query, type);
|
||||||
innerHit.setQuery(query);
|
|
||||||
this.innerHitBuilder = innerHit;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,8 +272,11 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HasChildQueryBuilder hasChildQueryBuilder = new HasChildQueryBuilder(childType, iqb, minChildren, maxChildren,
|
HasChildQueryBuilder hasChildQueryBuilder = new HasChildQueryBuilder(childType, iqb, scoreMode);
|
||||||
scoreMode, innerHitBuilder);
|
if (innerHitBuilder != null) {
|
||||||
|
hasChildQueryBuilder.innerHit(innerHitBuilder);
|
||||||
|
}
|
||||||
|
hasChildQueryBuilder.minMaxChildren(minChildren, maxChildren);
|
||||||
hasChildQueryBuilder.queryName(queryName);
|
hasChildQueryBuilder.queryName(queryName);
|
||||||
hasChildQueryBuilder.boost(boost);
|
hasChildQueryBuilder.boost(boost);
|
||||||
hasChildQueryBuilder.ignoreUnmapped(ignoreUnmapped);
|
hasChildQueryBuilder.ignoreUnmapped(ignoreUnmapped);
|
||||||
|
@ -337,10 +338,6 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
||||||
if (parentFieldMapper.active() == false) {
|
if (parentFieldMapper.active() == false) {
|
||||||
throw new QueryShardException(context, "[" + NAME + "] _parent field has no parent type configured");
|
throw new QueryShardException(context, "[" + NAME + "] _parent field has no parent type configured");
|
||||||
}
|
}
|
||||||
if (innerHitBuilder != null) {
|
|
||||||
context.addInnerHit(innerHitBuilder);
|
|
||||||
}
|
|
||||||
|
|
||||||
String parentType = parentFieldMapper.type();
|
String parentType = parentFieldMapper.type();
|
||||||
DocumentMapper parentDocMapper = context.getMapperService().documentMapper(parentType);
|
DocumentMapper parentDocMapper = context.getMapperService().documentMapper(parentType);
|
||||||
if (parentDocMapper == null) {
|
if (parentDocMapper == null) {
|
||||||
|
@ -477,4 +474,11 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void extractInnerHitBuilders(Map<String, InnerHitBuilder> innerHits) {
|
||||||
|
if (innerHitBuilder != null) {
|
||||||
|
innerHitBuilder.inlineInnerHits(innerHits);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,10 +33,10 @@ import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.index.fielddata.plain.ParentChildIndexFieldData;
|
import org.elasticsearch.index.fielddata.plain.ParentChildIndexFieldData;
|
||||||
import org.elasticsearch.index.mapper.DocumentMapper;
|
import org.elasticsearch.index.mapper.DocumentMapper;
|
||||||
import org.elasticsearch.index.mapper.internal.ParentFieldMapper;
|
import org.elasticsearch.index.mapper.internal.ParentFieldMapper;
|
||||||
import org.elasticsearch.index.query.support.InnerHitBuilder;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -127,9 +127,7 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
|
||||||
}
|
}
|
||||||
|
|
||||||
public HasParentQueryBuilder innerHit(InnerHitBuilder innerHit) {
|
public HasParentQueryBuilder innerHit(InnerHitBuilder innerHit) {
|
||||||
innerHit.setParentChildType(type);
|
this.innerHit = new InnerHitBuilder(innerHit, query, type);
|
||||||
innerHit.setQuery(query);
|
|
||||||
this.innerHit = innerHit;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,10 +173,6 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (innerHit != null) {
|
|
||||||
context.addInnerHit(innerHit);
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> childTypes = new HashSet<>();
|
Set<String> childTypes = new HashSet<>();
|
||||||
ParentChildIndexFieldData parentChildIndexFieldData = null;
|
ParentChildIndexFieldData parentChildIndexFieldData = null;
|
||||||
for (DocumentMapper documentMapper : context.getMapperService().docMappers(false)) {
|
for (DocumentMapper documentMapper : context.getMapperService().docMappers(false)) {
|
||||||
|
@ -282,8 +276,14 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new HasParentQueryBuilder(parentType, iqb, score, innerHits).ignoreUnmapped(ignoreUnmapped).queryName(queryName)
|
HasParentQueryBuilder queryBuilder = new HasParentQueryBuilder(parentType, iqb, score)
|
||||||
|
.ignoreUnmapped(ignoreUnmapped)
|
||||||
|
.queryName(queryName)
|
||||||
.boost(boost);
|
.boost(boost);
|
||||||
|
if (innerHits != null) {
|
||||||
|
queryBuilder.innerHit(innerHits);
|
||||||
|
}
|
||||||
|
return queryBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -313,4 +313,11 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void extractInnerHitBuilders(Map<String, InnerHitBuilder> innerHits) {
|
||||||
|
if (innerHit!= null) {
|
||||||
|
innerHit.inlineInnerHits(innerHits);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.index.query.support;
|
package org.elasticsearch.index.query;
|
||||||
|
|
||||||
import org.apache.lucene.search.Sort;
|
import org.apache.lucene.search.Sort;
|
||||||
import org.elasticsearch.action.support.ToXContentToBytes;
|
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.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.index.mapper.DocumentMapper;
|
import org.elasticsearch.index.mapper.DocumentMapper;
|
||||||
import org.elasticsearch.index.mapper.object.ObjectMapper;
|
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.Script;
|
||||||
import org.elasticsearch.script.ScriptContext;
|
import org.elasticsearch.script.ScriptContext;
|
||||||
import org.elasticsearch.script.SearchScript;
|
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 final class InnerHitBuilder extends ToXContentToBytes implements Writeable {
|
||||||
|
|
||||||
public static final ParseField NAME_FIELD = new ParseField("name");
|
public static final ParseField NAME_FIELD = new ParseField("name");
|
||||||
public static final ParseField NESTED_PATH_FIELD = new ParseField("path");
|
public static final ParseField INNER_HITS_FIELD = new ParseField("inner_hits");
|
||||||
public static final ParseField PARENT_CHILD_TYPE_FIELD = new ParseField("type");
|
|
||||||
|
|
||||||
private final static ObjectParser<InnerHitBuilder, QueryParseContext> PARSER = new ObjectParser<>("inner_hits", InnerHitBuilder::new);
|
private final static ObjectParser<InnerHitBuilder, QueryParseContext> PARSER = new ObjectParser<>("inner_hits", InnerHitBuilder::new);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
PARSER.declareString(InnerHitBuilder::setName, NAME_FIELD);
|
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::setFrom, SearchSourceBuilder.FROM_FIELD);
|
||||||
PARSER.declareInt(InnerHitBuilder::setSize, SearchSourceBuilder.SIZE_FIELD);
|
PARSER.declareInt(InnerHitBuilder::setSize, SearchSourceBuilder.SIZE_FIELD);
|
||||||
PARSER.declareBoolean(InnerHitBuilder::setExplain, SearchSourceBuilder.EXPLAIN_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);
|
}, SearchSourceBuilder._SOURCE_FIELD, ObjectParser.ValueType.OBJECT_OR_BOOLEAN);
|
||||||
PARSER.declareObject(InnerHitBuilder::setHighlightBuilder, (p, c) -> HighlightBuilder.fromXContent(c),
|
PARSER.declareObject(InnerHitBuilder::setHighlightBuilder, (p, c) -> HighlightBuilder.fromXContent(c),
|
||||||
SearchSourceBuilder.HIGHLIGHT_FIELD);
|
SearchSourceBuilder.HIGHLIGHT_FIELD);
|
||||||
PARSER.declareObject(InnerHitBuilder::setQuery, (p, c) ->{
|
PARSER.declareObject(InnerHitBuilder::setChildInnerHits, (p, c) -> {
|
||||||
try {
|
try {
|
||||||
return c.parseInnerQueryBuilder();
|
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) {
|
} catch (IOException e) {
|
||||||
throw new ParsingException(p.getTokenLocation(), "Could not parse inner query definition", e);
|
throw new ParsingException(p.getTokenLocation(), "Could not parse inner query definition", e);
|
||||||
}
|
}
|
||||||
}, SearchSourceBuilder.QUERY_FIELD);
|
}, INNER_HITS_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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
@ -132,8 +134,8 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl
|
||||||
private List<String> fieldDataFields;
|
private List<String> fieldDataFields;
|
||||||
private List<ScriptField> scriptFields;
|
private List<ScriptField> scriptFields;
|
||||||
private HighlightBuilder highlightBuilder;
|
private HighlightBuilder highlightBuilder;
|
||||||
private InnerHitsBuilder innerHitsBuilder;
|
|
||||||
private FetchSourceContext fetchSourceContext;
|
private FetchSourceContext fetchSourceContext;
|
||||||
|
private Map<String, InnerHitBuilder> childInnerHits;
|
||||||
|
|
||||||
public InnerHitBuilder() {
|
public InnerHitBuilder() {
|
||||||
}
|
}
|
||||||
|
@ -165,7 +167,62 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl
|
||||||
}
|
}
|
||||||
highlightBuilder = in.readOptionalWriteable(HighlightBuilder::new);
|
highlightBuilder = in.readOptionalWriteable(HighlightBuilder::new);
|
||||||
query = in.readNamedWriteable(QueryBuilder.class);
|
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
|
@Override
|
||||||
|
@ -196,17 +253,15 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl
|
||||||
}
|
}
|
||||||
out.writeOptionalWriteable(highlightBuilder);
|
out.writeOptionalWriteable(highlightBuilder);
|
||||||
out.writeNamedWriteable(query);
|
out.writeNamedWriteable(query);
|
||||||
out.writeOptionalWriteable(innerHitsBuilder);
|
boolean hasChildInnerHits = childInnerHits != null;
|
||||||
}
|
out.writeBoolean(hasChildInnerHits);
|
||||||
|
if (hasChildInnerHits) {
|
||||||
public InnerHitBuilder setParentChildType(String parentChildType) {
|
out.writeVInt(childInnerHits.size());
|
||||||
this.parentChildType = parentChildType;
|
for (Map.Entry<String, InnerHitBuilder> entry : childInnerHits.entrySet()) {
|
||||||
return this;
|
out.writeString(entry.getKey());
|
||||||
}
|
entry.getValue().writeTo(out);
|
||||||
|
}
|
||||||
public InnerHitBuilder setNestedPath(String nestedPath) {
|
}
|
||||||
this.nestedPath = nestedPath;
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -347,72 +402,53 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryBuilder<?> getQuery() {
|
QueryBuilder<?> getQuery() {
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InnerHitBuilder setQuery(QueryBuilder<?> query) {
|
void setChildInnerHits(Map<String, InnerHitBuilder> childInnerHits) {
|
||||||
this.query = Objects.requireNonNull(query);
|
this.childInnerHits = childInnerHits;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public InnerHitBuilder setInnerHitsBuilder(InnerHitsBuilder innerHitsBuilder) {
|
String getParentChildType() {
|
||||||
this.innerHitsBuilder = innerHitsBuilder;
|
return parentChildType;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public InnerHitsContext.BaseInnerHits buildInline(SearchContext parentSearchContext, QueryShardContext context) throws IOException {
|
String getNestedPath() {
|
||||||
InnerHitsContext.BaseInnerHits innerHitsContext;
|
return nestedPath;
|
||||||
if (nestedPath != null) {
|
}
|
||||||
ObjectMapper nestedObjectMapper = context.getObjectMapper(nestedPath);
|
|
||||||
ObjectMapper parentObjectMapper = context.nestedScope().getObjectMapper();
|
void addChildInnerHit(InnerHitBuilder innerHitBuilder) {
|
||||||
innerHitsContext = new InnerHitsContext.NestedInnerHits(
|
if (childInnerHits == null) {
|
||||||
name, parentSearchContext, parentObjectMapper, nestedObjectMapper
|
childInnerHits = new HashMap<>();
|
||||||
);
|
|
||||||
} 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");
|
|
||||||
}
|
}
|
||||||
setupInnerHitsContext(context, innerHitsContext);
|
this.childInnerHits.put(innerHitBuilder.getName(), innerHitBuilder);
|
||||||
return innerHitsContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public InnerHitsContext.BaseInnerHits build(SearchContext parentSearchContext,
|
||||||
* Top level inner hits are different than inline inner hits:
|
InnerHitsContext innerHitsContext) throws IOException {
|
||||||
* 1) Nesting. Top level inner hits can be hold nested inner hits, that why this method is recursive (via buildChildInnerHits)
|
QueryShardContext queryShardContext = parentSearchContext.getQueryShardContext();
|
||||||
* 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 {
|
|
||||||
if (nestedPath != null) {
|
if (nestedPath != null) {
|
||||||
ObjectMapper nestedObjectMapper = context.getObjectMapper(nestedPath);
|
ObjectMapper nestedObjectMapper = queryShardContext.getObjectMapper(nestedPath);
|
||||||
ObjectMapper parentObjectMapper = context.nestedScope().nextLevel(nestedObjectMapper);
|
ObjectMapper parentObjectMapper = queryShardContext.nestedScope().nextLevel(nestedObjectMapper);
|
||||||
InnerHitsContext.NestedInnerHits nestedInnerHits = new InnerHitsContext.NestedInnerHits(
|
InnerHitsContext.NestedInnerHits nestedInnerHits = new InnerHitsContext.NestedInnerHits(
|
||||||
name, parentSearchContext, parentObjectMapper, nestedObjectMapper
|
name, parentSearchContext, parentObjectMapper, nestedObjectMapper
|
||||||
);
|
);
|
||||||
setupInnerHitsContext(context, nestedInnerHits);
|
setupInnerHitsContext(queryShardContext, nestedInnerHits);
|
||||||
if (innerHitsBuilder != null) {
|
if (childInnerHits != null) {
|
||||||
buildChildInnerHits(parentSearchContext, context, nestedInnerHits);
|
buildChildInnerHits(parentSearchContext, nestedInnerHits);
|
||||||
}
|
}
|
||||||
context.nestedScope().previousLevel();
|
queryShardContext.nestedScope().previousLevel();
|
||||||
innerHitsContext.addInnerHitDefinition(nestedInnerHits);
|
innerHitsContext.addInnerHitDefinition(nestedInnerHits);
|
||||||
return nestedInnerHits;
|
return nestedInnerHits;
|
||||||
} else if (parentChildType != null) {
|
} else if (parentChildType != null) {
|
||||||
DocumentMapper documentMapper = context.getMapperService().documentMapper(parentChildType);
|
DocumentMapper documentMapper = queryShardContext.getMapperService().documentMapper(parentChildType);
|
||||||
InnerHitsContext.ParentChildInnerHits parentChildInnerHits = new InnerHitsContext.ParentChildInnerHits(
|
InnerHitsContext.ParentChildInnerHits parentChildInnerHits = new InnerHitsContext.ParentChildInnerHits(
|
||||||
name, parentSearchContext, context.getMapperService(), documentMapper
|
name, parentSearchContext, queryShardContext.getMapperService(), documentMapper
|
||||||
);
|
);
|
||||||
setupInnerHitsContext(context, parentChildInnerHits);
|
setupInnerHitsContext(queryShardContext, parentChildInnerHits);
|
||||||
if (innerHitsBuilder != null) {
|
if (childInnerHits != null) {
|
||||||
buildChildInnerHits(parentSearchContext, context, parentChildInnerHits);
|
buildChildInnerHits(parentSearchContext, parentChildInnerHits);
|
||||||
}
|
}
|
||||||
innerHitsContext.addInnerHitDefinition( parentChildInnerHits);
|
innerHitsContext.addInnerHitDefinition( parentChildInnerHits);
|
||||||
return parentChildInnerHits;
|
return parentChildInnerHits;
|
||||||
|
@ -421,12 +457,11 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildChildInnerHits(SearchContext parentSearchContext, QueryShardContext context,
|
private void buildChildInnerHits(SearchContext parentSearchContext, InnerHitsContext.BaseInnerHits innerHits) throws IOException {
|
||||||
InnerHitsContext.BaseInnerHits innerHits) throws IOException {
|
|
||||||
Map<String, InnerHitsContext.BaseInnerHits> childInnerHits = new HashMap<>();
|
Map<String, InnerHitsContext.BaseInnerHits> childInnerHits = new HashMap<>();
|
||||||
for (Map.Entry<String, InnerHitBuilder> entry : innerHitsBuilder.getInnerHitsBuilders().entrySet()) {
|
for (Map.Entry<String, InnerHitBuilder> entry : this.childInnerHits.entrySet()) {
|
||||||
InnerHitsContext.BaseInnerHits childInnerHit = entry.getValue().buildTopLevel(
|
InnerHitsContext.BaseInnerHits childInnerHit = entry.getValue().build(
|
||||||
parentSearchContext, context, new InnerHitsContext()
|
parentSearchContext, new InnerHitsContext()
|
||||||
);
|
);
|
||||||
childInnerHits.put(entry.getKey(), childInnerHit);
|
childInnerHits.put(entry.getKey(), childInnerHit);
|
||||||
}
|
}
|
||||||
|
@ -480,16 +515,23 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl
|
||||||
innerHitsContext.parsedQuery(parsedQuery);
|
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
|
@Override
|
||||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
builder.startObject();
|
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) {
|
if (name != null) {
|
||||||
builder.field(NAME_FIELD.getPreferredName(), name);
|
builder.field(NAME_FIELD.getPreferredName(), name);
|
||||||
}
|
}
|
||||||
|
@ -536,9 +578,12 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl
|
||||||
if (highlightBuilder != null) {
|
if (highlightBuilder != null) {
|
||||||
builder.field(SearchSourceBuilder.HIGHLIGHT_FIELD.getPreferredName(), highlightBuilder, params);
|
builder.field(SearchSourceBuilder.HIGHLIGHT_FIELD.getPreferredName(), highlightBuilder, params);
|
||||||
}
|
}
|
||||||
builder.field(SearchSourceBuilder.QUERY_FIELD.getPreferredName(), query, params);
|
if (childInnerHits != null) {
|
||||||
if (innerHitsBuilder != null) {
|
builder.startObject(INNER_HITS_FIELD.getPreferredName());
|
||||||
builder.field(SearchSourceBuilder.INNER_HITS_FIELD.getPreferredName(), innerHitsBuilder, params);
|
for (Map.Entry<String, InnerHitBuilder> entry : childInnerHits.entrySet()) {
|
||||||
|
builder.field(entry.getKey(), entry.getValue(), params);
|
||||||
|
}
|
||||||
|
builder.endObject();
|
||||||
}
|
}
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
return builder;
|
return builder;
|
||||||
|
@ -565,17 +610,26 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl
|
||||||
Objects.equals(sorts, that.sorts) &&
|
Objects.equals(sorts, that.sorts) &&
|
||||||
Objects.equals(highlightBuilder, that.highlightBuilder) &&
|
Objects.equals(highlightBuilder, that.highlightBuilder) &&
|
||||||
Objects.equals(query, that.query) &&
|
Objects.equals(query, that.query) &&
|
||||||
Objects.equals(innerHitsBuilder, that.innerHitsBuilder);
|
Objects.equals(childInnerHits, that.childInnerHits);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(name, nestedPath, parentChildType, from, size, explain, version, trackScores, fieldNames,
|
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 {
|
public static InnerHitBuilder fromXContent(QueryParseContext context) throws IOException {
|
||||||
return PARSER.parse(context.parser(), new InnerHitBuilder(), context);
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -32,9 +32,9 @@ import org.elasticsearch.common.lucene.search.Queries;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.index.mapper.object.ObjectMapper;
|
import org.elasticsearch.index.mapper.object.ObjectMapper;
|
||||||
import org.elasticsearch.index.query.support.InnerHitBuilder;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder> {
|
public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder> {
|
||||||
|
@ -109,9 +109,7 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
|
||||||
}
|
}
|
||||||
|
|
||||||
public NestedQueryBuilder innerHit(InnerHitBuilder innerHit) {
|
public NestedQueryBuilder innerHit(InnerHitBuilder innerHit) {
|
||||||
innerHit.setNestedPath(path);
|
this.innerHitBuilder = new InnerHitBuilder(innerHit, path, query);
|
||||||
innerHit.setQuery(query);
|
|
||||||
this.innerHitBuilder = innerHit;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,8 +194,14 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new NestedQueryBuilder(path, query, scoreMode, innerHitBuilder).ignoreUnmapped(ignoreUnmapped).queryName(queryName)
|
NestedQueryBuilder queryBuilder = new NestedQueryBuilder(path, query, scoreMode)
|
||||||
|
.ignoreUnmapped(ignoreUnmapped)
|
||||||
|
.queryName(queryName)
|
||||||
.boost(boost);
|
.boost(boost);
|
||||||
|
if (innerHitBuilder != null) {
|
||||||
|
queryBuilder.innerHit(innerHitBuilder);
|
||||||
|
}
|
||||||
|
return queryBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -236,9 +240,6 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
|
||||||
final Query childFilter;
|
final Query childFilter;
|
||||||
final Query innerQuery;
|
final Query innerQuery;
|
||||||
ObjectMapper objectMapper = context.nestedScope().getObjectMapper();
|
ObjectMapper objectMapper = context.nestedScope().getObjectMapper();
|
||||||
if (innerHitBuilder != null) {
|
|
||||||
context.addInnerHit(innerHitBuilder);
|
|
||||||
}
|
|
||||||
if (objectMapper == null) {
|
if (objectMapper == null) {
|
||||||
parentFilter = context.bitsetFilter(Queries.newNonNestedFilter());
|
parentFilter = context.bitsetFilter(Queries.newNonNestedFilter());
|
||||||
} else {
|
} else {
|
||||||
|
@ -265,4 +266,11 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void extractInnerHitBuilders(Map<String, InnerHitBuilder> innerHits) {
|
||||||
|
if (innerHitBuilder != null) {
|
||||||
|
innerHitBuilder.inlineInnerHits(innerHits);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,12 +57,10 @@ import org.elasticsearch.index.mapper.MapperService;
|
||||||
import org.elasticsearch.index.mapper.core.TextFieldMapper;
|
import org.elasticsearch.index.mapper.core.TextFieldMapper;
|
||||||
import org.elasticsearch.index.mapper.object.ObjectMapper;
|
import org.elasticsearch.index.mapper.object.ObjectMapper;
|
||||||
import org.elasticsearch.index.percolator.PercolatorQueryCache;
|
import org.elasticsearch.index.percolator.PercolatorQueryCache;
|
||||||
import org.elasticsearch.index.query.support.InnerHitBuilder;
|
|
||||||
import org.elasticsearch.index.query.support.NestedScope;
|
import org.elasticsearch.index.query.support.NestedScope;
|
||||||
import org.elasticsearch.index.similarity.SimilarityService;
|
import org.elasticsearch.index.similarity.SimilarityService;
|
||||||
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
|
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
|
||||||
import org.elasticsearch.script.ScriptService;
|
import org.elasticsearch.script.ScriptService;
|
||||||
import org.elasticsearch.search.fetch.innerhits.InnerHitsContext;
|
|
||||||
import org.elasticsearch.search.internal.SearchContext;
|
import org.elasticsearch.search.internal.SearchContext;
|
||||||
import org.elasticsearch.search.lookup.SearchLookup;
|
import org.elasticsearch.search.lookup.SearchLookup;
|
||||||
|
|
||||||
|
@ -185,16 +183,6 @@ public class QueryShardContext extends QueryRewriteContext {
|
||||||
return isFilter;
|
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<String> simpleMatchToIndexNames(String pattern) {
|
public Collection<String> simpleMatchToIndexNames(String pattern) {
|
||||||
return mapperService.simpleMatchToIndexNames(pattern);
|
return mapperService.simpleMatchToIndexNames(pattern);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,12 +42,14 @@ import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryParseContext;
|
import org.elasticsearch.index.query.QueryParseContext;
|
||||||
import org.elasticsearch.index.query.QueryRewriteContext;
|
import org.elasticsearch.index.query.QueryRewriteContext;
|
||||||
import org.elasticsearch.index.query.QueryShardContext;
|
import org.elasticsearch.index.query.QueryShardContext;
|
||||||
|
import org.elasticsearch.index.query.InnerHitBuilder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -429,8 +431,15 @@ public class FunctionScoreQueryBuilder extends AbstractQueryBuilder<FunctionScor
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void extractInnerHitBuilders(Map<String, InnerHitBuilder> innerHits) {
|
||||||
|
InnerHitBuilder.extractInnerHits(query(), innerHits);
|
||||||
|
}
|
||||||
|
|
||||||
public static FunctionScoreQueryBuilder fromXContent(ParseFieldRegistry<ScoreFunctionParser<?>> scoreFunctionsRegistry,
|
public static FunctionScoreQueryBuilder fromXContent(ParseFieldRegistry<ScoreFunctionParser<?>> scoreFunctionsRegistry,
|
||||||
QueryParseContext parseContext) throws IOException {
|
QueryParseContext parseContext) throws IOException {
|
||||||
XContentParser parser = parseContext.parser();
|
XContentParser parser = parseContext.parser();
|
||||||
|
|
||||||
QueryBuilder<?> query = null;
|
QueryBuilder<?> query = 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<String, InnerHitBuilder> innerHitsBuilders;
|
|
||||||
|
|
||||||
public InnerHitsBuilder() {
|
|
||||||
this.innerHitsBuilders = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InnerHitsBuilder(Map<String, InnerHitBuilder> 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<String, InnerHitBuilder> 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<String, InnerHitBuilder> getInnerHitsBuilders() {
|
|
||||||
return innerHitsBuilders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
|
||||||
builder.startObject();
|
|
||||||
for (Map.Entry<String, InnerHitBuilder> 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<String, InnerHitBuilder> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -62,7 +62,7 @@ import org.elasticsearch.index.IndexService;
|
||||||
import org.elasticsearch.index.engine.Engine;
|
import org.elasticsearch.index.engine.Engine;
|
||||||
import org.elasticsearch.index.query.QueryParseContext;
|
import org.elasticsearch.index.query.QueryParseContext;
|
||||||
import org.elasticsearch.index.query.QueryShardContext;
|
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.search.stats.StatsGroupsParseElement;
|
||||||
import org.elasticsearch.index.shard.IndexEventListener;
|
import org.elasticsearch.index.shard.IndexEventListener;
|
||||||
import org.elasticsearch.index.shard.IndexShard;
|
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;
|
||||||
import org.elasticsearch.search.fetch.fielddata.FieldDataFieldsContext.FieldDataField;
|
import org.elasticsearch.search.fetch.fielddata.FieldDataFieldsContext.FieldDataField;
|
||||||
import org.elasticsearch.search.fetch.fielddata.FieldDataFieldsFetchSubPhase;
|
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.fetch.script.ScriptFieldsContext.ScriptField;
|
||||||
import org.elasticsearch.search.highlight.HighlightBuilder;
|
import org.elasticsearch.search.highlight.HighlightBuilder;
|
||||||
import org.elasticsearch.search.internal.DefaultSearchContext;
|
import org.elasticsearch.search.internal.DefaultSearchContext;
|
||||||
|
@ -679,12 +678,24 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> imp
|
||||||
context.queryBoost(indexBoost);
|
context.queryBoost(indexBoost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Map<String, InnerHitBuilder> innerHitBuilders = new HashMap<>();
|
||||||
if (source.query() != null) {
|
if (source.query() != null) {
|
||||||
|
InnerHitBuilder.extractInnerHits(source.query(), innerHitBuilders);
|
||||||
context.parsedQuery(queryShardContext.toQuery(source.query()));
|
context.parsedQuery(queryShardContext.toQuery(source.query()));
|
||||||
}
|
}
|
||||||
if (source.postFilter() != null) {
|
if (source.postFilter() != null) {
|
||||||
|
InnerHitBuilder.extractInnerHits(source.postFilter(), innerHitBuilders);
|
||||||
context.parsedPostFilter(queryShardContext.toQuery(source.postFilter()));
|
context.parsedPostFilter(queryShardContext.toQuery(source.postFilter()));
|
||||||
}
|
}
|
||||||
|
if (innerHitBuilders.size() > 0) {
|
||||||
|
for (Map.Entry<String, InnerHitBuilder> 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) {
|
if (source.sorts() != null) {
|
||||||
try {
|
try {
|
||||||
Optional<Sort> optionalSort = SortBuilder.buildSort(source.sorts(), context.getQueryShardContext());
|
Optional<Sort> optionalSort = SortBuilder.buildSort(source.sorts(), context.getQueryShardContext());
|
||||||
|
@ -754,25 +765,6 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> imp
|
||||||
throw new SearchContextException(context, "failed to create SearchContextHighlighter", e);
|
throw new SearchContextException(context, "failed to create SearchContextHighlighter", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (source.innerHits() != null) {
|
|
||||||
for (Map.Entry<String, InnerHitBuilder> 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) {
|
if (source.scriptFields() != null) {
|
||||||
for (org.elasticsearch.search.builder.SearchSourceBuilder.ScriptField field : source.scriptFields()) {
|
for (org.elasticsearch.search.builder.SearchSourceBuilder.ScriptField field : source.scriptFields()) {
|
||||||
SearchScript searchScript = context.scriptService().search(context.lookup(), field.script(), ScriptContext.Standard.SEARCH,
|
SearchScript searchScript = context.scriptService().search(context.lookup(), field.script(), ScriptContext.Standard.SEARCH,
|
||||||
|
|
|
@ -40,7 +40,6 @@ import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryParseContext;
|
import org.elasticsearch.index.query.QueryParseContext;
|
||||||
import org.elasticsearch.index.query.QueryShardContext;
|
import org.elasticsearch.index.query.QueryShardContext;
|
||||||
import org.elasticsearch.index.query.support.InnerHitsBuilder;
|
|
||||||
import org.elasticsearch.script.Script;
|
import org.elasticsearch.script.Script;
|
||||||
import org.elasticsearch.search.aggregations.AggregatorBuilder;
|
import org.elasticsearch.search.aggregations.AggregatorBuilder;
|
||||||
import org.elasticsearch.search.aggregations.AggregatorFactories;
|
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 INDICES_BOOST_FIELD = new ParseField("indices_boost");
|
||||||
public static final ParseField AGGREGATIONS_FIELD = new ParseField("aggregations", "aggs");
|
public static final ParseField AGGREGATIONS_FIELD = new ParseField("aggregations", "aggs");
|
||||||
public static final ParseField HIGHLIGHT_FIELD = new ParseField("highlight");
|
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 SUGGEST_FIELD = new ParseField("suggest");
|
||||||
public static final ParseField RESCORE_FIELD = new ParseField("rescore");
|
public static final ParseField RESCORE_FIELD = new ParseField("rescore");
|
||||||
public static final ParseField STATS_FIELD = new ParseField("stats");
|
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 SuggestBuilder suggestBuilder;
|
||||||
|
|
||||||
private InnerHitsBuilder innerHitsBuilder;
|
|
||||||
|
|
||||||
private List<RescoreBuilder<?>> rescoreBuilders;
|
private List<RescoreBuilder<?>> rescoreBuilders;
|
||||||
|
|
||||||
private ObjectFloatHashMap<String> indexBoost = null;
|
private ObjectFloatHashMap<String> indexBoost = null;
|
||||||
|
@ -205,14 +201,11 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
|
||||||
boolean hasIndexBoost = in.readBoolean();
|
boolean hasIndexBoost = in.readBoolean();
|
||||||
if (hasIndexBoost) {
|
if (hasIndexBoost) {
|
||||||
int size = in.readVInt();
|
int size = in.readVInt();
|
||||||
indexBoost = new ObjectFloatHashMap<String>(size);
|
indexBoost = new ObjectFloatHashMap<>(size);
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
indexBoost.put(in.readString(), in.readFloat());
|
indexBoost.put(in.readString(), in.readFloat());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (in.readBoolean()) {
|
|
||||||
innerHitsBuilder = new InnerHitsBuilder(in);
|
|
||||||
}
|
|
||||||
if (in.readBoolean()) {
|
if (in.readBoolean()) {
|
||||||
minScore = in.readFloat();
|
minScore = in.readFloat();
|
||||||
}
|
}
|
||||||
|
@ -303,11 +296,6 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
|
||||||
out.writeFloat(indexBoost.get(key.value));
|
out.writeFloat(indexBoost.get(key.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean hasInnerHitsBuilder = innerHitsBuilder != null;
|
|
||||||
out.writeBoolean(hasInnerHitsBuilder);
|
|
||||||
if (hasInnerHitsBuilder) {
|
|
||||||
innerHitsBuilder.writeTo(out);
|
|
||||||
}
|
|
||||||
boolean hasMinScore = minScore != null;
|
boolean hasMinScore = minScore != null;
|
||||||
out.writeBoolean(hasMinScore);
|
out.writeBoolean(hasMinScore);
|
||||||
if (hasMinScore) {
|
if (hasMinScore) {
|
||||||
|
@ -653,15 +641,6 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
|
||||||
return highlightBuilder;
|
return highlightBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchSourceBuilder innerHits(InnerHitsBuilder innerHitsBuilder) {
|
|
||||||
this.innerHitsBuilder = innerHitsBuilder;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InnerHitsBuilder innerHits() {
|
|
||||||
return innerHitsBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SearchSourceBuilder suggest(SuggestBuilder suggestBuilder) {
|
public SearchSourceBuilder suggest(SuggestBuilder suggestBuilder) {
|
||||||
this.suggestBuilder = suggestBuilder;
|
this.suggestBuilder = suggestBuilder;
|
||||||
return this;
|
return this;
|
||||||
|
@ -957,7 +936,6 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
|
||||||
rewrittenBuilder.from = from;
|
rewrittenBuilder.from = from;
|
||||||
rewrittenBuilder.highlightBuilder = highlightBuilder;
|
rewrittenBuilder.highlightBuilder = highlightBuilder;
|
||||||
rewrittenBuilder.indexBoost = indexBoost;
|
rewrittenBuilder.indexBoost = indexBoost;
|
||||||
rewrittenBuilder.innerHitsBuilder = innerHitsBuilder;
|
|
||||||
rewrittenBuilder.minScore = minScore;
|
rewrittenBuilder.minScore = minScore;
|
||||||
rewrittenBuilder.postQueryBuilder = postQueryBuilder;
|
rewrittenBuilder.postQueryBuilder = postQueryBuilder;
|
||||||
rewrittenBuilder.profile = profile;
|
rewrittenBuilder.profile = profile;
|
||||||
|
@ -1051,8 +1029,6 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
|
||||||
aggregations = aggParsers.parseAggregators(context);
|
aggregations = aggParsers.parseAggregators(context);
|
||||||
} else if (context.getParseFieldMatcher().match(currentFieldName, HIGHLIGHT_FIELD)) {
|
} else if (context.getParseFieldMatcher().match(currentFieldName, HIGHLIGHT_FIELD)) {
|
||||||
highlightBuilder = HighlightBuilder.fromXContent(context);
|
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)) {
|
} else if (context.getParseFieldMatcher().match(currentFieldName, SUGGEST_FIELD)) {
|
||||||
suggestBuilder = SuggestBuilder.fromXContent(context, suggesters);
|
suggestBuilder = SuggestBuilder.fromXContent(context, suggesters);
|
||||||
} else if (context.getParseFieldMatcher().match(currentFieldName, SORT_FIELD)) {
|
} 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);
|
builder.field(HIGHLIGHT_FIELD.getPreferredName(), highlightBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (innerHitsBuilder != null) {
|
|
||||||
builder.field(INNER_HITS_FIELD.getPreferredName(), innerHitsBuilder, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (suggestBuilder != null) {
|
if (suggestBuilder != null) {
|
||||||
builder.field(SUGGEST_FIELD.getPreferredName(), suggestBuilder);
|
builder.field(SUGGEST_FIELD.getPreferredName(), suggestBuilder);
|
||||||
}
|
}
|
||||||
|
@ -1379,7 +1351,7 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(aggregations, explain, fetchSourceContext, fieldDataFields, fieldNames, from,
|
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);
|
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(from, other.from)
|
||||||
&& Objects.equals(highlightBuilder, other.highlightBuilder)
|
&& Objects.equals(highlightBuilder, other.highlightBuilder)
|
||||||
&& Objects.equals(indexBoost, other.indexBoost)
|
&& Objects.equals(indexBoost, other.indexBoost)
|
||||||
&& Objects.equals(innerHitsBuilder, other.innerHitsBuilder)
|
|
||||||
&& Objects.equals(minScore, other.minScore)
|
&& Objects.equals(minScore, other.minScore)
|
||||||
&& Objects.equals(postQueryBuilder, other.postQueryBuilder)
|
&& Objects.equals(postQueryBuilder, other.postQueryBuilder)
|
||||||
&& Objects.equals(queryBuilder, other.queryBuilder)
|
&& Objects.equals(queryBuilder, other.queryBuilder)
|
||||||
|
|
|
@ -42,7 +42,6 @@ import org.elasticsearch.index.mapper.MapperService;
|
||||||
import org.elasticsearch.index.mapper.Uid;
|
import org.elasticsearch.index.mapper.Uid;
|
||||||
import org.elasticsearch.index.mapper.internal.TypeFieldMapper;
|
import org.elasticsearch.index.mapper.internal.TypeFieldMapper;
|
||||||
import org.elasticsearch.index.mapper.internal.UidFieldMapper;
|
import org.elasticsearch.index.mapper.internal.UidFieldMapper;
|
||||||
import org.elasticsearch.index.query.support.InnerHitBuilder;
|
|
||||||
import org.elasticsearch.index.similarity.SimilarityService;
|
import org.elasticsearch.index.similarity.SimilarityService;
|
||||||
import org.elasticsearch.script.Script.ScriptParseException;
|
import org.elasticsearch.script.Script.ScriptParseException;
|
||||||
import org.elasticsearch.search.fetch.innerhits.InnerHitsContext;
|
import org.elasticsearch.search.fetch.innerhits.InnerHitsContext;
|
||||||
|
@ -53,6 +52,8 @@ import org.junit.BeforeClass;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.containsString;
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
@ -125,18 +126,24 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQue
|
||||||
assertEquals(queryBuilder.scoreMode(), lpq.getScoreMode()); // WTF is this why do we have two?
|
assertEquals(queryBuilder.scoreMode(), lpq.getScoreMode()); // WTF is this why do we have two?
|
||||||
}
|
}
|
||||||
if (queryBuilder.innerHit() != null) {
|
if (queryBuilder.innerHit() != null) {
|
||||||
assertNotNull(SearchContext.current());
|
SearchContext searchContext = SearchContext.current();
|
||||||
|
assertNotNull(searchContext);
|
||||||
if (query != null) {
|
if (query != null) {
|
||||||
assertNotNull(SearchContext.current().innerHits());
|
Map<String, InnerHitBuilder> innerHitBuilders = new HashMap<>();
|
||||||
assertEquals(1, SearchContext.current().innerHits().getInnerHits().size());
|
InnerHitBuilder.extractInnerHits(queryBuilder, innerHitBuilders);
|
||||||
assertTrue(SearchContext.current().innerHits().getInnerHits().containsKey(queryBuilder.innerHit().getName()));
|
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 =
|
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.size(), queryBuilder.innerHit().getSize());
|
||||||
assertEquals(innerHits.sort().getSort().length, 1);
|
assertEquals(innerHits.sort().getSort().length, 1);
|
||||||
assertEquals(innerHits.sort().getSort()[0].getField(), STRING_FIELD_NAME_2);
|
assertEquals(innerHits.sort().getSort()[0].getField(), STRING_FIELD_NAME_2);
|
||||||
} else {
|
} 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<HasChildQue
|
||||||
" \"boost\" : 2.0,\n" +
|
" \"boost\" : 2.0,\n" +
|
||||||
" \"_name\" : \"WNzYMJKRwePuRBh\",\n" +
|
" \"_name\" : \"WNzYMJKRwePuRBh\",\n" +
|
||||||
" \"inner_hits\" : {\n" +
|
" \"inner_hits\" : {\n" +
|
||||||
" \"type\" : \"child\",\n" +
|
|
||||||
" \"name\" : \"inner_hits_name\",\n" +
|
" \"name\" : \"inner_hits_name\",\n" +
|
||||||
" \"from\" : 0,\n" +
|
" \"from\" : 0,\n" +
|
||||||
" \"size\" : 100,\n" +
|
" \"size\" : 100,\n" +
|
||||||
|
@ -199,18 +205,7 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQue
|
||||||
" \"mapped_string\" : {\n" +
|
" \"mapped_string\" : {\n" +
|
||||||
" \"order\" : \"asc\"\n" +
|
" \"order\" : \"asc\"\n" +
|
||||||
" }\n" +
|
" }\n" +
|
||||||
" } ],\n" +
|
" } ]\n" +
|
||||||
" \"query\" : {\n" +
|
|
||||||
" \"range\" : {\n" +
|
|
||||||
" \"mapped_string\" : {\n" +
|
|
||||||
" \"from\" : \"agJhRET\",\n" +
|
|
||||||
" \"to\" : \"zvqIq\",\n" +
|
|
||||||
" \"include_lower\" : true,\n" +
|
|
||||||
" \"include_upper\" : true,\n" +
|
|
||||||
" \"boost\" : 1.0\n" +
|
|
||||||
" }\n" +
|
|
||||||
" }\n" +
|
|
||||||
" }\n" +
|
|
||||||
" }\n" +
|
" }\n" +
|
||||||
" }\n" +
|
" }\n" +
|
||||||
"}";
|
"}";
|
||||||
|
@ -223,11 +218,11 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQue
|
||||||
assertEquals(query, queryBuilder.childType(), "child");
|
assertEquals(query, queryBuilder.childType(), "child");
|
||||||
assertEquals(query, queryBuilder.scoreMode(), ScoreMode.Avg);
|
assertEquals(query, queryBuilder.scoreMode(), ScoreMode.Avg);
|
||||||
assertNotNull(query, queryBuilder.innerHit());
|
assertNotNull(query, queryBuilder.innerHit());
|
||||||
assertEquals(query, queryBuilder.innerHit(), new InnerHitBuilder().setParentChildType("child")
|
InnerHitBuilder expected = new InnerHitBuilder(new InnerHitBuilder(), queryBuilder.query(), "child")
|
||||||
.setName("inner_hits_name")
|
.setName("inner_hits_name")
|
||||||
.setSize(100)
|
.setSize(100)
|
||||||
.addSort(new FieldSortBuilder("mapped_string").order(SortOrder.ASC))
|
.addSort(new FieldSortBuilder("mapped_string").order(SortOrder.ASC));
|
||||||
.setQuery(queryBuilder.query()));
|
assertEquals(query, queryBuilder.innerHit(), expected);
|
||||||
|
|
||||||
}
|
}
|
||||||
public void testToQueryInnerQueryType() throws IOException {
|
public void testToQueryInnerQueryType() throws IOException {
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
package org.elasticsearch.index.query;
|
package org.elasticsearch.index.query;
|
||||||
|
|
||||||
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
|
||||||
import com.fasterxml.jackson.core.JsonParseException;
|
import com.fasterxml.jackson.core.JsonParseException;
|
||||||
|
|
||||||
import org.apache.lucene.search.MatchNoDocsQuery;
|
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||||
|
@ -34,7 +33,6 @@ import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.index.mapper.MapperService;
|
import org.elasticsearch.index.mapper.MapperService;
|
||||||
import org.elasticsearch.index.query.support.InnerHitBuilder;
|
|
||||||
import org.elasticsearch.script.Script.ScriptParseException;
|
import org.elasticsearch.script.Script.ScriptParseException;
|
||||||
import org.elasticsearch.search.fetch.innerhits.InnerHitsContext;
|
import org.elasticsearch.search.fetch.innerhits.InnerHitsContext;
|
||||||
import org.elasticsearch.search.internal.SearchContext;
|
import org.elasticsearch.search.internal.SearchContext;
|
||||||
|
@ -43,7 +41,8 @@ import org.elasticsearch.search.sort.SortOrder;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.containsString;
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
@ -108,18 +107,24 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQ
|
||||||
assertEquals(queryBuilder.score() ? ScoreMode.Max : ScoreMode.None, lpq.getScoreMode());
|
assertEquals(queryBuilder.score() ? ScoreMode.Max : ScoreMode.None, lpq.getScoreMode());
|
||||||
}
|
}
|
||||||
if (queryBuilder.innerHit() != null) {
|
if (queryBuilder.innerHit() != null) {
|
||||||
assertNotNull(SearchContext.current());
|
SearchContext searchContext = SearchContext.current();
|
||||||
|
assertNotNull(searchContext);
|
||||||
if (query != null) {
|
if (query != null) {
|
||||||
assertNotNull(SearchContext.current().innerHits());
|
Map<String, InnerHitBuilder> innerHitBuilders = new HashMap<>();
|
||||||
assertEquals(1, SearchContext.current().innerHits().getInnerHits().size());
|
InnerHitBuilder.extractInnerHits(queryBuilder, innerHitBuilders);
|
||||||
assertTrue(SearchContext.current().innerHits().getInnerHits().containsKey(queryBuilder.innerHit().getName()));
|
for (InnerHitBuilder builder : innerHitBuilders.values()) {
|
||||||
InnerHitsContext.BaseInnerHits innerHits = SearchContext.current().innerHits()
|
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());
|
.getInnerHits().get(queryBuilder.innerHit().getName());
|
||||||
assertEquals(innerHits.size(), queryBuilder.innerHit().getSize());
|
assertEquals(innerHits.size(), queryBuilder.innerHit().getSize());
|
||||||
assertEquals(innerHits.sort().getSort().length, 1);
|
assertEquals(innerHits.sort().getSort().length, 1);
|
||||||
assertEquals(innerHits.sort().getSort()[0].getField(), STRING_FIELD_NAME_2);
|
assertEquals(innerHits.sort().getSort()[0].getField(), STRING_FIELD_NAME_2);
|
||||||
} else {
|
} else {
|
||||||
assertThat(SearchContext.current().innerHits().getInnerHits().size(), equalTo(0));
|
assertThat(searchContext.innerHits().getInnerHits().size(), equalTo(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* 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.equalTo;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
import static org.hamcrest.Matchers.sameInstance;
|
import static org.hamcrest.Matchers.sameInstance;
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -29,6 +31,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.apache.lucene.search.join.ScoreMode;
|
||||||
import org.elasticsearch.common.ParseFieldMatcher;
|
import org.elasticsearch.common.ParseFieldMatcher;
|
||||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||||
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
|
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.XContentHelper;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.index.query.MatchQueryBuilder;
|
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryParseContext;
|
|
||||||
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
|
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
|
||||||
import org.elasticsearch.script.Script;
|
import org.elasticsearch.script.Script;
|
||||||
import org.elasticsearch.script.ScriptService;
|
import org.elasticsearch.script.ScriptService;
|
||||||
|
@ -87,7 +89,7 @@ public class InnerHitBuilderTests extends ESTestCase {
|
||||||
|
|
||||||
public void testFromAndToXContent() throws Exception {
|
public void testFromAndToXContent() throws Exception {
|
||||||
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
|
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
|
||||||
InnerHitBuilder innerHit = randomInnerHits();
|
InnerHitBuilder innerHit = randomInnerHits(true, false);
|
||||||
XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values()));
|
XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values()));
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
builder.prettyPrint();
|
builder.prettyPrint();
|
||||||
|
@ -111,7 +113,7 @@ public class InnerHitBuilderTests extends ESTestCase {
|
||||||
assertTrue("inner it is not equal to self", firstInnerHit.equals(firstInnerHit));
|
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(),
|
assertThat("same inner hit's hashcode returns different values if called multiple times", firstInnerHit.hashCode(),
|
||||||
equalTo(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);
|
InnerHitBuilder secondBuilder = serializedCopy(firstInnerHit);
|
||||||
assertTrue("inner hit is not equal to self", secondBuilder.equals(secondBuilder));
|
assertTrue("inner hit is not equal to self", secondBuilder.equals(secondBuilder));
|
||||||
|
@ -133,18 +135,83 @@ public class InnerHitBuilderTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InnerHitBuilder randomInnerHits() {
|
public void testInlineLeafInnerHitsNestedQuery() throws Exception {
|
||||||
return randomInnerHits(true);
|
InnerHitBuilder leafInnerHits = randomInnerHits();
|
||||||
|
NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("path", new MatchAllQueryBuilder(), ScoreMode.None);
|
||||||
|
nestedQueryBuilder.innerHit(leafInnerHits);
|
||||||
|
Map<String, InnerHitBuilder> innerHitBuilders = new HashMap<>();
|
||||||
|
nestedQueryBuilder.extractInnerHitBuilders(innerHitBuilders);
|
||||||
|
assertThat(innerHitBuilders.get(leafInnerHits.getName()), notNullValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InnerHitBuilder randomInnerHits(boolean recursive) {
|
public void testInlineLeafInnerHitsHasChildQuery() throws Exception {
|
||||||
InnerHitBuilder innerHits = new InnerHitBuilder();
|
InnerHitBuilder leafInnerHits = randomInnerHits();
|
||||||
if (randomBoolean()) {
|
HasChildQueryBuilder hasChildQueryBuilder = new HasChildQueryBuilder("type", new MatchAllQueryBuilder(), ScoreMode.None)
|
||||||
innerHits.setNestedPath(randomAsciiOfLengthBetween(1, 16));
|
.innerHit(leafInnerHits);
|
||||||
} else {
|
Map<String, InnerHitBuilder> innerHitBuilders = new HashMap<>();
|
||||||
innerHits.setParentChildType(randomAsciiOfLengthBetween(1, 16));
|
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<String, InnerHitBuilder> 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<String, InnerHitBuilder> 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<String, InnerHitBuilder> 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<String, InnerHitBuilder> 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<String, InnerHitBuilder> 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.setName(randomAsciiOfLengthBetween(1, 16));
|
||||||
innerHits.setFrom(randomIntBetween(0, 128));
|
innerHits.setFrom(randomIntBetween(0, 128));
|
||||||
innerHits.setSize(randomIntBetween(0, 128));
|
innerHits.setSize(randomIntBetween(0, 128));
|
||||||
|
@ -170,54 +237,76 @@ public class InnerHitBuilderTests extends ESTestCase {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
innerHits.setHighlightBuilder(HighlightBuilderTests.randomHighlighterBuilder());
|
innerHits.setHighlightBuilder(HighlightBuilderTests.randomHighlighterBuilder());
|
||||||
if (randomBoolean()) {
|
|
||||||
innerHits.setQuery(new MatchQueryBuilder(randomAsciiOfLengthBetween(1, 16), randomAsciiOfLengthBetween(1, 16)));
|
|
||||||
}
|
|
||||||
if (recursive && randomBoolean()) {
|
if (recursive && randomBoolean()) {
|
||||||
InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder();
|
|
||||||
int size = randomIntBetween(1, 16);
|
int size = randomIntBetween(1, 16);
|
||||||
for (int i = 0; i < size; i++) {
|
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 {
|
public void testCopyConstructor() throws Exception {
|
||||||
InnerHitBuilder copy = serializedCopy(innerHits);
|
InnerHitBuilder original = randomInnerHits();
|
||||||
int surprise = randomIntBetween(0, 10);
|
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) {
|
switch (surprise) {
|
||||||
case 0:
|
case 0:
|
||||||
copy.setFrom(randomValueOtherThan(innerHits.getFrom(), () -> randomIntBetween(0, 128)));
|
instance.setFrom(randomValueOtherThan(instance.getFrom(), () -> randomIntBetween(0, 128)));
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
copy.setSize(randomValueOtherThan(innerHits.getSize(), () -> randomIntBetween(0, 128)));
|
instance.setSize(randomValueOtherThan(instance.getSize(), () -> randomIntBetween(0, 128)));
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
copy.setExplain(!copy.isExplain());
|
instance.setExplain(!instance.isExplain());
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
copy.setVersion(!copy.isVersion());
|
instance.setVersion(!instance.isVersion());
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
copy.setTrackScores(!copy.isTrackScores());
|
instance.setTrackScores(!instance.isTrackScores());
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
copy.setName(randomValueOtherThan(innerHits.getName(), () -> randomAsciiOfLengthBetween(1, 16)));
|
instance.setName(randomValueOtherThan(instance.getName(), () -> randomAsciiOfLengthBetween(1, 16)));
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
copy.setFieldDataFields(randomValueOtherThan(copy.getFieldDataFields(), () -> {
|
if (randomBoolean()) {
|
||||||
return randomListStuff(16, () -> randomAsciiOfLengthBetween(1, 16));
|
instance.setFieldDataFields(randomValueOtherThan(instance.getFieldDataFields(), () -> {
|
||||||
}));
|
return randomListStuff(16, () -> randomAsciiOfLengthBetween(1, 16));
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
instance.addFieldDataField(randomAsciiOfLengthBetween(1, 16));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
copy.setScriptFields(randomValueOtherThan(copy.getScriptFields(), () -> {
|
if (randomBoolean()) {
|
||||||
return randomListStuff(16, InnerHitBuilderTests::randomScript);}));
|
instance.setScriptFields(randomValueOtherThan(instance.getScriptFields(), () -> {
|
||||||
|
return randomListStuff(16, InnerHitBuilderTests::randomScript);}));
|
||||||
|
} else {
|
||||||
|
SearchSourceBuilder.ScriptField script = randomScript();
|
||||||
|
instance.addScriptField(script.fieldName(), script.script());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
copy.setFetchSourceContext(randomValueOtherThan(copy.getFetchSourceContext(), () -> {
|
instance.setFetchSourceContext(randomValueOtherThan(instance.getFetchSourceContext(), () -> {
|
||||||
FetchSourceContext randomFetchSourceContext;
|
FetchSourceContext randomFetchSourceContext;
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
randomFetchSourceContext = new FetchSourceContext(randomBoolean());
|
randomFetchSourceContext = new FetchSourceContext(randomBoolean());
|
||||||
|
@ -231,21 +320,34 @@ public class InnerHitBuilderTests extends ESTestCase {
|
||||||
}));
|
}));
|
||||||
break;
|
break;
|
||||||
case 9:
|
case 9:
|
||||||
final List<SortBuilder<?>> sortBuilders = randomValueOtherThan(copy.getSorts(), () -> {
|
if (randomBoolean()) {
|
||||||
List<SortBuilder<?>> builders = randomListStuff(16,
|
final List<SortBuilder<?>> sortBuilders = randomValueOtherThan(instance.getSorts(), () -> {
|
||||||
() -> SortBuilders.fieldSort(randomAsciiOfLengthBetween(5, 20)).order(randomFrom(SortOrder.values())));
|
List<SortBuilder<?>> builders = randomListStuff(16,
|
||||||
return builders;
|
() -> SortBuilders.fieldSort(randomAsciiOfLengthBetween(5, 20)).order(randomFrom(SortOrder.values())));
|
||||||
});
|
return builders;
|
||||||
copy.setSorts(sortBuilders);
|
});
|
||||||
|
instance.setSorts(sortBuilders);
|
||||||
|
} else {
|
||||||
|
instance.addSort(SortBuilders.fieldSort(randomAsciiOfLengthBetween(5, 20)));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 10:
|
case 10:
|
||||||
copy.setHighlightBuilder(randomValueOtherThan(copy.getHighlightBuilder(),
|
instance.setHighlightBuilder(randomValueOtherThan(instance.getHighlightBuilder(),
|
||||||
HighlightBuilderTests::randomHighlighterBuilder));
|
HighlightBuilderTests::randomHighlighterBuilder));
|
||||||
break;
|
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:
|
default:
|
||||||
throw new IllegalStateException("unexpected surprise [" + surprise + "]");
|
throw new IllegalStateException("unexpected surprise [" + surprise + "]");
|
||||||
}
|
}
|
||||||
return copy;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SearchSourceBuilder.ScriptField randomScript() {
|
static SearchSourceBuilder.ScriptField randomScript() {
|
|
@ -21,20 +21,25 @@ package org.elasticsearch.index.query;
|
||||||
|
|
||||||
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParseException;
|
||||||
import org.apache.lucene.search.MatchNoDocsQuery;
|
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.search.join.ScoreMode;
|
import org.apache.lucene.search.join.ScoreMode;
|
||||||
import org.apache.lucene.search.join.ToParentBlockJoinQuery;
|
import org.apache.lucene.search.join.ToParentBlockJoinQuery;
|
||||||
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
|
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
|
||||||
|
import org.elasticsearch.common.ParsingException;
|
||||||
import org.elasticsearch.common.compress.CompressedXContent;
|
import org.elasticsearch.common.compress.CompressedXContent;
|
||||||
import org.elasticsearch.index.mapper.MapperService;
|
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.fetch.innerhits.InnerHitsContext;
|
||||||
import org.elasticsearch.search.internal.SearchContext;
|
import org.elasticsearch.search.internal.SearchContext;
|
||||||
import org.elasticsearch.search.sort.FieldSortBuilder;
|
import org.elasticsearch.search.sort.FieldSortBuilder;
|
||||||
import org.elasticsearch.search.sort.SortOrder;
|
import org.elasticsearch.search.sort.SortOrder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.containsString;
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
@ -66,11 +71,11 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBu
|
||||||
protected NestedQueryBuilder doCreateTestQueryBuilder() {
|
protected NestedQueryBuilder doCreateTestQueryBuilder() {
|
||||||
NestedQueryBuilder nqb = new NestedQueryBuilder("nested1", RandomQueryBuilder.createQuery(random()),
|
NestedQueryBuilder nqb = new NestedQueryBuilder("nested1", RandomQueryBuilder.createQuery(random()),
|
||||||
RandomPicks.randomFrom(random(), ScoreMode.values()));
|
RandomPicks.randomFrom(random(), ScoreMode.values()));
|
||||||
if (SearchContext.current() != null) {
|
if (randomBoolean()) {
|
||||||
nqb.innerHit(new InnerHitBuilder()
|
nqb.innerHit(new InnerHitBuilder()
|
||||||
.setName(randomAsciiOfLengthBetween(1, 10))
|
.setName(randomAsciiOfLengthBetween(1, 10))
|
||||||
.setSize(randomIntBetween(0, 100))
|
.setSize(randomIntBetween(0, 100))
|
||||||
.addSort(new FieldSortBuilder(STRING_FIELD_NAME).order(SortOrder.ASC)));
|
.addSort(new FieldSortBuilder(INT_FIELD_NAME).order(SortOrder.ASC)));
|
||||||
}
|
}
|
||||||
nqb.ignoreUnmapped(randomBoolean());
|
nqb.ignoreUnmapped(randomBoolean());
|
||||||
return nqb;
|
return nqb;
|
||||||
|
@ -87,17 +92,23 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBu
|
||||||
//TODO how to assert this?
|
//TODO how to assert this?
|
||||||
}
|
}
|
||||||
if (queryBuilder.innerHit() != null) {
|
if (queryBuilder.innerHit() != null) {
|
||||||
assertNotNull(SearchContext.current());
|
SearchContext searchContext = SearchContext.current();
|
||||||
|
assertNotNull(searchContext);
|
||||||
if (query != null) {
|
if (query != null) {
|
||||||
assertNotNull(SearchContext.current().innerHits());
|
Map<String, InnerHitBuilder> innerHitBuilders = new HashMap<>();
|
||||||
assertEquals(1, SearchContext.current().innerHits().getInnerHits().size());
|
InnerHitBuilder.extractInnerHits(queryBuilder, innerHitBuilders);
|
||||||
assertTrue(SearchContext.current().innerHits().getInnerHits().containsKey("inner_hits_name"));
|
for (InnerHitBuilder builder : innerHitBuilders.values()) {
|
||||||
InnerHitsContext.BaseInnerHits innerHits = SearchContext.current().innerHits().getInnerHits().get("inner_hits_name");
|
builder.build(searchContext, searchContext.innerHits());
|
||||||
assertEquals(innerHits.size(), 100);
|
}
|
||||||
|
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().length, 1);
|
||||||
assertEquals(innerHits.sort().getSort()[0].getField(), STRING_FIELD_NAME);
|
assertEquals(innerHits.sort().getSort()[0].getField(), INT_FIELD_NAME);
|
||||||
} else {
|
} 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<NestedQueryBu
|
||||||
assertEquals(json, ScoreMode.Avg, parsed.scoreMode());
|
assertEquals(json, ScoreMode.Avg, parsed.scoreMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* override superclass test, because here we need to take care that mutation doesn't happen inside
|
||||||
|
* `inner_hits` structure, because we don't parse them yet and so no exception will be triggered
|
||||||
|
* for any mutation there.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void testUnknownObjectException() throws IOException {
|
||||||
|
String validQuery = createTestQueryBuilder().toString();
|
||||||
|
assertThat(validQuery, containsString("{"));
|
||||||
|
int endPosition = validQuery.indexOf("inner_hits");
|
||||||
|
if (endPosition == -1) {
|
||||||
|
endPosition = validQuery.length() - 1;
|
||||||
|
}
|
||||||
|
for (int insertionPosition = 0; insertionPosition < endPosition; insertionPosition++) {
|
||||||
|
if (validQuery.charAt(insertionPosition) == '{') {
|
||||||
|
String testQuery = validQuery.substring(0, insertionPosition) + "{ \"newField\" : " +
|
||||||
|
validQuery.substring(insertionPosition) + "}";
|
||||||
|
try {
|
||||||
|
parseQuery(testQuery);
|
||||||
|
fail("some parsing exception expected for query: " + testQuery);
|
||||||
|
} catch (ParsingException | Script.ScriptParseException | ElasticsearchParseException e) {
|
||||||
|
// different kinds of exception wordings depending on location
|
||||||
|
// of mutation, so no simple asserts possible here
|
||||||
|
} catch (JsonParseException e) {
|
||||||
|
// mutation produced invalid json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void testIgnoreUnmapped() throws IOException {
|
public void testIgnoreUnmapped() throws IOException {
|
||||||
final NestedQueryBuilder queryBuilder = new NestedQueryBuilder("unmapped", new MatchAllQueryBuilder(), ScoreMode.None);
|
final NestedQueryBuilder queryBuilder = new NestedQueryBuilder("unmapped", new MatchAllQueryBuilder(), ScoreMode.None);
|
||||||
queryBuilder.ignoreUnmapped(true);
|
queryBuilder.ignoreUnmapped(true);
|
||||||
|
|
|
@ -1,140 +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.common.ParseFieldMatcher;
|
|
||||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
|
||||||
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
|
|
||||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
|
||||||
import org.elasticsearch.common.settings.Settings;
|
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
|
||||||
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.QueryParseContext;
|
|
||||||
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
|
|
||||||
import org.elasticsearch.search.SearchModule;
|
|
||||||
import org.elasticsearch.test.ESTestCase;
|
|
||||||
import org.junit.AfterClass;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
|
||||||
import static org.hamcrest.Matchers.not;
|
|
||||||
import static org.hamcrest.Matchers.sameInstance;
|
|
||||||
|
|
||||||
public class InnerHitsBuilderTests extends ESTestCase {
|
|
||||||
|
|
||||||
private static final int NUMBER_OF_TESTBUILDERS = 20;
|
|
||||||
private static NamedWriteableRegistry namedWriteableRegistry;
|
|
||||||
private static IndicesQueriesRegistry indicesQueriesRegistry;
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
public static void init() {
|
|
||||||
namedWriteableRegistry = new NamedWriteableRegistry();
|
|
||||||
indicesQueriesRegistry = new SearchModule(Settings.EMPTY, namedWriteableRegistry).getQueryParserRegistry();
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterClass
|
|
||||||
public static void afterClass() throws Exception {
|
|
||||||
namedWriteableRegistry = null;
|
|
||||||
indicesQueriesRegistry = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSerialization() throws Exception {
|
|
||||||
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
|
|
||||||
InnerHitsBuilder original = randomInnerHits();
|
|
||||||
InnerHitsBuilder deserialized = serializedCopy(original);
|
|
||||||
assertEquals(deserialized, original);
|
|
||||||
assertEquals(deserialized.hashCode(), original.hashCode());
|
|
||||||
assertNotSame(deserialized, original);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testFromAndToXContent() throws Exception {
|
|
||||||
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
|
|
||||||
InnerHitsBuilder innerHits = randomInnerHits();
|
|
||||||
XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values()));
|
|
||||||
if (randomBoolean()) {
|
|
||||||
builder.prettyPrint();
|
|
||||||
}
|
|
||||||
innerHits.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
|
||||||
|
|
||||||
XContentParser parser = XContentHelper.createParser(builder.bytes());
|
|
||||||
QueryParseContext context = new QueryParseContext(indicesQueriesRegistry, parser, ParseFieldMatcher.EMPTY);
|
|
||||||
parser.nextToken();
|
|
||||||
InnerHitsBuilder secondInnerHits = InnerHitsBuilder.fromXContent(context);
|
|
||||||
assertThat(innerHits, not(sameInstance(secondInnerHits)));
|
|
||||||
assertThat(innerHits, equalTo(secondInnerHits));
|
|
||||||
assertThat(innerHits.hashCode(), equalTo(secondInnerHits.hashCode()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testEqualsAndHashcode() throws IOException {
|
|
||||||
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
|
|
||||||
InnerHitsBuilder firstInnerHits = randomInnerHits();
|
|
||||||
assertFalse("inner hit is equal to null", firstInnerHits.equals(null));
|
|
||||||
assertFalse("inner hit is equal to incompatible type", firstInnerHits.equals(""));
|
|
||||||
assertTrue("inner it is not equal to self", firstInnerHits.equals(firstInnerHits));
|
|
||||||
assertThat("same inner hit's hashcode returns different values if called multiple times", firstInnerHits.hashCode(),
|
|
||||||
equalTo(firstInnerHits.hashCode()));
|
|
||||||
|
|
||||||
InnerHitsBuilder secondBuilder = serializedCopy(firstInnerHits);
|
|
||||||
assertTrue("inner hit is not equal to self", secondBuilder.equals(secondBuilder));
|
|
||||||
assertTrue("inner hit is not equal to its copy", firstInnerHits.equals(secondBuilder));
|
|
||||||
assertTrue("equals is not symmetric", secondBuilder.equals(firstInnerHits));
|
|
||||||
assertThat("inner hits copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
|
|
||||||
equalTo(firstInnerHits.hashCode()));
|
|
||||||
|
|
||||||
InnerHitsBuilder thirdBuilder = serializedCopy(secondBuilder);
|
|
||||||
assertTrue("inner hit is not equal to self", thirdBuilder.equals(thirdBuilder));
|
|
||||||
assertTrue("inner hit is not equal to its copy", secondBuilder.equals(thirdBuilder));
|
|
||||||
assertThat("inner hit copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
|
|
||||||
equalTo(thirdBuilder.hashCode()));
|
|
||||||
assertTrue("equals is not transitive", firstInnerHits.equals(thirdBuilder));
|
|
||||||
assertThat("inner hit copy's hashcode is different from original hashcode", firstInnerHits.hashCode(),
|
|
||||||
equalTo(thirdBuilder.hashCode()));
|
|
||||||
assertTrue("equals is not symmetric", thirdBuilder.equals(secondBuilder));
|
|
||||||
assertTrue("equals is not symmetric", thirdBuilder.equals(firstInnerHits));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static InnerHitsBuilder randomInnerHits() {
|
|
||||||
InnerHitsBuilder innerHits = new InnerHitsBuilder();
|
|
||||||
int numInnerHits = randomIntBetween(0, 12);
|
|
||||||
for (int i = 0; i < numInnerHits; i++) {
|
|
||||||
innerHits.addInnerHit(randomAsciiOfLength(5), InnerHitBuilderTests.randomInnerHits());
|
|
||||||
}
|
|
||||||
return innerHits;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static InnerHitsBuilder serializedCopy(InnerHitsBuilder original) throws IOException {
|
|
||||||
try (BytesStreamOutput output = new BytesStreamOutput()) {
|
|
||||||
original.writeTo(output);
|
|
||||||
try (StreamInput in = new NamedWriteableAwareStreamInput(StreamInput.wrap(output.bytes()), namedWriteableRegistry)) {
|
|
||||||
return new InnerHitsBuilder(in);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -43,7 +43,7 @@ import org.elasticsearch.index.query.Operator;
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
import org.elasticsearch.index.query.QueryShardException;
|
import org.elasticsearch.index.query.QueryShardException;
|
||||||
import org.elasticsearch.index.query.functionscore.WeightBuilder;
|
import org.elasticsearch.index.query.functionscore.WeightBuilder;
|
||||||
import org.elasticsearch.index.query.support.InnerHitBuilder;
|
import org.elasticsearch.index.query.InnerHitBuilder;
|
||||||
import org.elasticsearch.search.highlight.HighlightBuilder;
|
import org.elasticsearch.search.highlight.HighlightBuilder;
|
||||||
import org.elasticsearch.test.ESIntegTestCase;
|
import org.elasticsearch.test.ESIntegTestCase;
|
||||||
|
|
||||||
|
@ -1827,35 +1827,6 @@ public class PercolatorIT extends ESIntegTestCase {
|
||||||
assertThat(response1.getMatches()[0].getId().string(), equalTo("1"));
|
assertThat(response1.getMatches()[0].getId().string(), equalTo("1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFailNicelyWithInnerHits() throws Exception {
|
|
||||||
XContentBuilder mapping = XContentFactory.jsonBuilder().startObject()
|
|
||||||
.startObject("mapping")
|
|
||||||
.startObject("properties")
|
|
||||||
.startObject("nested")
|
|
||||||
.field("type", "nested")
|
|
||||||
.startObject("properties")
|
|
||||||
.startObject("name")
|
|
||||||
.field("type", "text")
|
|
||||||
.endObject()
|
|
||||||
.endObject()
|
|
||||||
.endObject()
|
|
||||||
.endObject()
|
|
||||||
.endObject();
|
|
||||||
|
|
||||||
assertAcked(prepareCreate(INDEX_NAME)
|
|
||||||
.addMapping(TYPE_NAME, "query", "type=percolator")
|
|
||||||
.addMapping("mapping", mapping));
|
|
||||||
try {
|
|
||||||
client().prepareIndex(INDEX_NAME, TYPE_NAME, "1")
|
|
||||||
.setSource(jsonBuilder().startObject().field("query", nestedQuery("nested", matchQuery("nested.name", "value"), ScoreMode.Avg).innerHit(new InnerHitBuilder())).endObject())
|
|
||||||
.execute().actionGet();
|
|
||||||
fail("Expected a parse error, because inner_hits isn't supported in the percolate api");
|
|
||||||
} catch (Exception e) {
|
|
||||||
assertThat(e.getCause(), instanceOf(QueryShardException.class));
|
|
||||||
assertThat(e.getCause().getMessage(), containsString("inner_hits unsupported"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testParentChild() throws Exception {
|
public void testParentChild() throws Exception {
|
||||||
// We don't fail p/c queries, but those queries are unusable because only a single document can be provided in
|
// We don't fail p/c queries, but those queries are unusable because only a single document can be provided in
|
||||||
// the percolate api
|
// the percolate api
|
||||||
|
|
|
@ -52,8 +52,6 @@ import org.elasticsearch.index.query.AbstractQueryTestCase;
|
||||||
import org.elasticsearch.index.query.EmptyQueryBuilder;
|
import org.elasticsearch.index.query.EmptyQueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
import org.elasticsearch.index.query.QueryParseContext;
|
import org.elasticsearch.index.query.QueryParseContext;
|
||||||
import org.elasticsearch.index.query.support.InnerHitBuilderTests;
|
|
||||||
import org.elasticsearch.index.query.support.InnerHitsBuilder;
|
|
||||||
import org.elasticsearch.indices.IndicesModule;
|
import org.elasticsearch.indices.IndicesModule;
|
||||||
import org.elasticsearch.indices.breaker.CircuitBreakerService;
|
import org.elasticsearch.indices.breaker.CircuitBreakerService;
|
||||||
import org.elasticsearch.indices.breaker.NoneCircuitBreakerService;
|
import org.elasticsearch.indices.breaker.NoneCircuitBreakerService;
|
||||||
|
@ -410,14 +408,6 @@ public class SearchSourceBuilderTests extends ESTestCase {
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
builder.suggest(SuggestBuilderTests.randomSuggestBuilder());
|
builder.suggest(SuggestBuilderTests.randomSuggestBuilder());
|
||||||
}
|
}
|
||||||
if (randomBoolean()) {
|
|
||||||
InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder();
|
|
||||||
int num = randomIntBetween(0, 3);
|
|
||||||
for (int i = 0; i < num; i++) {
|
|
||||||
innerHitsBuilder.addInnerHit(randomAsciiOfLengthBetween(5, 20), InnerHitBuilderTests.randomInnerHits());
|
|
||||||
}
|
|
||||||
builder.innerHits(innerHitsBuilder);
|
|
||||||
}
|
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
int numRescores = randomIntBetween(1, 5);
|
int numRescores = randomIntBetween(1, 5);
|
||||||
for (int i = 0; i < numRescores; i++) {
|
for (int i = 0; i < numRescores; i++) {
|
||||||
|
|
|
@ -22,14 +22,11 @@ package org.elasticsearch.search.innerhits;
|
||||||
import org.apache.lucene.search.join.ScoreMode;
|
import org.apache.lucene.search.join.ScoreMode;
|
||||||
import org.apache.lucene.util.ArrayUtil;
|
import org.apache.lucene.util.ArrayUtil;
|
||||||
import org.elasticsearch.action.index.IndexRequestBuilder;
|
import org.elasticsearch.action.index.IndexRequestBuilder;
|
||||||
import org.elasticsearch.action.search.SearchRequest;
|
|
||||||
import org.elasticsearch.action.search.SearchResponse;
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
import org.elasticsearch.cluster.health.ClusterHealthStatus;
|
import org.elasticsearch.cluster.health.ClusterHealthStatus;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.index.query.BoolQueryBuilder;
|
import org.elasticsearch.index.query.BoolQueryBuilder;
|
||||||
import org.elasticsearch.index.query.MatchAllQueryBuilder;
|
import org.elasticsearch.index.query.InnerHitBuilder;
|
||||||
import org.elasticsearch.index.query.support.InnerHitBuilder;
|
|
||||||
import org.elasticsearch.index.query.support.InnerHitsBuilder;
|
|
||||||
import org.elasticsearch.plugins.Plugin;
|
import org.elasticsearch.plugins.Plugin;
|
||||||
import org.elasticsearch.script.MockScriptEngine;
|
import org.elasticsearch.script.MockScriptEngine;
|
||||||
import org.elasticsearch.script.Script;
|
import org.elasticsearch.script.Script;
|
||||||
|
@ -68,8 +65,6 @@ import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
import static org.hamcrest.Matchers.nullValue;
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
public class InnerHitsIT extends ESIntegTestCase {
|
public class InnerHitsIT extends ESIntegTestCase {
|
||||||
@Override
|
@Override
|
||||||
protected Collection<Class<? extends Plugin>> nodePlugins() {
|
protected Collection<Class<? extends Plugin>> nodePlugins() {
|
||||||
|
@ -112,105 +107,62 @@ public class InnerHitsIT extends ESIntegTestCase {
|
||||||
.endObject()));
|
.endObject()));
|
||||||
indexRandom(true, requests);
|
indexRandom(true, requests);
|
||||||
|
|
||||||
InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder();
|
SearchResponse response = client().prepareSearch("articles")
|
||||||
innerHitsBuilder.addInnerHit("comment", new InnerHitBuilder()
|
.setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg)
|
||||||
.setNestedPath("comments")
|
.innerHit(new InnerHitBuilder().setName("comment"))
|
||||||
.setQuery(matchQuery("comments.message", "fox"))
|
).get();
|
||||||
);
|
assertNoFailures(response);
|
||||||
// Inner hits can be defined in two ways: 1) with the query 2) as separate inner_hit definition
|
assertHitCount(response, 1);
|
||||||
SearchRequest[] searchRequests = new SearchRequest[]{
|
assertSearchHit(response, 1, hasId("1"));
|
||||||
client().prepareSearch("articles").setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit(
|
assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1));
|
||||||
new InnerHitBuilder().setName("comment"))).request(),
|
SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment");
|
||||||
client().prepareSearch("articles").setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg))
|
assertThat(innerHits.totalHits(), equalTo(2L));
|
||||||
.innerHits(innerHitsBuilder).request()
|
assertThat(innerHits.getHits().length, equalTo(2));
|
||||||
};
|
assertThat(innerHits.getAt(0).getId(), equalTo("1"));
|
||||||
for (SearchRequest searchRequest : searchRequests) {
|
assertThat(innerHits.getAt(0).getNestedIdentity().getField().string(), equalTo("comments"));
|
||||||
SearchResponse response = client().search(searchRequest).actionGet();
|
assertThat(innerHits.getAt(0).getNestedIdentity().getOffset(), equalTo(0));
|
||||||
assertNoFailures(response);
|
assertThat(innerHits.getAt(1).getId(), equalTo("1"));
|
||||||
assertHitCount(response, 1);
|
assertThat(innerHits.getAt(1).getNestedIdentity().getField().string(), equalTo("comments"));
|
||||||
assertSearchHit(response, 1, hasId("1"));
|
assertThat(innerHits.getAt(1).getNestedIdentity().getOffset(), equalTo(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();
|
response = client().prepareSearch("articles")
|
||||||
innerHitsBuilder.addInnerHit("comment", new InnerHitBuilder()
|
.setQuery(nestedQuery("comments", matchQuery("comments.message", "elephant"), ScoreMode.Avg)
|
||||||
.setQuery(matchQuery("comments.message", "elephant")).setNestedPath("comments")
|
.innerHit(new InnerHitBuilder().setName("comment"))
|
||||||
);
|
).get();
|
||||||
// Inner hits can be defined in two ways: 1) with the query 2) as
|
assertNoFailures(response);
|
||||||
// separate inner_hit definition
|
assertHitCount(response, 1);
|
||||||
searchRequests = new SearchRequest[] {
|
assertSearchHit(response, 1, hasId("2"));
|
||||||
client().prepareSearch("articles")
|
assertThat(response.getHits().getAt(0).getShard(), notNullValue());
|
||||||
.setQuery(nestedQuery("comments", matchQuery("comments.message", "elephant"), ScoreMode.Avg))
|
assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1));
|
||||||
.innerHits(innerHitsBuilder).request(),
|
innerHits = response.getHits().getAt(0).getInnerHits().get("comment");
|
||||||
client().prepareSearch("articles")
|
assertThat(innerHits.totalHits(), equalTo(3L));
|
||||||
.setQuery(nestedQuery("comments", matchQuery("comments.message", "elephant"), ScoreMode.Avg).innerHit(new InnerHitBuilder().setName("comment"))).request(),
|
assertThat(innerHits.getHits().length, equalTo(3));
|
||||||
client().prepareSearch("articles")
|
assertThat(innerHits.getAt(0).getId(), equalTo("2"));
|
||||||
.setQuery(nestedQuery("comments", matchQuery("comments.message", "elephant"), ScoreMode.Avg).innerHit(new InnerHitBuilder().setName("comment").addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC)))).request()
|
assertThat(innerHits.getAt(0).getNestedIdentity().getField().string(), equalTo("comments"));
|
||||||
};
|
assertThat(innerHits.getAt(0).getNestedIdentity().getOffset(), equalTo(0));
|
||||||
for (SearchRequest searchRequest : searchRequests) {
|
assertThat(innerHits.getAt(1).getId(), equalTo("2"));
|
||||||
SearchResponse response = client().search(searchRequest).actionGet();
|
assertThat(innerHits.getAt(1).getNestedIdentity().getField().string(), equalTo("comments"));
|
||||||
assertNoFailures(response);
|
assertThat(innerHits.getAt(1).getNestedIdentity().getOffset(), equalTo(1));
|
||||||
assertHitCount(response, 1);
|
assertThat(innerHits.getAt(2).getId(), equalTo("2"));
|
||||||
assertSearchHit(response, 1, hasId("2"));
|
assertThat(innerHits.getAt(2).getNestedIdentity().getField().string(), equalTo("comments"));
|
||||||
assertThat(response.getHits().getAt(0).getShard(), notNullValue());
|
assertThat(innerHits.getAt(2).getNestedIdentity().getOffset(), equalTo(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(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()
|
|
||||||
};
|
|
||||||
|
|
||||||
for (SearchRequest searchRequest : searchRequests) {
|
response = client().prepareSearch("articles")
|
||||||
SearchResponse response = client().search(searchRequest).actionGet();
|
.setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit(
|
||||||
assertNoFailures(response);
|
new InnerHitBuilder().setHighlightBuilder(new HighlightBuilder().field("comments.message"))
|
||||||
SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comments");
|
.setExplain(true)
|
||||||
assertThat(innerHits.getTotalHits(), equalTo(2L));
|
.addFieldDataField("comments.message")
|
||||||
assertThat(innerHits.getHits().length, equalTo(1));
|
.addScriptField("script", new Script("5", ScriptService.ScriptType.INLINE, MockScriptEngine.NAME, Collections.emptyMap()))
|
||||||
assertThat(innerHits.getAt(0).getHighlightFields().get("comments.message").getFragments()[0].string(), equalTo("<em>fox</em> eat quick"));
|
.setSize(1)
|
||||||
assertThat(innerHits.getAt(0).explanation().toString(), containsString("weight(comments.message:fox in"));
|
)).get();
|
||||||
assertThat(innerHits.getAt(0).getFields().get("comments.message").getValue().toString(), equalTo("eat"));
|
assertNoFailures(response);
|
||||||
assertThat(innerHits.getAt(0).getFields().get("script").getValue().toString(), equalTo("5"));
|
innerHits = response.getHits().getAt(0).getInnerHits().get("comments");
|
||||||
}
|
assertThat(innerHits.getTotalHits(), equalTo(2L));
|
||||||
|
assertThat(innerHits.getHits().length, equalTo(1));
|
||||||
|
assertThat(innerHits.getAt(0).getHighlightFields().get("comments.message").getFragments()[0].string(), equalTo("<em>fox</em> eat quick"));
|
||||||
|
assertThat(innerHits.getAt(0).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 {
|
public void testRandomNested() throws Exception {
|
||||||
|
@ -237,38 +189,16 @@ public class InnerHitsIT extends ESIntegTestCase {
|
||||||
indexRandom(true, requestBuilders);
|
indexRandom(true, requestBuilders);
|
||||||
|
|
||||||
int size = randomIntBetween(0, numDocs);
|
int size = randomIntBetween(0, numDocs);
|
||||||
SearchResponse searchResponse;
|
BoolQueryBuilder boolQuery = new BoolQueryBuilder();
|
||||||
if (randomBoolean()) {
|
boolQuery.should(nestedQuery("field1", matchAllQuery(), ScoreMode.Avg).innerHit(new InnerHitBuilder().setName("a").setSize(size)
|
||||||
InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder();
|
.addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC))));
|
||||||
innerHitsBuilder.addInnerHit("a", new InnerHitBuilder().setNestedPath("field1")
|
boolQuery.should(nestedQuery("field2", matchAllQuery(), ScoreMode.Avg).innerHit(new InnerHitBuilder().setName("b")
|
||||||
// Sort order is DESC, because we reverse the inner objects during indexing!
|
.addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC)).setSize(size)));
|
||||||
.addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC)).setSize(size));
|
SearchResponse searchResponse = client().prepareSearch("idx")
|
||||||
innerHitsBuilder.addInnerHit("b", new InnerHitBuilder().setNestedPath("field2")
|
.setQuery(boolQuery)
|
||||||
.addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC)).setSize(size));
|
.setSize(numDocs)
|
||||||
searchResponse = client().prepareSearch("idx")
|
.addSort("_uid", SortOrder.ASC)
|
||||||
.setSize(numDocs)
|
.get();
|
||||||
.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();
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNoFailures(searchResponse);
|
assertNoFailures(searchResponse);
|
||||||
assertHitCount(searchResponse, numDocs);
|
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"));
|
requests.add(client().prepareIndex("articles", "comment", "6").setParent("2").setSource("message", "elephant scared by mice x y"));
|
||||||
indexRandom(true, requests);
|
indexRandom(true, requests);
|
||||||
|
|
||||||
InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder();
|
SearchResponse response = client().prepareSearch("articles")
|
||||||
innerHitsBuilder.addInnerHit("comment", new InnerHitBuilder().setParentChildType("comment")
|
.setQuery(hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit(new InnerHitBuilder()))
|
||||||
.setQuery(matchQuery("message", "fox")));
|
.get();
|
||||||
SearchRequest[] searchRequests = new SearchRequest[]{
|
assertNoFailures(response);
|
||||||
client().prepareSearch("articles")
|
assertHitCount(response, 1);
|
||||||
.setQuery(hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None))
|
assertSearchHit(response, 1, hasId("1"));
|
||||||
.innerHits(innerHitsBuilder)
|
assertThat(response.getHits().getAt(0).getShard(), notNullValue());
|
||||||
.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());
|
|
||||||
|
|
||||||
assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(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("comment");
|
||||||
assertThat(innerHits.totalHits(), equalTo(2L));
|
assertThat(innerHits.totalHits(), equalTo(2L));
|
||||||
|
|
||||||
assertThat(innerHits.getAt(0).getId(), equalTo("1"));
|
assertThat(innerHits.getAt(0).getId(), equalTo("1"));
|
||||||
assertThat(innerHits.getAt(0).type(), equalTo("comment"));
|
assertThat(innerHits.getAt(0).type(), equalTo("comment"));
|
||||||
assertThat(innerHits.getAt(1).getId(), equalTo("2"));
|
assertThat(innerHits.getAt(1).getId(), equalTo("2"));
|
||||||
assertThat(innerHits.getAt(1).type(), equalTo("comment"));
|
assertThat(innerHits.getAt(1).type(), equalTo("comment"));
|
||||||
}
|
|
||||||
|
|
||||||
innerHitsBuilder = new InnerHitsBuilder();
|
response = client().prepareSearch("articles")
|
||||||
innerHitsBuilder.addInnerHit("comment", new InnerHitBuilder().setParentChildType("comment")
|
.setQuery(hasChildQuery("comment", matchQuery("message", "elephant"), ScoreMode.None).innerHit(new InnerHitBuilder()))
|
||||||
.setQuery(matchQuery("message", "elephant")));
|
.get();
|
||||||
searchRequests = new SearchRequest[] {
|
assertNoFailures(response);
|
||||||
client().prepareSearch("articles")
|
assertHitCount(response, 1);
|
||||||
.setQuery(hasChildQuery("comment", matchQuery("message", "elephant"), ScoreMode.None))
|
assertSearchHit(response, 1, hasId("2"));
|
||||||
.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"));
|
|
||||||
|
|
||||||
assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1));
|
assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1));
|
||||||
SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment");
|
innerHits = response.getHits().getAt(0).getInnerHits().get("comment");
|
||||||
assertThat(innerHits.totalHits(), equalTo(3L));
|
assertThat(innerHits.totalHits(), equalTo(3L));
|
||||||
|
|
||||||
assertThat(innerHits.getAt(0).getId(), equalTo("4"));
|
assertThat(innerHits.getAt(0).getId(), equalTo("4"));
|
||||||
assertThat(innerHits.getAt(0).type(), equalTo("comment"));
|
assertThat(innerHits.getAt(0).type(), equalTo("comment"));
|
||||||
assertThat(innerHits.getAt(1).getId(), equalTo("5"));
|
assertThat(innerHits.getAt(1).getId(), equalTo("5"));
|
||||||
assertThat(innerHits.getAt(1).type(), equalTo("comment"));
|
assertThat(innerHits.getAt(1).type(), equalTo("comment"));
|
||||||
assertThat(innerHits.getAt(2).getId(), equalTo("6"));
|
assertThat(innerHits.getAt(2).getId(), equalTo("6"));
|
||||||
assertThat(innerHits.getAt(2).type(), equalTo("comment"));
|
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(),
|
|
||||||
|
|
||||||
client().prepareSearch("articles")
|
response = client().prepareSearch("articles")
|
||||||
.setQuery(
|
.setQuery(
|
||||||
hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit(
|
hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit(
|
||||||
new InnerHitBuilder()
|
new InnerHitBuilder()
|
||||||
.addFieldDataField("message")
|
.addFieldDataField("message")
|
||||||
.setHighlightBuilder(new HighlightBuilder().field("message"))
|
.setHighlightBuilder(new HighlightBuilder().field("message"))
|
||||||
.setExplain(true).setSize(1)
|
.setExplain(true).setSize(1)
|
||||||
.addScriptField("script", new Script("5", ScriptService.ScriptType.INLINE,
|
.addScriptField("script", new Script("5", ScriptService.ScriptType.INLINE,
|
||||||
MockScriptEngine.NAME, Collections.emptyMap()))
|
MockScriptEngine.NAME, Collections.emptyMap()))
|
||||||
)
|
)
|
||||||
).request() };
|
).get();
|
||||||
|
assertNoFailures(response);
|
||||||
for (SearchRequest searchRequest : searchRequests) {
|
innerHits = response.getHits().getAt(0).getInnerHits().get("comment");
|
||||||
SearchResponse response = client().search(searchRequest).actionGet();
|
assertThat(innerHits.getHits().length, equalTo(1));
|
||||||
assertNoFailures(response);
|
assertThat(innerHits.getAt(0).getHighlightFields().get("message").getFragments()[0].string(), equalTo("<em>fox</em> eat quick"));
|
||||||
SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment");
|
assertThat(innerHits.getAt(0).explanation().toString(), containsString("weight(message:fox"));
|
||||||
assertThat(innerHits.getHits().length, equalTo(1));
|
assertThat(innerHits.getAt(0).getFields().get("message").getValue().toString(), equalTo("eat"));
|
||||||
assertThat(innerHits.getAt(0).getHighlightFields().get("message").getFragments()[0].string(), equalTo("<em>fox</em> eat quick"));
|
assertThat(innerHits.getAt(0).getFields().get("script").getValue().toString(), equalTo("5"));
|
||||||
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 {
|
public void testRandomParentChild() throws Exception {
|
||||||
|
@ -442,33 +329,17 @@ public class InnerHitsIT extends ESIntegTestCase {
|
||||||
indexRandom(true, requestBuilders);
|
indexRandom(true, requestBuilders);
|
||||||
|
|
||||||
int size = randomIntBetween(0, numDocs);
|
int size = randomIntBetween(0, numDocs);
|
||||||
InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder();
|
BoolQueryBuilder boolQuery = new BoolQueryBuilder();
|
||||||
innerHitsBuilder.addInnerHit("a", new InnerHitBuilder().setParentChildType("child1").addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size));
|
boolQuery.should(constantScoreQuery(hasChildQuery("child1", matchAllQuery(), ScoreMode.None)
|
||||||
innerHitsBuilder.addInnerHit("b", new InnerHitBuilder().setParentChildType("child2").addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size));
|
.innerHit(new InnerHitBuilder().setName("a").addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size))));
|
||||||
SearchResponse searchResponse;
|
boolQuery.should(constantScoreQuery(hasChildQuery("child2", matchAllQuery(), ScoreMode.None)
|
||||||
if (randomBoolean()) {
|
.innerHit(new InnerHitBuilder().setName("b").addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size))));
|
||||||
searchResponse = client().prepareSearch("idx")
|
SearchResponse searchResponse = client().prepareSearch("idx")
|
||||||
.setSize(numDocs)
|
.setSize(numDocs)
|
||||||
.setTypes("parent")
|
.setTypes("parent")
|
||||||
.addSort("_uid", SortOrder.ASC)
|
.addSort("_uid", SortOrder.ASC)
|
||||||
.innerHits(innerHitsBuilder)
|
.setQuery(boolQuery)
|
||||||
.get();
|
.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();
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNoFailures(searchResponse);
|
assertNoFailures(searchResponse);
|
||||||
assertHitCount(searchResponse, numDocs);
|
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"));
|
requests.add(client().prepareIndex("articles", "remark", "2").setParent("2").setRouting("2").setSource("message", "bad"));
|
||||||
indexRandom(true, requests);
|
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")
|
SearchResponse response = client().prepareSearch("articles")
|
||||||
.setQuery(hasChildQuery("comment", hasChildQuery("remark", matchQuery("message", "good"), ScoreMode.None), ScoreMode.None))
|
.setQuery(hasChildQuery("comment",
|
||||||
.innerHits(innerHitsBuilder)
|
hasChildQuery("remark", matchQuery("message", "good"), ScoreMode.None).innerHit(new InnerHitBuilder()),
|
||||||
|
ScoreMode.None).innerHit(new InnerHitBuilder()))
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
assertNoFailures(response);
|
assertNoFailures(response);
|
||||||
|
@ -590,18 +452,10 @@ public class InnerHitsIT extends ESIntegTestCase {
|
||||||
assertThat(innerHits.getAt(0).getId(), equalTo("1"));
|
assertThat(innerHits.getAt(0).getId(), equalTo("1"));
|
||||||
assertThat(innerHits.getAt(0).type(), equalTo("remark"));
|
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")
|
response = client().prepareSearch("articles")
|
||||||
.setQuery(hasChildQuery("comment", hasChildQuery("remark", matchQuery("message", "bad"), ScoreMode.None), ScoreMode.None))
|
.setQuery(hasChildQuery("comment",
|
||||||
.innerHits(innerHitsBuilder)
|
hasChildQuery("remark", matchQuery("message", "bad"), ScoreMode.None).innerHit(new InnerHitBuilder()),
|
||||||
|
ScoreMode.None).innerHit(new InnerHitBuilder()))
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
assertNoFailures(response);
|
assertNoFailures(response);
|
||||||
|
@ -662,24 +516,18 @@ public class InnerHitsIT extends ESIntegTestCase {
|
||||||
.endObject()));
|
.endObject()));
|
||||||
indexRandom(true, requests);
|
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")
|
SearchResponse response = client().prepareSearch("articles")
|
||||||
.setQuery(nestedQuery("comments", nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "good"), ScoreMode.Avg), ScoreMode.Avg))
|
.setQuery(
|
||||||
.innerHits(innerHitsBuilder).get();
|
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);
|
assertNoFailures(response);
|
||||||
assertHitCount(response, 1);
|
assertHitCount(response, 1);
|
||||||
assertSearchHit(response, 1, hasId("1"));
|
assertSearchHit(response, 1, hasId("1"));
|
||||||
assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(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.totalHits(), equalTo(1L));
|
||||||
assertThat(innerHits.getHits().length, equalTo(1));
|
assertThat(innerHits.getHits().length, equalTo(1));
|
||||||
assertThat(innerHits.getAt(0).getId(), 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().getField().string(), equalTo("remarks"));
|
||||||
assertThat(innerHits.getAt(0).getNestedIdentity().getChild().getOffset(), equalTo(0));
|
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")
|
response = client().prepareSearch("articles")
|
||||||
.setQuery(nestedQuery("comments", nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "bad"), ScoreMode.Avg), ScoreMode.Avg))
|
.setQuery(
|
||||||
.innerHits(innerHitsBuilder)
|
nestedQuery("comments",
|
||||||
.get();
|
nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "bad"), ScoreMode.Avg)
|
||||||
|
.innerHit(new InnerHitBuilder().setName("remark")),
|
||||||
|
ScoreMode.Avg).innerHit(new InnerHitBuilder())
|
||||||
|
).get();
|
||||||
assertNoFailures(response);
|
assertNoFailures(response);
|
||||||
assertHitCount(response, 1);
|
assertHitCount(response, 1);
|
||||||
assertSearchHit(response, 1, hasId("2"));
|
assertSearchHit(response, 1, hasId("2"));
|
||||||
assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1));
|
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.totalHits(), equalTo(1L));
|
||||||
assertThat(innerHits.getHits().length, equalTo(1));
|
assertThat(innerHits.getHits().length, equalTo(1));
|
||||||
assertThat(innerHits.getAt(0).getId(), equalTo("2"));
|
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("{}"));
|
requests.add(client().prepareIndex("royals", "baron", "baron4").setParent("earl4").setRouting("king").setSource("{}"));
|
||||||
indexRandom(true, requests);
|
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")
|
SearchResponse response = client().prepareSearch("royals")
|
||||||
.setTypes("duke")
|
.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();
|
.get();
|
||||||
assertHitCount(response, 1);
|
assertHitCount(response, 1);
|
||||||
assertThat(response.getHits().getAt(0).getId(), equalTo("duke"));
|
assertThat(response.getHits().getAt(0).getId(), equalTo("duke"));
|
||||||
|
@ -1086,25 +927,4 @@ public class InnerHitsIT extends ESIntegTestCase {
|
||||||
assertHitCount(response, 1);
|
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<IndexRequestBuilder> 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"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,5 +151,6 @@ specifying the sort order with the `order` option.
|
||||||
|
|
||||||
==== Inner hits
|
==== 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.
|
* Top level inner hits syntax has been removed. Inner hits can now only be specified as part of the `nested`,
|
||||||
So the `path` and `type` options are specified on the same level where `query` and other options are specified.
|
`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.
|
||||||
|
|
|
@ -227,77 +227,3 @@ 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_name>" : {
|
|
||||||
"<path|type>" : {
|
|
||||||
"<path-to-nested-object-field|child-or-parent-type>" : {
|
|
||||||
<inner_hits_body>
|
|
||||||
[,"inner_hits" : { [<sub_inner_hits>]+ } ]?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[,"<inner_hits_name_2>" : { ... } ]*
|
|
||||||
}
|
|
||||||
--------------------------------------------------
|
|
||||||
|
|
||||||
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 `<inner_hits_body>` any option for features
|
|
||||||
that `inner_hits` support can be defined. Optionally another `inner_hits` definition can be defined in the `<inner_hits_body>`.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
Loading…
Reference in New Issue