Split HasParentQueryParser into toQuery and formXContent

This commit splits HasParentQueryParser into toQuery and fromXContent.
This change also deprecates several keys in favor of simplified settings
and adds basic unittests for HasParentQueryParser.

Relates to #10217
This commit is contained in:
Simon Willnauer 2015-09-08 22:15:25 +02:00
parent 6395349bc0
commit d2e53e0e0c
8 changed files with 440 additions and 160 deletions

View File

@ -18,10 +18,28 @@
*/
package org.elasticsearch.index.query;
import org.apache.lucene.search.*;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.Version;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.fielddata.plain.ParentChildIndexFieldData;
import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.mapper.internal.ParentFieldMapper;
import org.elasticsearch.index.query.support.QueryInnerHits;
import org.elasticsearch.index.search.child.ParentConstantScoreQuery;
import org.elasticsearch.index.search.child.ParentQuery;
import org.elasticsearch.search.fetch.innerhits.InnerHitsContext;
import org.elasticsearch.search.fetch.innerhits.InnerHitsSubSearchContext;
import java.io.IOException;
import java.util.HashSet;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
/**
* Builder for the 'has_parent' query.
@ -29,26 +47,37 @@ import java.io.IOException;
public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBuilder> {
public static final String NAME = "has_parent";
private final QueryBuilder queryBuilder;
private final String parentType;
private String scoreType;
private QueryInnerHits innerHit = null;
static final HasParentQueryBuilder PROTOTYPE = new HasParentQueryBuilder(null, null);
private final QueryBuilder query;
private final String type;
private boolean score = false;
private QueryInnerHits innerHit;
/**
* @param parentType The parent type
* @param parentQuery The query that will be matched with parent documents
* @param type The parent type
* @param query The query that will be matched with parent documents
*/
public HasParentQueryBuilder(String parentType, QueryBuilder parentQuery) {
this.parentType = parentType;
this.queryBuilder = parentQuery;
public HasParentQueryBuilder(String type, QueryBuilder query) {
if (type == null) {
throw new IllegalArgumentException("[" + NAME + "] requires 'parent_type' field");
}
if (query == null) {
throw new IllegalArgumentException("[" + NAME + "] requires 'query' field");
}
this.type = type;
this.query = query;
}
public HasParentQueryBuilder(String type, QueryBuilder query, boolean score, QueryInnerHits innerHits) {
this(type, query);
this.score = score;
this.innerHit = innerHits;
}
/**
* Defines how the parent score is mapped into the child documents.
* Defines if the parent score is mapped into the child documents.
*/
public HasParentQueryBuilder scoreType(String scoreType) {
this.scoreType = scoreType;
public HasParentQueryBuilder score(boolean score) {
this.score = score;
return this;
}
@ -60,15 +89,116 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
return this;
}
/**
* Returns the query to execute.
*/
public QueryBuilder query() {
return query;
}
/**
* Returns <code>true</code> if the parent score is mapped into the child documents
*/
public boolean score() {
return score;
}
/**
* Returns inner hit definition in the scope of this query and reusing the defined type and query.
*/
public QueryInnerHits innerHit() {
return innerHit;
}
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
Query innerQuery = query.toQuery(context);
if (innerQuery == null) {
return null;
}
innerQuery.setBoost(boost);
DocumentMapper parentDocMapper = context.mapperService().documentMapper(type);
if (parentDocMapper == null) {
throw new QueryParsingException(context.parseContext(), "[has_parent] query configured 'parent_type' [" + type
+ "] is not a valid type");
}
if (innerHit != null) {
try (XContentParser parser = innerHit.getXcontentParser()) {
XContentParser.Token token = parser.nextToken();
if (token != XContentParser.Token.START_OBJECT) {
throw new IllegalStateException("start object expected but was: [" + token + "]");
}
InnerHitsSubSearchContext innerHits = context.indexQueryParserService().getInnerHitsQueryParserHelper().parse(parser);
if (innerHits != null) {
ParsedQuery parsedQuery = new ParsedQuery(innerQuery, context.copyNamedQueries());
InnerHitsContext.ParentChildInnerHits parentChildInnerHits = new InnerHitsContext.ParentChildInnerHits(innerHits.getSubSearchContext(), parsedQuery, null, context.mapperService(), parentDocMapper);
String name = innerHits.getName() != null ? innerHits.getName() : type;
context.addInnerHits(name, parentChildInnerHits);
}
}
}
Set<String> parentTypes = new HashSet<>(5);
parentTypes.add(parentDocMapper.type());
ParentChildIndexFieldData parentChildIndexFieldData = null;
for (DocumentMapper documentMapper : context.mapperService().docMappers(false)) {
ParentFieldMapper parentFieldMapper = documentMapper.parentFieldMapper();
if (parentFieldMapper.active()) {
DocumentMapper parentTypeDocumentMapper = context.mapperService().documentMapper(parentFieldMapper.type());
parentChildIndexFieldData = context.getForField(parentFieldMapper.fieldType());
if (parentTypeDocumentMapper == null) {
// Only add this, if this parentFieldMapper (also a parent) isn't a child of another parent.
parentTypes.add(parentFieldMapper.type());
}
}
}
if (parentChildIndexFieldData == null) {
throw new QueryParsingException(context.parseContext(), "[has_parent] no _parent field configured");
}
Query parentFilter = null;
if (parentTypes.size() == 1) {
DocumentMapper documentMapper = context.mapperService().documentMapper(parentTypes.iterator().next());
if (documentMapper != null) {
parentFilter = documentMapper.typeFilter();
}
} else {
BooleanQuery.Builder parentsFilter = new BooleanQuery.Builder();
for (String parentTypeStr : parentTypes) {
DocumentMapper documentMapper = context.mapperService().documentMapper(parentTypeStr);
if (documentMapper != null) {
parentsFilter.add(documentMapper.typeFilter(), BooleanClause.Occur.SHOULD);
}
}
parentFilter = parentsFilter.build();
}
if (parentFilter == null) {
return null;
}
// wrap the query with type query
innerQuery = Queries.filtered(innerQuery, parentDocMapper.typeFilter());
Filter childrenFilter = new QueryWrapperFilter(Queries.not(parentFilter));
if (context.indexVersionCreated().onOrAfter(Version.V_2_0_0_beta1)) {
return new HasChildQueryBuilder.LateParsingQuery(childrenFilter, innerQuery, HasChildQueryBuilder.DEFAULT_MIN_CHILDREN, HasChildQueryBuilder.DEFAULT_MAX_CHILDREN, type, score ? ScoreMode.Max : ScoreMode.None, parentChildIndexFieldData);
} else {
if (score) {
return new ParentQuery(parentChildIndexFieldData, innerQuery, parentDocMapper.type(), childrenFilter);
} else {
return new ParentConstantScoreQuery(parentChildIndexFieldData, innerQuery, parentDocMapper.type(), childrenFilter);
}
}
}
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
builder.field("query");
queryBuilder.toXContent(builder, params);
builder.field("parent_type", parentType);
if (scoreType != null) {
builder.field("score_type", scoreType);
}
query.toXContent(builder, params);
builder.field("parent_type", type);
builder.field("score", score);
printBoostAndQueryName(builder);
if (innerHit != null) {
innerHit.toXContent(builder, params);
@ -80,5 +210,44 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
public String getWriteableName() {
return NAME;
}
}
protected HasParentQueryBuilder(StreamInput in) throws IOException {
type = in.readString();
score = in.readBoolean();
query = in.readQuery();
if (in.readBoolean()) {
innerHit = new QueryInnerHits(in);
}
}
@Override
protected HasParentQueryBuilder doReadFrom(StreamInput in) throws IOException {
return new HasParentQueryBuilder(in);
}
@Override
protected void doWriteTo(StreamOutput out) throws IOException {
out.writeString(type);
out.writeBoolean(score);
out.writeQuery(query);
if (innerHit != null) {
out.writeBoolean(true);
innerHit.writeTo(out);
} else {
out.writeBoolean(false);
}
}
@Override
protected boolean doEquals(HasParentQueryBuilder that) {
return Objects.equals(query, that.query)
&& Objects.equals(type, that.type)
&& Objects.equals(score, that.score)
&& Objects.equals(innerHit, that.innerHit);
}
@Override
protected int doHashCode() {
return Objects.hash(query, type, score, innerHit);
}
}

View File

@ -30,6 +30,7 @@ import org.elasticsearch.index.fielddata.plain.ParentChildIndexFieldData;
import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.mapper.internal.ParentFieldMapper;
import org.elasticsearch.index.query.support.InnerHitsQueryParserHelper;
import org.elasticsearch.index.query.support.QueryInnerHits;
import org.elasticsearch.index.query.support.XContentStructure;
import org.elasticsearch.index.search.child.ParentConstantScoreQuery;
import org.elasticsearch.index.search.child.ParentQuery;
@ -42,16 +43,12 @@ import java.util.HashSet;
import java.util.Set;
public class HasParentQueryParser extends BaseQueryParserTemp {
public class HasParentQueryParser extends BaseQueryParser {
private static final HasParentQueryBuilder PROTOTYPE = new HasParentQueryBuilder("", EmptyQueryBuilder.PROTOTYPE);
private static final ParseField QUERY_FIELD = new ParseField("query", "filter");
private final InnerHitsQueryParserHelper innerHitsQueryParserHelper;
@Inject
public HasParentQueryParser(InnerHitsQueryParserHelper innerHitsQueryParserHelper) {
this.innerHitsQueryParserHelper = innerHitsQueryParserHelper;
}
private static final ParseField SCORE_FIELD = new ParseField("score_type", "score_mode").withAllDeprecated("score");
private static final ParseField TYPE_FIELD = new ParseField("parent_type", "type");
@Override
public String[] names() {
@ -59,20 +56,18 @@ public class HasParentQueryParser extends BaseQueryParserTemp {
}
@Override
public Query parse(QueryShardContext context) throws IOException, QueryParsingException {
QueryParseContext parseContext = context.parseContext();
public QueryBuilder fromXContent(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser();
boolean queryFound = false;
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
String parentType = null;
boolean score = false;
String queryName = null;
InnerHitsSubSearchContext innerHits = null;
QueryInnerHits innerHits = null;
String currentFieldName = null;
XContentParser.Token token;
XContentStructure.InnerQuery iq = null;
QueryBuilder iqb = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
@ -82,30 +77,25 @@ public class HasParentQueryParser extends BaseQueryParserTemp {
// XContentStructure.<type> facade to parse if available,
// or delay parsing if not.
if (parseContext.parseFieldMatcher().match(currentFieldName, QUERY_FIELD)) {
iq = new XContentStructure.InnerQuery(parseContext, parentType == null ? null : new String[] {parentType});
queryFound = true;
iqb = parseContext.parseInnerQueryBuilder();
} else if ("inner_hits".equals(currentFieldName)) {
innerHits = innerHitsQueryParserHelper.parse(parser);
innerHits = new QueryInnerHits(parser);
} else {
throw new QueryParsingException(parseContext, "[has_parent] query does not support [" + currentFieldName + "]");
}
} else if (token.isValue()) {
if ("type".equals(currentFieldName) || "parent_type".equals(currentFieldName) || "parentType".equals(currentFieldName)) {
if (parseContext.parseFieldMatcher().match(currentFieldName, TYPE_FIELD)) {
parentType = parser.text();
} else if ("score_type".equals(currentFieldName) || "scoreType".equals(currentFieldName)) {
} else if (parseContext.parseFieldMatcher().match(currentFieldName, SCORE_FIELD)) {
// deprecated we use a boolean now
String scoreTypeValue = parser.text();
if ("score".equals(scoreTypeValue)) {
score = true;
} else if ("none".equals(scoreTypeValue)) {
score = false;
}
} else if ("score_mode".equals(currentFieldName) || "scoreMode".equals(currentFieldName)) {
String scoreModeValue = parser.text();
if ("score".equals(scoreModeValue)) {
score = true;
} else if ("none".equals(scoreModeValue)) {
score = false;
}
} else if ("score".equals(currentFieldName)) {
score = parser.booleanValue();
} else if ("boost".equals(currentFieldName)) {
boost = parser.floatValue();
} else if ("_name".equals(currentFieldName)) {
@ -115,103 +105,11 @@ public class HasParentQueryParser extends BaseQueryParserTemp {
}
}
}
if (!queryFound) {
throw new QueryParsingException(parseContext, "[has_parent] query requires 'query' field");
}
if (parentType == null) {
throw new QueryParsingException(parseContext, "[has_parent] query requires 'parent_type' field");
}
Query innerQuery = iq.asQuery(parentType);
if (innerQuery == null) {
return null;
}
innerQuery.setBoost(boost);
Query query = createParentQuery(innerQuery, parentType, score, context, innerHits);
if (query == null) {
return null;
}
query.setBoost(boost);
if (queryName != null) {
context.addNamedQuery(queryName, query);
}
return query;
}
static Query createParentQuery(Query innerQuery, String parentType, boolean score, QueryShardContext context, InnerHitsSubSearchContext innerHits) throws IOException {
DocumentMapper parentDocMapper = context.mapperService().documentMapper(parentType);
if (parentDocMapper == null) {
throw new QueryParsingException(context.parseContext(), "[has_parent] query configured 'parent_type' [" + parentType
+ "] is not a valid type");
}
if (innerHits != null) {
ParsedQuery parsedQuery = new ParsedQuery(innerQuery, context.copyNamedQueries());
InnerHitsContext.ParentChildInnerHits parentChildInnerHits = new InnerHitsContext.ParentChildInnerHits(innerHits.getSubSearchContext(), parsedQuery, null, context.mapperService(), parentDocMapper);
String name = innerHits.getName() != null ? innerHits.getName() : parentType;
context.addInnerHits(name, parentChildInnerHits);
}
Set<String> parentTypes = new HashSet<>(5);
parentTypes.add(parentDocMapper.type());
ParentChildIndexFieldData parentChildIndexFieldData = null;
for (DocumentMapper documentMapper : context.mapperService().docMappers(false)) {
ParentFieldMapper parentFieldMapper = documentMapper.parentFieldMapper();
if (parentFieldMapper.active()) {
DocumentMapper parentTypeDocumentMapper = context.mapperService().documentMapper(parentFieldMapper.type());
parentChildIndexFieldData = context.getForField(parentFieldMapper.fieldType());
if (parentTypeDocumentMapper == null) {
// Only add this, if this parentFieldMapper (also a parent) isn't a child of another parent.
parentTypes.add(parentFieldMapper.type());
}
}
}
if (parentChildIndexFieldData == null) {
throw new QueryParsingException(context.parseContext(), "[has_parent] no _parent field configured");
}
Query parentFilter = null;
if (parentTypes.size() == 1) {
DocumentMapper documentMapper = context.mapperService().documentMapper(parentTypes.iterator().next());
if (documentMapper != null) {
parentFilter = documentMapper.typeFilter();
}
} else {
BooleanQuery.Builder parentsFilter = new BooleanQuery.Builder();
for (String parentTypeStr : parentTypes) {
DocumentMapper documentMapper = context.mapperService().documentMapper(parentTypeStr);
if (documentMapper != null) {
parentsFilter.add(documentMapper.typeFilter(), BooleanClause.Occur.SHOULD);
}
}
parentFilter = parentsFilter.build();
}
if (parentFilter == null) {
return null;
}
// wrap the query with type query
innerQuery = Queries.filtered(innerQuery, parentDocMapper.typeFilter());
Filter childrenFilter = new QueryWrapperFilter(Queries.not(parentFilter));
if (context.indexVersionCreated().onOrAfter(Version.V_2_0_0_beta1)) {
ScoreMode scoreMode = score ? ScoreMode.Max : ScoreMode.None;
return new HasChildQueryBuilder.LateParsingQuery(childrenFilter, innerQuery, HasChildQueryBuilder.DEFAULT_MIN_CHILDREN, HasChildQueryBuilder.DEFAULT_MAX_CHILDREN, parentType, scoreMode, parentChildIndexFieldData);
} else {
if (score) {
return new ParentQuery(parentChildIndexFieldData, innerQuery, parentDocMapper.type(), childrenFilter);
} else {
return new ParentConstantScoreQuery(parentChildIndexFieldData, innerQuery, parentDocMapper.type(), childrenFilter);
}
}
return new HasParentQueryBuilder(parentType, iqb, score, innerHits).queryName(queryName).boost(boost);
}
@Override
public HasParentQueryBuilder getBuilderPrototype() {
return HasParentQueryBuilder.PROTOTYPE;
return PROTOTYPE;
}
}

View File

@ -312,12 +312,12 @@ public class ChildSearchBenchmark {
System.out.println("--> Running has_parent query with score type");
// run parent child score query
for (int j = 0; j < QUERY_WARMUP; j++) {
client.prepareSearch(indexName).setQuery(hasParentQuery("parent", termQuery("field1", parentChildIndexGenerator.getQueryValue())).scoreType("score")).execute().actionGet();
client.prepareSearch(indexName).setQuery(hasParentQuery("parent", termQuery("field1", parentChildIndexGenerator.getQueryValue())).score(true)).execute().actionGet();
}
totalQueryTime = 0;
for (int j = 1; j < QUERY_COUNT; j++) {
SearchResponse searchResponse = client.prepareSearch(indexName).setQuery(hasParentQuery("parent", termQuery("field1", parentChildIndexGenerator.getQueryValue())).scoreType("score")).execute().actionGet();
SearchResponse searchResponse = client.prepareSearch(indexName).setQuery(hasParentQuery("parent", termQuery("field1", parentChildIndexGenerator.getQueryValue())).score(true)).execute().actionGet();
if (j % 10 == 0) {
System.out.println("--> hits [" + j + "], got [" + searchResponse.getHits().totalHits() + "]");
}
@ -327,7 +327,7 @@ public class ChildSearchBenchmark {
totalQueryTime = 0;
for (int j = 1; j < QUERY_COUNT; j++) {
SearchResponse searchResponse = client.prepareSearch(indexName).setQuery(hasParentQuery("parent", matchAllQuery()).scoreType("score")).execute().actionGet();
SearchResponse searchResponse = client.prepareSearch(indexName).setQuery(hasParentQuery("parent", matchAllQuery()).score(true)).execute().actionGet();
if (j % 10 == 0) {
System.out.println("--> hits [" + j + "], got [" + searchResponse.getHits().totalHits() + "]");
}

View File

@ -78,7 +78,7 @@ import java.util.Map;
import static org.hamcrest.Matchers.*;
public abstract class BaseQueryTestCase<QB extends AbstractQueryBuilder<QB>> extends ESTestCase {
public abstract class BaseQueryTestCase<QB extends AbstractQueryBuilder<QB>> extends ESTestCase { // TODO rename this AbstractQueryTestCase
protected static final String STRING_FIELD_NAME = "mapped_string";
protected static final String INT_FIELD_NAME = "mapped_int";
@ -268,6 +268,8 @@ public abstract class BaseQueryTestCase<QB extends AbstractQueryBuilder<QB>> ext
XContentParser parser = XContentFactory.xContent(queryAsString).createParser(queryAsString);
QueryParseContext context = createParseContext();
context.reset(parser);
// TODO this should set context.parseFieldMatcher(ParseFieldMatcher.STRICT);
// all our builders should only create non-deprecated XContent.
return context.parseInnerQueryBuilder();
}

View File

@ -0,0 +1,212 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.index.query;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.Version;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.xcontent.*;
import org.elasticsearch.index.fielddata.IndexFieldDataService;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.query.support.QueryInnerHits;
import org.elasticsearch.index.search.child.ChildrenQuery;
import org.elasticsearch.index.search.child.ParentConstantScoreQuery;
import org.elasticsearch.index.search.child.ParentQuery;
import org.elasticsearch.index.search.child.ScoreType;
import org.elasticsearch.search.fetch.innerhits.InnerHitsBuilder;
import org.elasticsearch.search.fetch.innerhits.InnerHitsContext;
import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.test.TestSearchContext;
import java.io.IOException;
import java.util.Arrays;
import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath;
import static org.hamcrest.CoreMatchers.instanceOf;
public class HasParentQueryBuilderTests extends BaseQueryTestCase<HasParentQueryBuilder> {
protected static final String PARENT_TYPE = "parent";
protected static final String CHILD_TYPE = "child";
public void setUp() throws Exception {
super.setUp();
MapperService mapperService = queryParserService().mapperService;
mapperService.merge(PARENT_TYPE, new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef(PARENT_TYPE,
STRING_FIELD_NAME, "type=string",
INT_FIELD_NAME, "type=integer",
DOUBLE_FIELD_NAME, "type=double",
BOOLEAN_FIELD_NAME, "type=boolean",
DATE_FIELD_NAME, "type=date",
OBJECT_FIELD_NAME, "type=object"
).string()), false, false);
mapperService.merge(CHILD_TYPE, new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef(CHILD_TYPE,
"_parent", "type=" + PARENT_TYPE,
STRING_FIELD_NAME, "type=string",
INT_FIELD_NAME, "type=integer",
DOUBLE_FIELD_NAME, "type=double",
BOOLEAN_FIELD_NAME, "type=boolean",
DATE_FIELD_NAME, "type=date",
OBJECT_FIELD_NAME, "type=object"
).string()), false, false);
}
protected void setSearchContext(String[] types) {
final MapperService mapperService = queryParserService().mapperService;
final IndexFieldDataService fieldData = queryParserService().fieldDataService;
TestSearchContext testSearchContext = new TestSearchContext() {
private InnerHitsContext context;
@Override
public void innerHits(InnerHitsContext innerHitsContext) {
context = innerHitsContext;
}
@Override
public InnerHitsContext innerHits() {
return context;
}
@Override
public MapperService mapperService() {
return mapperService; // need to build / parse inner hits sort fields
}
@Override
public IndexFieldDataService fieldData() {
return fieldData; // need to build / parse inner hits sort fields
}
};
testSearchContext.setTypes(types);
SearchContext.setCurrent(testSearchContext);
}
/**
* @return a {@link HasChildQueryBuilder} with random values all over the place
*/
@Override
protected HasParentQueryBuilder doCreateTestQueryBuilder() {
InnerHitsBuilder.InnerHit innerHit = new InnerHitsBuilder.InnerHit().setSize(100).addSort(STRING_FIELD_NAME, SortOrder.ASC);
return new HasParentQueryBuilder(PARENT_TYPE,
RandomQueryBuilder.createQuery(random()),randomBoolean(),
SearchContext.current() == null ? null : new QueryInnerHits("inner_hits_name", innerHit));
}
@Override
protected void doAssertLuceneQuery(HasParentQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
QueryBuilder innerQueryBuilder = queryBuilder.query();
if (innerQueryBuilder instanceof EmptyQueryBuilder) {
assertNull(query);
} else if (context.indexVersionCreated().onOrAfter(Version.V_2_0_0_beta1)) {
assertThat(query, instanceOf(HasChildQueryBuilder.LateParsingQuery.class));
HasChildQueryBuilder.LateParsingQuery lpq = (HasChildQueryBuilder.LateParsingQuery) query;
assertEquals(queryBuilder.score() ? ScoreMode.Max : ScoreMode.None, lpq.getScoreMode());
} else {
if (queryBuilder.score()) {
assertThat(query, instanceOf(ParentQuery.class));
ParentQuery pq = (ParentQuery) query;
assertEquals(queryBuilder.boost(), pq.getBoost(), 0f);
} else {
assertThat(query, instanceOf(ParentConstantScoreQuery.class));
ParentConstantScoreQuery csq = (ParentConstantScoreQuery) query;
assertEquals(queryBuilder.boost(), csq.getBoost(), 0f);
}
}
if (queryBuilder.innerHit() != null) {
assertNotNull(SearchContext.current());
if (query != null) {
assertNotNull(SearchContext.current().innerHits());
assertEquals(1, SearchContext.current().innerHits().getInnerHits().size());
assertTrue(SearchContext.current().innerHits().getInnerHits().containsKey("inner_hits_name"));
InnerHitsContext.BaseInnerHits innerHits = SearchContext.current().innerHits().getInnerHits().get("inner_hits_name");
assertEquals(innerHits.size(), 100);
assertEquals(innerHits.sort().getSort().length, 1);
assertEquals(innerHits.sort().getSort()[0].getField(), STRING_FIELD_NAME);
} else {
assertNull(SearchContext.current().innerHits());
}
}
}
public void testIllegalValues() {
QueryBuilder query = RandomQueryBuilder.createQuery(random());
try {
new HasParentQueryBuilder(null, query);
fail("must not be null");
} catch (IllegalArgumentException ex) {
}
try {
new HasParentQueryBuilder("foo", null);
fail("must not be null");
} catch (IllegalArgumentException ex) {
}
}
public void testDeprecatedXContent() throws IOException {
XContentBuilder builder = XContentFactory.jsonBuilder().prettyPrint();
builder.startObject();
builder.startObject("has_parent");
builder.field("query");
EmptyQueryBuilder.PROTOTYPE.toXContent(builder, ToXContent.EMPTY_PARAMS);
builder.field("type", "foo"); // deprecated
builder.endObject();
builder.endObject();
String queryAsString = builder.string();
QueryShardContext shardContext = createShardContext();
QueryParseContext context = shardContext.parseContext();
XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(queryAsString);
context.reset(parser);
context.parseFieldMatcher(ParseFieldMatcher.STRICT);
try {
context.parseInnerQueryBuilder();
fail("type is deprecated");
} catch (IllegalArgumentException ex) {
assertEquals("Deprecated field [type] used, expected [parent_type] instead", ex.getMessage());
}
String key = RandomPicks.randomFrom(random(), Arrays.asList("score_mode", "scoreMode", "score_type", "scoreType"));
builder = XContentFactory.jsonBuilder().prettyPrint();
builder.startObject();
builder.startObject("has_parent");
builder.field("query");
EmptyQueryBuilder.PROTOTYPE.toXContent(builder, ToXContent.EMPTY_PARAMS);
builder.field(key, "score");
builder.endObject();
builder.endObject();
queryAsString = builder.string();
parser = XContentFactory.xContent(XContentType.JSON).createParser(queryAsString);
context.reset(parser);
context.parseFieldMatcher(ParseFieldMatcher.STRICT);
try {
context.parseInnerQueryBuilder();
fail(key + " is deprecated");
} catch (IllegalArgumentException ex) {
assertEquals("Deprecated field [" + key + "] used, replaced by [score]", ex.getMessage());
}
}
}

View File

@ -106,7 +106,7 @@ public class ChildQuerySearchBwcIT extends ChildQuerySearchIT {
assertSearchHits(searchResponse, "c1");
searchResponse = client().prepareSearch("test")
.setQuery(hasParentQuery("parent", termQuery("p_field", "1")).scoreType("score"))
.setQuery(hasParentQuery("parent", termQuery("p_field", "1")).score(true))
.get();
assertHitCount(searchResponse, 1l);
assertSearchHits(searchResponse, "c1");
@ -133,7 +133,7 @@ public class ChildQuerySearchBwcIT extends ChildQuerySearchIT {
searchResponse = client().prepareSearch("test")
.setExplain(true)
.setQuery(hasParentQuery("parent", termQuery("p_field", "1")).scoreType("score"))
.setQuery(hasParentQuery("parent", termQuery("p_field", "1")).score(true))
.get();
assertHitCount(searchResponse, 1l);
assertThat(searchResponse.getHits().getAt(0).explanation().getDescription(), equalTo("not implemented yet..."));

View File

@ -290,7 +290,7 @@ public class ChildQuerySearchIT extends ESIntegTestCase {
.get();
assertNoFailures(searchResponse);
searchResponse = client().prepareSearch("test")
.setQuery(constantScoreQuery(hasParentQuery("parent", matchAllQuery()).scoreType("score")))
.setQuery(constantScoreQuery(hasParentQuery("parent", matchAllQuery()).score(true)))
.get();
assertNoFailures(searchResponse);
}
@ -552,7 +552,7 @@ public class ChildQuerySearchIT extends ESIntegTestCase {
.get();
assertHitCount(countResponse, 1l);
countResponse = client().prepareCount("test").setQuery(hasParentQuery("parent", termQuery("p_field", "1")).scoreType("score"))
countResponse = client().prepareCount("test").setQuery(hasParentQuery("parent", termQuery("p_field", "1")).score(true))
.get();
assertHitCount(countResponse, 1l);
@ -586,7 +586,7 @@ public class ChildQuerySearchIT extends ESIntegTestCase {
searchResponse = client().prepareSearch("test")
.setExplain(true)
.setQuery(hasParentQuery("parent", termQuery("p_field", "1")).scoreType("score"))
.setQuery(hasParentQuery("parent", termQuery("p_field", "1")).score(true))
.get();
assertHitCount(searchResponse, 1l);
assertThat(searchResponse.getHits().getAt(0).explanation().getDescription(), equalTo("Score based on join value p1"));
@ -721,7 +721,7 @@ public class ChildQuerySearchIT extends ESIntegTestCase {
"parent",
QueryBuilders.functionScoreQuery(matchQuery("p_field1", "p_value3"),
scriptFunction(new Script("doc['p_field2'].value")))
.boostMode(CombineFunction.REPLACE.getName())).scoreType("score"))
.boostMode(CombineFunction.REPLACE.getName())).score(true))
.addSort(SortBuilders.fieldSort("c_field3")).addSort(SortBuilders.scoreSort()).get();
assertThat(response.getHits().totalHits(), equalTo(7l));
@ -770,7 +770,7 @@ public class ChildQuerySearchIT extends ESIntegTestCase {
assertNoFailures(response);
assertThat(response.getHits().totalHits(), equalTo(0l));
response = client().prepareSearch("test").setQuery(QueryBuilders.hasParentQuery("child", matchQuery("text", "value")).scoreType("score"))
response = client().prepareSearch("test").setQuery(QueryBuilders.hasParentQuery("child", matchQuery("text", "value")).score(true))
.get();
assertNoFailures(response);
assertThat(response.getHits().totalHits(), equalTo(0l));
@ -868,7 +868,7 @@ public class ChildQuerySearchIT extends ESIntegTestCase {
assertThat(searchResponse.getHits().hits()[4].id(), equalTo("p004"));
searchResponse = client().prepareSearch("test").setSearchType(searchType)
.setQuery(hasParentQuery("parent", prefixQuery("p_field", "p")).scoreType("score")).addSort("c_field", SortOrder.ASC)
.setQuery(hasParentQuery("parent", prefixQuery("p_field", "p")).score(true)).addSort("c_field", SortOrder.ASC)
.setSize(5).get();
assertNoFailures(searchResponse);
assertThat(searchResponse.getHits().totalHits(), equalTo(500L));
@ -910,7 +910,7 @@ public class ChildQuerySearchIT extends ESIntegTestCase {
.prepareSearch("test")
.setQuery(
boolQuery().must(matchQuery("c_field", "x")).must(
hasParentQuery("parent", termQuery("p_field", "p_value2")).scoreType("score"))).get();
hasParentQuery("parent", termQuery("p_field", "p_value2")).score(true))).get();
assertNoFailures(searchResponse);
assertThat(searchResponse.getHits().totalHits(), equalTo(2l));
assertThat(searchResponse.getHits().getAt(0).id(), equalTo("c3"));
@ -936,7 +936,7 @@ public class ChildQuerySearchIT extends ESIntegTestCase {
.prepareSearch("test")
.setQuery(
boolQuery().must(matchQuery("c_field", "x")).must(
hasParentQuery("parent", termQuery("p_field", "p_value2")).scoreType("score"))).get();
hasParentQuery("parent", termQuery("p_field", "p_value2")).score(true))).get();
assertNoFailures(searchResponse);
assertThat(searchResponse.getHits().totalHits(), equalTo(2l));
assertThat(searchResponse.getHits().getAt(0).id(), Matchers.anyOf(equalTo("c3"), equalTo("c4")));
@ -1262,7 +1262,7 @@ public class ChildQuerySearchIT extends ESIntegTestCase {
assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1));
assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test"));
searchResponse = client().prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "1")).scoreType("score").queryName("test"))
searchResponse = client().prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "1")).score(true).queryName("test"))
.get();
assertHitCount(searchResponse, 1l);
assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1));
@ -1322,7 +1322,7 @@ public class ChildQuerySearchIT extends ESIntegTestCase {
try {
client().prepareSearch("test")
.setQuery(hasParentQuery("parent", termQuery("p_field", "1")).scoreType("score"))
.setQuery(hasParentQuery("parent", termQuery("p_field", "1")).score(true))
.get();
fail();
} catch (SearchPhaseExecutionException e) {

View File

@ -24,21 +24,20 @@ in the same manner as the `has_child` query.
[float]
==== Scoring capabilities
The `has_parent` also has scoring support. The
supported score types are `score` or `none`. The default is `none` and
this ignores the score from the parent document. The score is in this
The `has_parent` also has scoring support. The default is `false` which
ignores the score from the parent document. The score is in this
case equal to the boost on the `has_parent` query (Defaults to 1). If
the score type is set to `score`, then the score of the matching parent
the score is set to `true`, then the score of the matching parent
document is aggregated into the child documents belonging to the
matching parent document. The score type can be specified with the
`score_mode` field inside the `has_parent` query:
`score` field inside the `has_parent` query:
[source,js]
--------------------------------------------------
{
"has_parent" : {
"parent_type" : "blog",
"score_mode" : "score",
"score" : true,
"query" : {
"term" : {
"tag" : "something"