Added filter support in the `has_child` and `has_parent` filters.
Example: ``` curl -XPOST 'localhost:9200/_search' -d '{ "query": { "filtered_query": { "query": { "match": { "title": "distributed systems" } }, "filter": { "has_child": { "type": "tag", "filter": { "term": { "name": "book" } } } } } } }' ``` Closes #2585
This commit is contained in:
parent
a39469a252
commit
9013eeae8a
|
@ -352,7 +352,7 @@ public abstract class FilterBuilders {
|
|||
/**
|
||||
* A filter to filter based on the relationship between a shape and indexed shapes
|
||||
*
|
||||
* @param name The shape field name
|
||||
* @param name The shape field name
|
||||
* @param shape Shape to use in the filter
|
||||
*/
|
||||
public static GeoShapeFilterBuilder geoShapeFilter(String name, Shape shape) {
|
||||
|
@ -392,10 +392,39 @@ public abstract class FilterBuilders {
|
|||
return new HasChildFilterBuilder(type, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a child filter, with the child type and the filter to run against child documents, with
|
||||
* the result of the filter being the *parent* documents.
|
||||
*
|
||||
* @param type The child type
|
||||
* @param filter The query to run against the child type
|
||||
*/
|
||||
public static HasChildFilterBuilder hasChildFilter(String type, FilterBuilder filter) {
|
||||
return new HasChildFilterBuilder(type, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a parent filter, with the parent type and the query to run against parent documents, with
|
||||
* the result of the filter being the *child* documents.
|
||||
*
|
||||
* @param parentType The parent type
|
||||
* @param query The query to run against the parent type
|
||||
*/
|
||||
public static HasParentFilterBuilder hasParentFilter(String parentType, QueryBuilder query) {
|
||||
return new HasParentFilterBuilder(parentType, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a parent filter, with the parent type and the filter to run against parent documents, with
|
||||
* the result of the filter being the *child* documents.
|
||||
*
|
||||
* @param parentType The parent type
|
||||
* @param filter The filter to run against the parent type
|
||||
*/
|
||||
public static HasParentFilterBuilder hasParentFilter(String parentType, FilterBuilder filter) {
|
||||
return new HasParentFilterBuilder(parentType, filter);
|
||||
}
|
||||
|
||||
public static BoolFilterBuilder boolFilter() {
|
||||
return new BoolFilterBuilder();
|
||||
}
|
||||
|
|
|
@ -28,19 +28,23 @@ import java.io.IOException;
|
|||
*/
|
||||
public class HasChildFilterBuilder extends BaseFilterBuilder {
|
||||
|
||||
private final FilterBuilder filterBuilder;
|
||||
private final QueryBuilder queryBuilder;
|
||||
|
||||
private String childType;
|
||||
|
||||
private String scope;
|
||||
|
||||
private String filterName;
|
||||
|
||||
private String executionType;
|
||||
|
||||
public HasChildFilterBuilder(String type, QueryBuilder queryBuilder) {
|
||||
this.childType = type;
|
||||
this.queryBuilder = queryBuilder;
|
||||
this.filterBuilder = null;
|
||||
}
|
||||
|
||||
public HasChildFilterBuilder(String type, FilterBuilder filterBuilder) {
|
||||
this.childType = type;
|
||||
this.queryBuilder = null;
|
||||
this.filterBuilder = filterBuilder;
|
||||
}
|
||||
|
||||
public HasChildFilterBuilder scope(String scope) {
|
||||
|
@ -58,7 +62,7 @@ public class HasChildFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
/**
|
||||
* Expert: Sets the low level child to parent filtering implementation. Can be: 'bitset' or 'uid'
|
||||
*
|
||||
* <p/>
|
||||
* This option is experimental and will be removed.
|
||||
*/
|
||||
public HasChildFilterBuilder executionType(String executionType) {
|
||||
|
@ -69,8 +73,13 @@ public class HasChildFilterBuilder extends BaseFilterBuilder {
|
|||
@Override
|
||||
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(HasChildFilterParser.NAME);
|
||||
builder.field("query");
|
||||
queryBuilder.toXContent(builder, params);
|
||||
if (queryBuilder != null) {
|
||||
builder.field("query");
|
||||
queryBuilder.toXContent(builder, params);
|
||||
} else if (filterBuilder != null) {
|
||||
builder.field("filter");
|
||||
filterBuilder.toXContent(builder, params);
|
||||
}
|
||||
builder.field("child_type", childType);
|
||||
if (scope != null) {
|
||||
builder.field("_scope", scope);
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.apache.lucene.search.Filter;
|
|||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.lucene.search.XConstantScoreQuery;
|
||||
import org.elasticsearch.common.lucene.search.XFilteredQuery;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.mapper.DocumentMapper;
|
||||
|
@ -74,6 +75,16 @@ public class HasChildFilterParser implements FilterParser {
|
|||
} finally {
|
||||
QueryParseContext.setTypes(origTypes);
|
||||
}
|
||||
} else if ("filter".equals(currentFieldName)) {
|
||||
// TODO handle `filter` element before `type` element...
|
||||
String[] origTypes = QueryParseContext.setTypesWithPrevious(childType == null ? null : new String[]{childType});
|
||||
try {
|
||||
Filter innerFilter = parseContext.parseInnerFilter();
|
||||
query = new XConstantScoreQuery(innerFilter);
|
||||
queryFound = true;
|
||||
} finally {
|
||||
QueryParseContext.setTypes(origTypes);
|
||||
}
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext.index(), "[has_child] filter does not support [" + currentFieldName + "]");
|
||||
}
|
||||
|
|
|
@ -29,18 +29,30 @@ import java.io.IOException;
|
|||
public class HasParentFilterBuilder extends BaseFilterBuilder {
|
||||
|
||||
private final QueryBuilder queryBuilder;
|
||||
private final FilterBuilder filterBuilder;
|
||||
private final String parentType;
|
||||
private String scope;
|
||||
private String filterName;
|
||||
private String executionType;
|
||||
|
||||
/**
|
||||
* @param parentType The parent type
|
||||
* @param parentType The parent type
|
||||
* @param parentQuery The query that will be matched with parent documents
|
||||
*/
|
||||
public HasParentFilterBuilder(String parentType, QueryBuilder parentQuery) {
|
||||
this.parentType = parentType;
|
||||
this.queryBuilder = parentQuery;
|
||||
this.filterBuilder = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parentType The parent type
|
||||
* @param parentFilter The filter that will be matched with parent documents
|
||||
*/
|
||||
public HasParentFilterBuilder(String parentType, FilterBuilder parentFilter) {
|
||||
this.parentType = parentType;
|
||||
this.queryBuilder = null;
|
||||
this.filterBuilder = parentFilter;
|
||||
}
|
||||
|
||||
public HasParentFilterBuilder scope(String scope) {
|
||||
|
@ -55,7 +67,7 @@ public class HasParentFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
/**
|
||||
* Expert: Sets the low level parent to child filtering implementation. Can be: 'bitset' or 'uid'
|
||||
*
|
||||
* <p/>
|
||||
* This option is experimental and will be removed.
|
||||
*/
|
||||
public HasParentFilterBuilder executionType(String executionType) {
|
||||
|
@ -66,8 +78,13 @@ public class HasParentFilterBuilder extends BaseFilterBuilder {
|
|||
@Override
|
||||
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(HasParentFilterParser.NAME);
|
||||
builder.field("query");
|
||||
queryBuilder.toXContent(builder, params);
|
||||
if (queryBuilder != null) {
|
||||
builder.field("query");
|
||||
queryBuilder.toXContent(builder, params);
|
||||
} else if (filterBuilder != null) {
|
||||
builder.field("filter");
|
||||
filterBuilder.toXContent(builder, params);
|
||||
}
|
||||
builder.field("parent_type", parentType);
|
||||
if (scope != null) {
|
||||
builder.field("_scope", scope);
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.apache.lucene.search.Filter;
|
|||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.lucene.search.XConstantScoreQuery;
|
||||
import org.elasticsearch.common.lucene.search.XFilteredQuery;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.mapper.DocumentMapper;
|
||||
|
@ -73,6 +74,16 @@ public class HasParentFilterParser implements FilterParser {
|
|||
} finally {
|
||||
QueryParseContext.setTypes(origTypes);
|
||||
}
|
||||
} else if ("filter".equals(currentFieldName)) {
|
||||
// TODO handle `filter` element before `type` element...
|
||||
String[] origTypes = QueryParseContext.setTypesWithPrevious(parentType == null ? null : new String[]{parentType});
|
||||
try {
|
||||
Filter innerFilter = parseContext.parseInnerFilter();
|
||||
query = new XConstantScoreQuery(innerFilter);
|
||||
queryFound = true;
|
||||
} finally {
|
||||
QueryParseContext.setTypes(origTypes);
|
||||
}
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext.index(), "[has_parent] filter does not support [" + currentFieldName + "]");
|
||||
}
|
||||
|
|
|
@ -40,8 +40,7 @@ import java.util.Map;
|
|||
|
||||
import static com.google.common.collect.Maps.newHashMap;
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.index.query.FilterBuilders.hasChildFilter;
|
||||
import static org.elasticsearch.index.query.FilterBuilders.hasParentFilter;
|
||||
import static org.elasticsearch.index.query.FilterBuilders.*;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
import static org.elasticsearch.search.facet.FacetBuilders.termsFacet;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
@ -824,7 +823,7 @@ public class SimpleChildQuerySearchTests extends AbstractNodesTests {
|
|||
assertThat(searchResponse.failedShards(), equalTo(0));
|
||||
assertThat(searchResponse.hits().totalHits(), equalTo(1l));
|
||||
|
||||
client.prepareSearch("test")
|
||||
searchResponse = client.prepareSearch("test")
|
||||
.setQuery(filteredQuery(matchAllQuery(), hasParentFilter("parent", matchAllQuery())))
|
||||
.execute().actionGet();
|
||||
assertThat("Failures " + Arrays.toString(searchResponse.shardFailures()), searchResponse.shardFailures().length, equalTo(0));
|
||||
|
@ -1138,4 +1137,49 @@ public class SimpleChildQuerySearchTests extends AbstractNodesTests {
|
|||
assertThat(response.hits().totalHits(), equalTo(0l));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasChildAndHasParentFilter_withFilter() throws Exception {
|
||||
client.admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client.admin().indices().prepareCreate("test").setSettings(
|
||||
ImmutableSettings.settingsBuilder()
|
||||
.put("index.number_of_shards", 1)
|
||||
.put("index.number_of_replicas", 0)
|
||||
).execute().actionGet();
|
||||
client.admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet();
|
||||
client.admin().indices().preparePutMapping("test").setType("child").setSource(
|
||||
jsonBuilder()
|
||||
.startObject()
|
||||
.startObject("type")
|
||||
.startObject("_parent")
|
||||
.field("type", "parent")
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject()
|
||||
).execute().actionGet();
|
||||
|
||||
client.prepareIndex("test", "parent", "1").setSource("p_field", 1).execute().actionGet();
|
||||
client.prepareIndex("test", "child", "2").setParent("1").setSource("c_field", 1).execute().actionGet();
|
||||
client.admin().indices().prepareFlush("test").execute().actionGet();
|
||||
|
||||
client.prepareIndex("test", "type1", "3").setSource("p_field", "p_value1").execute().actionGet();
|
||||
client.admin().indices().prepareFlush("test").execute().actionGet();
|
||||
|
||||
SearchResponse searchResponse = client.prepareSearch("test")
|
||||
.setQuery(filteredQuery(matchAllQuery(), hasChildFilter("child", termFilter("c_field", 1))))
|
||||
.execute().actionGet();
|
||||
assertThat("Failures " + Arrays.toString(searchResponse.shardFailures()), searchResponse.shardFailures().length, equalTo(0));
|
||||
assertThat(searchResponse.failedShards(), equalTo(0));
|
||||
assertThat(searchResponse.hits().totalHits(), equalTo(1l));
|
||||
assertThat(searchResponse.hits().hits()[0].id(), equalTo("1"));
|
||||
|
||||
searchResponse = client.prepareSearch("test")
|
||||
.setQuery(filteredQuery(matchAllQuery(), hasParentFilter("parent", termFilter("p_field", 1))))
|
||||
.execute().actionGet();
|
||||
assertThat("Failures " + Arrays.toString(searchResponse.shardFailures()), searchResponse.shardFailures().length, equalTo(0));
|
||||
assertThat(searchResponse.failedShards(), equalTo(0));
|
||||
assertThat(searchResponse.hits().totalHits(), equalTo(1l));
|
||||
assertThat(searchResponse.hits().hits()[0].id(), equalTo("2"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue