Query DSL: Add different execution models for terms filter, closes #1568.

This commit is contained in:
Shay Banon 2011-12-24 00:29:55 +02:00
parent 73b74847aa
commit 0ea58cacfd
3 changed files with 148 additions and 23 deletions

View File

@ -145,6 +145,28 @@ public class XBooleanFilter extends Filter {
}
}
public void addMust(Filter filter) {
if (mustFilters == null) {
mustFilters = new ArrayList<Filter>();
}
mustFilters.add(filter);
}
public void addShould(Filter filter) {
if (shouldFilters == null) {
shouldFilters = new ArrayList<Filter>();
}
shouldFilters.add(filter);
}
public void addNot(Filter filter) {
if (notFilters == null) {
notFilters = new ArrayList<Filter>();
}
notFilters.add(filter);
}
private boolean equalFilters(ArrayList<Filter> filters1, ArrayList<Filter> filters2) {
return (filters1 == filters2) ||
((filters1 != null) && filters1.equals(filters2));

View File

@ -25,8 +25,6 @@ import java.io.IOException;
/**
* A filer for a field based on several terms matching on any of them.
*
*
*/
public class TermsFilterBuilder extends BaseFilterBuilder {
@ -39,6 +37,8 @@ public class TermsFilterBuilder extends BaseFilterBuilder {
private String filterName;
private String execution;
/**
* A filer for a field based on several terms matching on any of them.
*
@ -116,6 +116,15 @@ public class TermsFilterBuilder extends BaseFilterBuilder {
this.values = values;
}
/**
* Sets the execution mode for the terms filter. Cane be either "plain", "bool"
* "and". Defaults to "plain".
*/
public TermsFilterBuilder execution(String execution) {
this.execution = execution;
return this;
}
/**
* Sets the filter name for the filter that can be used when searching for matched_filters per hit.
*/
@ -146,6 +155,10 @@ public class TermsFilterBuilder extends BaseFilterBuilder {
}
builder.endArray();
if (execution != null) {
builder.field("execution", execution);
}
if (filterName != null) {
builder.field("_name", filterName);
}

View File

@ -19,16 +19,21 @@
package org.elasticsearch.index.query;
import com.google.common.collect.Lists;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.PublicTermsFilter;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.lucene.search.AndFilter;
import org.elasticsearch.common.lucene.search.TermFilter;
import org.elasticsearch.common.lucene.search.XBooleanFilter;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.cache.filter.support.CacheKeyFilter;
import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.MapperService;
import java.io.IOException;
import java.util.List;
import static org.elasticsearch.index.query.support.QueryParsers.wrapSmartNameFilter;
@ -53,40 +58,31 @@ public class TermsFilterParser implements FilterParser {
XContentParser parser = parseContext.parser();
MapperService.SmartNameFieldMappers smartNameFieldMappers = null;
boolean cache = true;
PublicTermsFilter termsFilter = new PublicTermsFilter();
Boolean cache = null;
String filterName = null;
String currentFieldName = null;
CacheKeyFilter.Key cacheKey = null;
XContentParser.Token token;
String execution = "plain";
List<String> terms = Lists.newArrayList();
String fieldName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_ARRAY) {
String fieldName = currentFieldName;
FieldMapper fieldMapper = null;
smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
if (smartNameFieldMappers != null) {
if (smartNameFieldMappers.hasMapper()) {
fieldMapper = smartNameFieldMappers.mapper();
fieldName = fieldMapper.names().indexName();
}
}
fieldName = currentFieldName;
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
String value = parser.text();
if (value == null) {
throw new QueryParsingException(parseContext.index(), "No value specified for term filter");
}
if (fieldMapper != null) {
value = fieldMapper.indexedValue(value);
termsFilter.addTerm(fieldMapper.names().createIndexNameTerm(value));
} else {
termsFilter.addTerm(new Term(fieldName, value));
}
terms.add(value);
}
} else if (token.isValue()) {
if ("_name".equals(currentFieldName)) {
if ("execution".equals(currentFieldName)) {
execution = parser.text();
} else if ("_name".equals(currentFieldName)) {
filterName = parser.text();
} else if ("_cache".equals(currentFieldName)) {
cache = parser.booleanValue();
@ -96,9 +92,103 @@ public class TermsFilterParser implements FilterParser {
}
}
Filter filter = termsFilter;
if (cache) {
filter = parseContext.cacheFilter(filter, cacheKey);
if (fieldName == null) {
throw new QueryParsingException(parseContext.index(), "bool filter requires a field name, followed by array of terms");
}
FieldMapper fieldMapper = null;
smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
if (smartNameFieldMappers != null) {
if (smartNameFieldMappers.hasMapper()) {
fieldMapper = smartNameFieldMappers.mapper();
fieldName = fieldMapper.names().indexName();
}
}
Filter filter;
if ("plain".equals(execution)) {
PublicTermsFilter termsFilter = new PublicTermsFilter();
if (fieldMapper != null) {
for (String term : terms) {
termsFilter.addTerm(fieldMapper.names().createIndexNameTerm(fieldMapper.indexedValue(term)));
}
} else {
for (String term : terms) {
termsFilter.addTerm(new Term(fieldName, term));
}
}
filter = termsFilter;
// cache the whole filter by default, or if explicitly told to
if (cache == null || cache) {
filter = parseContext.cacheFilter(filter, cacheKey);
}
} else if ("bool".equals(execution)) {
XBooleanFilter boolFiler = new XBooleanFilter();
if (fieldMapper != null) {
for (String term : terms) {
boolFiler.addShould(parseContext.cacheFilter(fieldMapper.fieldFilter(term, parseContext), null));
}
} else {
for (String term : terms) {
boolFiler.addShould(parseContext.cacheFilter(new TermFilter(new Term(fieldName, term)), null));
}
}
filter = boolFiler;
// only cache if explicitly told to, since we cache inner filters
if (cache != null && cache) {
filter = parseContext.cacheFilter(filter, cacheKey);
}
} else if ("bool_nocache".equals(execution)) {
XBooleanFilter boolFiler = new XBooleanFilter();
if (fieldMapper != null) {
for (String term : terms) {
boolFiler.addShould(fieldMapper.fieldFilter(term, parseContext));
}
} else {
for (String term : terms) {
boolFiler.addShould(new TermFilter(new Term(fieldName, term)));
}
}
filter = boolFiler;
// cache the whole filter by default, or if explicitly told to
if (cache == null || cache) {
filter = parseContext.cacheFilter(filter, cacheKey);
}
} else if ("and".equals(execution)) {
List<Filter> filters = Lists.newArrayList();
if (fieldMapper != null) {
for (String term : terms) {
filters.add(parseContext.cacheFilter(fieldMapper.fieldFilter(term, parseContext), null));
}
} else {
for (String term : terms) {
filters.add(parseContext.cacheFilter(new TermFilter(new Term(fieldName, term)), null));
}
}
filter = new AndFilter(filters);
// only cache if explicitly told to, since we cache inner filters
if (cache != null && cache) {
filter = parseContext.cacheFilter(filter, cacheKey);
}
} else if ("and_nocache".equals(execution)) {
List<Filter> filters = Lists.newArrayList();
if (fieldMapper != null) {
for (String term : terms) {
filters.add(fieldMapper.fieldFilter(term, parseContext));
}
} else {
for (String term : terms) {
filters.add(new TermFilter(new Term(fieldName, term)));
}
}
filter = new AndFilter(filters);
// cache the whole filter by default, or if explicitly told to
if (cache == null || cache) {
filter = parseContext.cacheFilter(filter, cacheKey);
}
} else {
throw new QueryParsingException(parseContext.index(), "bool filter execution value [" + execution + "] not supported");
}
filter = wrapSmartNameFilter(filter, smartNameFieldMappers, parseContext);