diff --git a/src/main/java/org/elasticsearch/index/mapper/core/AbstractFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/AbstractFieldMapper.java index 6e545a88479..d35d2239cb0 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/AbstractFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/AbstractFieldMapper.java @@ -39,6 +39,7 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.lucene.BytesRefs; import org.elasticsearch.common.lucene.Lucene; +import org.elasticsearch.common.lucene.search.MatchNoDocsFilter; import org.elasticsearch.common.lucene.search.RegexpFilter; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; @@ -496,11 +497,23 @@ public abstract class AbstractFieldMapper implements FieldMapper { @Override public Filter termsFilter(List values, @Nullable QueryParseContext context) { - BytesRef[] bytesRefs = new BytesRef[values.size()]; - for (int i = 0; i < bytesRefs.length; i++) { - bytesRefs[i] = indexedValueForSearch(values.get(i)); + switch (values.size()) { + case 0: + return new MatchNoDocsFilter(); + case 1: + // When there is a single term, it's important to return a term filter so that + // it can return a DocIdSet that is directly backed by a postings list, instead + // of loading everything into a bit set and returning an iterator based on the + // bit set + return termFilter(values.get(0), context); + default: + BytesRef[] bytesRefs = new BytesRef[values.size()]; + for (int i = 0; i < bytesRefs.length; i++) { + bytesRefs[i] = indexedValueForSearch(values.get(i)); + } + return new TermsFilter(names.indexName(), bytesRefs); + } - return new TermsFilter(names.indexName(), bytesRefs); } /** diff --git a/src/test/java/org/elasticsearch/index/mapper/string/SimpleStringMappingTests.java b/src/test/java/org/elasticsearch/index/mapper/string/SimpleStringMappingTests.java index e015a71012c..f52d0d2e322 100644 --- a/src/test/java/org/elasticsearch/index/mapper/string/SimpleStringMappingTests.java +++ b/src/test/java/org/elasticsearch/index/mapper/string/SimpleStringMappingTests.java @@ -20,10 +20,15 @@ package org.elasticsearch.index.mapper.string; import com.google.common.collect.ImmutableMap; + import org.apache.lucene.index.DocValuesType; import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.IndexableField; import org.apache.lucene.index.IndexableFieldType; +import org.apache.lucene.index.Term; +import org.apache.lucene.queries.TermFilter; +import org.apache.lucene.queries.TermsFilter; +import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.ToXContent; @@ -41,6 +46,7 @@ import org.junit.Before; import org.junit.Test; import java.util.Arrays; +import java.util.Collections; import static org.hamcrest.Matchers.*; @@ -374,4 +380,18 @@ public class SimpleStringMappingTests extends ElasticsearchSingleNodeTest { assertTrue(mergeResult.conflicts()[0].contains("cannot enable norms")); } + public void testTermsFilter() throws Exception { + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .startObject("properties").startObject("field").field("type", "string").field("index", "not_analyzed").endObject().endObject() + .endObject().endObject().string(); + + DocumentMapper defaultMapper = parser.parse(mapping); + FieldMapper mapper = defaultMapper.mappers().fullName("field").mapper(); + assertNotNull(mapper); + assertTrue(mapper instanceof StringFieldMapper); + assertEquals(Queries.MATCH_NO_FILTER, mapper.termsFilter(Collections.emptyList(), null)); + assertEquals(new TermFilter(new Term("field", "value")), mapper.termsFilter(Collections.singletonList("value"), null)); + assertEquals(new TermsFilter(new Term("field", "value1"), new Term("field", "value2")), mapper.termsFilter(Arrays.asList("value1", "value2"), null)); + } + }