Parent/child: has_parent filter must take parent filter into account when executing the inner query/filter.

Closes #8020
Closes #7943
This commit is contained in:
Martijn van Groningen 2014-10-08 13:58:23 +02:00
parent 9ce7ca21e4
commit 6b26c2021a
3 changed files with 51 additions and 21 deletions

View File

@ -23,10 +23,6 @@ import org.apache.lucene.search.Query;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.xcontent.XContentParser; 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.query.support.XContentStructure;
import org.elasticsearch.index.search.child.CustomQueryWrappingFilter; import org.elasticsearch.index.search.child.CustomQueryWrappingFilter;
@ -63,7 +59,7 @@ public class HasParentFilterParser implements FilterParser {
String filterName = null; String filterName = null;
String currentFieldName = null; String currentFieldName = null;
XContentParser.Token token; XContentParser.Token token;
XContentStructure.InnerQuery innerQuery = null; XContentStructure.InnerQuery iq = null;
XContentStructure.InnerFilter innerFilter = null; XContentStructure.InnerFilter innerFilter = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) { if (token == XContentParser.Token.FIELD_NAME) {
@ -74,7 +70,7 @@ public class HasParentFilterParser implements FilterParser {
// XContentStructure.<type> facade to parse if available, // XContentStructure.<type> facade to parse if available,
// or delay parsing if not. // or delay parsing if not.
if ("query".equals(currentFieldName)) { 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; queryFound = true;
} else if ("filter".equals(currentFieldName)) { } else if ("filter".equals(currentFieldName)) {
innerFilter = new XContentStructure.InnerFilter(parseContext, parentType == null ? null : new String[] {parentType}); innerFilter = new XContentStructure.InnerFilter(parseContext, parentType == null ? null : new String[] {parentType});
@ -103,18 +99,18 @@ public class HasParentFilterParser implements FilterParser {
throw new QueryParsingException(parseContext.index(), "[has_parent] filter requires 'parent_type' field"); throw new QueryParsingException(parseContext.index(), "[has_parent] filter requires 'parent_type' field");
} }
Query query; Query innerQuery;
if (queryFound) { if (queryFound) {
query = innerQuery.asQuery(parentType); innerQuery = iq.asQuery(parentType);
} else { } else {
query = innerFilter.asFilter(parentType); innerQuery = innerFilter.asFilter(parentType);
} }
if (query == null) { if (innerQuery == null) {
return null; return null;
} }
Query parentQuery = createParentQuery(query, parentType, false, parseContext); Query parentQuery = createParentQuery(innerQuery, parentType, false, parseContext);
if (parentQuery == null) { if (parentQuery == null) {
return null; return null;
} }

View File

@ -122,14 +122,7 @@ public class HasParentQueryParser implements QueryParser {
return null; 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); 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); Query query = createParentQuery(innerQuery, parentType, score, parseContext);
if (query == null) { if (query == null) {
return null; return null;
@ -143,8 +136,13 @@ public class HasParentQueryParser implements QueryParser {
} }
static Query createParentQuery(Query innerQuery, String parentType, boolean score, QueryParseContext parseContext) { 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); Set<String> parentTypes = new HashSet<>(5);
parentTypes.add(parentType); parentTypes.add(parentDocMapper.type());
ParentChildIndexFieldData parentChildIndexFieldData = null; ParentChildIndexFieldData parentChildIndexFieldData = null;
for (DocumentMapper documentMapper : parseContext.mapperService().docMappers(false)) { for (DocumentMapper documentMapper : parseContext.mapperService().docMappers(false)) {
ParentFieldMapper parentFieldMapper = documentMapper.parentFieldMapper(); ParentFieldMapper parentFieldMapper = documentMapper.parentFieldMapper();
@ -182,11 +180,13 @@ public class HasParentQueryParser implements QueryParser {
return null; return null;
} }
// wrap the query with type query
innerQuery = new XFilteredQuery(innerQuery, parseContext.cacheFilter(parentDocMapper.typeFilter(), null));
FixedBitSetFilter childrenFilter = parseContext.fixedBitSetFilter(new NotFilter(parentFilter)); FixedBitSetFilter childrenFilter = parseContext.fixedBitSetFilter(new NotFilter(parentFilter));
if (score) { if (score) {
return new ParentQuery(parentChildIndexFieldData, innerQuery, parentType, childrenFilter); return new ParentQuery(parentChildIndexFieldData, innerQuery, parentDocMapper.type(), childrenFilter);
} else { } else {
return new ParentConstantScoreQuery(parentChildIndexFieldData, innerQuery, parentType, childrenFilter); return new ParentConstantScoreQuery(parentChildIndexFieldData, innerQuery, parentDocMapper.type(), childrenFilter);
} }
} }

View File

@ -2160,6 +2160,40 @@ public class SimpleChildQuerySearchTests extends ElasticsearchIntegrationTest {
assertThat(response.getHits().getAt(0).id(), equalTo("1")); 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> createMinMaxDocBuilders() {
List<IndexRequestBuilder> indexBuilders = new ArrayList<>(); List<IndexRequestBuilder> indexBuilders = new ArrayList<>();
// Parent 1 and its children // Parent 1 and its children