Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

has_parent filter must take parent filter into account when executing the inner query/filter #8020

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -23,10 +23,6 @@
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.cache.fixedbitset.FixedBitSetFilter;
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.XContentStructure;
import org.elasticsearch.index.search.child.CustomQueryWrappingFilter;

Expand Down Expand Up @@ -63,7 +59,7 @@ public Filter parse(QueryParseContext parseContext) throws IOException, QueryPar
String filterName = null;
String currentFieldName = null;
XContentParser.Token token;
XContentStructure.InnerQuery innerQuery = null;
XContentStructure.InnerQuery iq = null;
XContentStructure.InnerFilter innerFilter = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
Expand All @@ -74,7 +70,7 @@ public Filter parse(QueryParseContext parseContext) throws IOException, QueryPar
// XContentStructure.<type> facade to parse if available,
// or delay parsing if not.
if ("query".equals(currentFieldName)) {
innerQuery = new XContentStructure.InnerQuery(parseContext, parentType == null ? null : new String[] {parentType});
iq = new XContentStructure.InnerQuery(parseContext, parentType == null ? null : new String[] {parentType});
queryFound = true;
} else if ("filter".equals(currentFieldName)) {
innerFilter = new XContentStructure.InnerFilter(parseContext, parentType == null ? null : new String[] {parentType});
Expand Down Expand Up @@ -103,18 +99,18 @@ public Filter parse(QueryParseContext parseContext) throws IOException, QueryPar
throw new QueryParsingException(parseContext.index(), "[has_parent] filter requires 'parent_type' field");
}

Query query;
Query innerQuery;
if (queryFound) {
query = innerQuery.asQuery(parentType);
innerQuery = iq.asQuery(parentType);
} else {
query = innerFilter.asFilter(parentType);
innerQuery = innerFilter.asFilter(parentType);
}

if (query == null) {
if (innerQuery == null) {
return null;
}

Query parentQuery = createParentQuery(query, parentType, false, parseContext);
Query parentQuery = createParentQuery(innerQuery, parentType, false, parseContext);
if (parentQuery == null) {
return null;
}
Expand Down
Expand Up @@ -122,14 +122,7 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
return null;
}

DocumentMapper parentDocMapper = parseContext.mapperService().documentMapper(parentType);
if (parentDocMapper == null) {
throw new QueryParsingException(parseContext.index(), "[has_parent] query configured 'parent_type' [" + parentType + "] is not a valid type");
}

innerQuery.setBoost(boost);
// wrap the query with type query
innerQuery = new XFilteredQuery(innerQuery, parseContext.cacheFilter(parentDocMapper.typeFilter(), null));
Query query = createParentQuery(innerQuery, parentType, score, parseContext);
if (query == null) {
return null;
Expand All @@ -143,8 +136,13 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
}

static Query createParentQuery(Query innerQuery, String parentType, boolean score, QueryParseContext parseContext) {
DocumentMapper parentDocMapper = parseContext.mapperService().documentMapper(parentType);
if (parentDocMapper == null) {
throw new QueryParsingException(parseContext.index(), "[has_parent] query configured 'parent_type' [" + parentType + "] is not a valid type");
}

Set<String> parentTypes = new HashSet<>(5);
parentTypes.add(parentType);
parentTypes.add(parentDocMapper.type());
ParentChildIndexFieldData parentChildIndexFieldData = null;
for (DocumentMapper documentMapper : parseContext.mapperService().docMappers(false)) {
ParentFieldMapper parentFieldMapper = documentMapper.parentFieldMapper();
Expand Down Expand Up @@ -182,11 +180,13 @@ static Query createParentQuery(Query innerQuery, String parentType, boolean scor
return null;
}

// wrap the query with type query
innerQuery = new XFilteredQuery(innerQuery, parseContext.cacheFilter(parentDocMapper.typeFilter(), null));
FixedBitSetFilter childrenFilter = parseContext.fixedBitSetFilter(new NotFilter(parentFilter));
if (score) {
return new ParentQuery(parentChildIndexFieldData, innerQuery, parentType, childrenFilter);
return new ParentQuery(parentChildIndexFieldData, innerQuery, parentDocMapper.type(), childrenFilter);
} else {
return new ParentConstantScoreQuery(parentChildIndexFieldData, innerQuery, parentType, childrenFilter);
return new ParentConstantScoreQuery(parentChildIndexFieldData, innerQuery, parentDocMapper.type(), childrenFilter);
}
}

Expand Down
Expand Up @@ -2160,6 +2160,40 @@ public void testParentFieldInMultiMatchField() throws Exception {
assertThat(response.getHits().getAt(0).id(), equalTo("1"));
}

@Test
public void testTypeIsAppliedInHasParentInnerQuery() throws Exception {
assertAcked(prepareCreate("test")
.addMapping("parent")
.addMapping("child", "_parent", "type=parent"));
ensureGreen();

List<IndexRequestBuilder> indexRequests = new ArrayList<>();
indexRequests.add(client().prepareIndex("test", "parent", "1").setSource("field1", "a"));
indexRequests.add(client().prepareIndex("test", "child", "1").setParent("1").setSource("{}"));
indexRequests.add(client().prepareIndex("test", "child", "2").setParent("1").setSource("{}"));
indexRandom(true, indexRequests);

SearchResponse searchResponse = client().prepareSearch("test")
.setQuery(constantScoreQuery(hasParentFilter("parent", notFilter(termFilter("field1", "a")))))
.get();
assertHitCount(searchResponse, 0l);

searchResponse = client().prepareSearch("test")
.setQuery(hasParentQuery("parent", constantScoreQuery(notFilter(termFilter("field1", "a")))))
.get();
assertHitCount(searchResponse, 0l);

searchResponse = client().prepareSearch("test")
.setQuery(constantScoreQuery(hasParentFilter("parent", termFilter("field1", "a"))))
.get();
assertHitCount(searchResponse, 2l);

searchResponse = client().prepareSearch("test")
.setQuery(hasParentQuery("parent", constantScoreQuery(termFilter("field1", "a"))))
.get();
assertHitCount(searchResponse, 2l);
}

List<IndexRequestBuilder> createMinMaxDocBuilders() {
List<IndexRequestBuilder> indexBuilders = new ArrayList<>();
// Parent 1 and its children
Expand Down