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) { private boolean equalFilters(ArrayList<Filter> filters1, ArrayList<Filter> filters2) {
return (filters1 == filters2) || return (filters1 == filters2) ||
((filters1 != null) && filters1.equals(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. * A filer for a field based on several terms matching on any of them.
*
*
*/ */
public class TermsFilterBuilder extends BaseFilterBuilder { public class TermsFilterBuilder extends BaseFilterBuilder {
@ -39,6 +37,8 @@ public class TermsFilterBuilder extends BaseFilterBuilder {
private String filterName; private String filterName;
private String execution;
/** /**
* A filer for a field based on several terms matching on any of them. * 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; 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. * 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(); builder.endArray();
if (execution != null) {
builder.field("execution", execution);
}
if (filterName != null) { if (filterName != null) {
builder.field("_name", filterName); builder.field("_name", filterName);
} }

View File

@ -19,16 +19,21 @@
package org.elasticsearch.index.query; package org.elasticsearch.index.query;
import com.google.common.collect.Lists;
import org.apache.lucene.index.Term; import org.apache.lucene.index.Term;
import org.apache.lucene.search.Filter; import org.apache.lucene.search.Filter;
import org.apache.lucene.search.PublicTermsFilter; import org.apache.lucene.search.PublicTermsFilter;
import org.elasticsearch.common.inject.Inject; 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.common.xcontent.XContentParser;
import org.elasticsearch.index.cache.filter.support.CacheKeyFilter; import org.elasticsearch.index.cache.filter.support.CacheKeyFilter;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperService;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import static org.elasticsearch.index.query.support.QueryParsers.wrapSmartNameFilter; import static org.elasticsearch.index.query.support.QueryParsers.wrapSmartNameFilter;
@ -53,40 +58,31 @@ public class TermsFilterParser implements FilterParser {
XContentParser parser = parseContext.parser(); XContentParser parser = parseContext.parser();
MapperService.SmartNameFieldMappers smartNameFieldMappers = null; MapperService.SmartNameFieldMappers smartNameFieldMappers = null;
boolean cache = true; Boolean cache = null;
PublicTermsFilter termsFilter = new PublicTermsFilter();
String filterName = null; String filterName = null;
String currentFieldName = null; String currentFieldName = null;
CacheKeyFilter.Key cacheKey = null; CacheKeyFilter.Key cacheKey = null;
XContentParser.Token token; XContentParser.Token token;
String execution = "plain";
List<String> terms = Lists.newArrayList();
String fieldName = 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) {
currentFieldName = parser.currentName(); currentFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_ARRAY) { } else if (token == XContentParser.Token.START_ARRAY) {
String fieldName = currentFieldName; fieldName = currentFieldName;
FieldMapper fieldMapper = null;
smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
if (smartNameFieldMappers != null) {
if (smartNameFieldMappers.hasMapper()) {
fieldMapper = smartNameFieldMappers.mapper();
fieldName = fieldMapper.names().indexName();
}
}
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
String value = parser.text(); String value = parser.text();
if (value == null) { if (value == null) {
throw new QueryParsingException(parseContext.index(), "No value specified for term filter"); throw new QueryParsingException(parseContext.index(), "No value specified for term filter");
} }
if (fieldMapper != null) { terms.add(value);
value = fieldMapper.indexedValue(value);
termsFilter.addTerm(fieldMapper.names().createIndexNameTerm(value));
} else {
termsFilter.addTerm(new Term(fieldName, value));
}
} }
} else if (token.isValue()) { } else if (token.isValue()) {
if ("_name".equals(currentFieldName)) { if ("execution".equals(currentFieldName)) {
execution = parser.text();
} else if ("_name".equals(currentFieldName)) {
filterName = parser.text(); filterName = parser.text();
} else if ("_cache".equals(currentFieldName)) { } else if ("_cache".equals(currentFieldName)) {
cache = parser.booleanValue(); cache = parser.booleanValue();
@ -96,9 +92,103 @@ public class TermsFilterParser implements FilterParser {
} }
} }
Filter filter = termsFilter; if (fieldName == null) {
if (cache) { throw new QueryParsingException(parseContext.index(), "bool filter requires a field name, followed by array of terms");
filter = parseContext.cacheFilter(filter, cacheKey); }
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); filter = wrapSmartNameFilter(filter, smartNameFieldMappers, parseContext);