Search: parse terms filters on a single term as a term filter.

Running a terms filter on a single term is equivalent to loading a postings
list into a bit set and then returning the bit set instead of reading the
postings list on the fly.

Close #9014
This commit is contained in:
Adrien Grand 2014-12-19 14:11:08 +01:00
parent 01bb02a0a4
commit 24591b3c70
2 changed files with 37 additions and 4 deletions

View File

@ -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<T> implements FieldMapper<T> {
@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);
}
/**

View File

@ -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));
}
}