Do not index `_type` when there is at most one type. (#24363)

This change makes `_type` behave pretty much like `_index` when
`index.mapping.single_type` is true.
This commit is contained in:
Adrien Grand 2017-05-04 16:29:35 +02:00 committed by GitHub
parent d928ae210d
commit 977016ba25
18 changed files with 256 additions and 144 deletions

View File

@ -21,7 +21,6 @@ package org.elasticsearch.common.lucene.search;
import org.apache.lucene.index.Term;
import org.apache.lucene.queries.ExtendedCommonTermsQuery;
import org.apache.lucene.search.AutomatonQuery;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
@ -31,9 +30,6 @@ import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.automaton.Automata;
import org.apache.lucene.util.automaton.Automaton;
import org.apache.lucene.util.automaton.Operations;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.index.mapper.TypeFieldMapper;
@ -42,29 +38,6 @@ import java.util.regex.Pattern;
public class Queries {
private static final Automaton NON_NESTED_TYPE_AUTOMATON;
static {
Automaton nestedTypeAutomaton = Operations.concatenate(
Automata.makeString("__"),
Automata.makeAnyString());
NON_NESTED_TYPE_AUTOMATON = Operations.complement(nestedTypeAutomaton, Operations.DEFAULT_MAX_DETERMINIZED_STATES);
}
// We use a custom class rather than AutomatonQuery directly in order to
// have a better toString
private static class NonNestedQuery extends AutomatonQuery {
NonNestedQuery() {
super(new Term(TypeFieldMapper.NAME), NON_NESTED_TYPE_AUTOMATON);
}
@Override
public String toString(String field) {
return "_type:[^_].*";
}
}
public static Query newMatchAllQuery() {
return new MatchAllDocsQuery();
}
@ -79,9 +52,11 @@ public class Queries {
}
public static Query newNonNestedFilter() {
// we use this automaton query rather than a negation of newNestedFilter
// since purely negative queries against high-cardinality clauses are costly
return new NonNestedQuery();
// TODO: this is slow, make it a positive query
return new BooleanQuery.Builder()
.add(new MatchAllDocsQuery(), Occur.FILTER)
.add(newNestedFilter(), Occur.MUST_NOT)
.build();
}
public static BooleanQuery filtered(@Nullable Query query, @Nullable Query filter) {

View File

@ -44,26 +44,33 @@ import org.elasticsearch.search.MultiValueMode;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Function;
public class IndexIndexFieldData extends AbstractIndexOrdinalsFieldData {
public class ConstantIndexFieldData extends AbstractIndexOrdinalsFieldData {
public static class Builder implements IndexFieldData.Builder {
private final Function<MapperService, String> valueFunction;
public Builder(Function<MapperService, String> valueFunction) {
this.valueFunction = valueFunction;
}
@Override
public IndexFieldData<?> build(IndexSettings indexSettings, MappedFieldType fieldType, IndexFieldDataCache cache,
CircuitBreakerService breakerService, MapperService mapperService) {
return new IndexIndexFieldData(indexSettings, fieldType.name());
return new ConstantIndexFieldData(indexSettings, fieldType.name(), valueFunction.apply(mapperService));
}
}
private static class IndexAtomicFieldData extends AbstractAtomicOrdinalsFieldData {
private static class ConstantAtomicFieldData extends AbstractAtomicOrdinalsFieldData {
private final String index;
private final String value;
IndexAtomicFieldData(String index) {
ConstantAtomicFieldData(String value) {
super(DEFAULT_SCRIPT_FUNCTION);
this.index = index;
this.value = value;
}
@Override
@ -78,7 +85,7 @@ public class IndexIndexFieldData extends AbstractIndexOrdinalsFieldData {
@Override
public SortedSetDocValues getOrdinalsValues() {
final BytesRef term = new BytesRef(index);
final BytesRef term = new BytesRef(value);
final SortedDocValues sortedValues = new AbstractSortedDocValues() {
private int docID = -1;
@ -120,12 +127,12 @@ public class IndexIndexFieldData extends AbstractIndexOrdinalsFieldData {
private final AtomicOrdinalsFieldData atomicFieldData;
private IndexIndexFieldData(IndexSettings indexSettings, String name) {
private ConstantIndexFieldData(IndexSettings indexSettings, String name, String value) {
super(indexSettings, name, null, null,
TextFieldMapper.Defaults.FIELDDATA_MIN_FREQUENCY,
TextFieldMapper.Defaults.FIELDDATA_MAX_FREQUENCY,
TextFieldMapper.Defaults.FIELDDATA_MIN_SEGMENT_SIZE);
atomicFieldData = new IndexAtomicFieldData(index().getName());
atomicFieldData = new ConstantAtomicFieldData(value);
}
@Override
@ -144,7 +151,8 @@ public class IndexIndexFieldData extends AbstractIndexOrdinalsFieldData {
}
@Override
public SortField sortField(@Nullable Object missingValue, MultiValueMode sortMode, XFieldComparatorSource.Nested nested, boolean reverse) {
public SortField sortField(@Nullable Object missingValue, MultiValueMode sortMode, XFieldComparatorSource.Nested nested,
boolean reverse) {
final XFieldComparatorSource source = new BytesRefFieldComparatorSource(this, missingValue, sortMode, nested);
return new SortField(getFieldName(), source, reverse);
}

View File

@ -24,17 +24,16 @@ import org.apache.lucene.search.Query;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Weight;
import org.elasticsearch.ElasticsearchGenerationException;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.analysis.IndexAnalyzers;
import org.elasticsearch.index.mapper.MetadataFieldMapper.TypeParser;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException;
@ -241,8 +240,8 @@ public class DocumentMapper implements ToXContent {
return metadataMapper(IndexFieldMapper.class);
}
public Query typeFilter() {
return typeMapper().fieldType().termQuery(type, null);
public Query typeFilter(QueryShardContext context) {
return typeMapper().fieldType().termQuery(type, context);
}
public boolean hasNestedObjects() {

View File

@ -30,7 +30,7 @@ import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.fielddata.plain.IndexIndexFieldData;
import org.elasticsearch.index.fielddata.plain.ConstantIndexFieldData;
import org.elasticsearch.index.query.QueryShardContext;
import java.io.IOException;
@ -157,7 +157,7 @@ public class IndexFieldMapper extends MetadataFieldMapper {
@Override
public IndexFieldData.Builder fielddataBuilder() {
return new IndexIndexFieldData.Builder();
return new ConstantIndexFieldData.Builder(mapperService -> mapperService.index().getName());
}
}

View File

@ -30,26 +30,29 @@ import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TermInSetQuery;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.action.fieldstats.FieldStats;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.fielddata.plain.DocValuesIndexFieldData;
import org.elasticsearch.index.fielddata.plain.PagedBytesIndexFieldData;
import org.elasticsearch.index.fielddata.plain.ConstantIndexFieldData;
import org.elasticsearch.index.query.QueryShardContext;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
public class TypeFieldMapper extends MetadataFieldMapper {
@ -88,29 +91,12 @@ public class TypeFieldMapper extends MetadataFieldMapper {
}
static final class TypeFieldType extends StringFieldType {
private boolean fielddata;
TypeFieldType() {
this.fielddata = false;
}
protected TypeFieldType(TypeFieldType ref) {
super(ref);
this.fielddata = ref.fielddata;
}
@Override
public boolean equals(Object o) {
if (super.equals(o) == false) {
return false;
}
TypeFieldType that = (TypeFieldType) o;
return fielddata == that.fielddata;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), fielddata);
}
@Override
@ -123,49 +109,76 @@ public class TypeFieldMapper extends MetadataFieldMapper {
return CONTENT_TYPE;
}
public boolean fielddata() {
return fielddata;
}
public void setFielddata(boolean fielddata) {
checkIfFrozen();
this.fielddata = fielddata;
}
@Override
public IndexFieldData.Builder fielddataBuilder() {
if (hasDocValues()) {
return new DocValuesIndexFieldData.Builder();
} else {
// means the index has a single type and the type field is implicit
Function<MapperService, String> typeFunction = mapperService -> {
Collection<String> types = mapperService.types();
if (types.size() > 1) {
throw new AssertionError();
}
// If we reach here, there is necessarily one type since we were able to find a `_type` field
String type = types.iterator().next();
return type;
};
return new ConstantIndexFieldData.Builder(typeFunction);
}
assert indexOptions() != IndexOptions.NONE;
if (fielddata) {
return new PagedBytesIndexFieldData.Builder(TextFieldMapper.Defaults.FIELDDATA_MIN_FREQUENCY,
TextFieldMapper.Defaults.FIELDDATA_MAX_FREQUENCY,
TextFieldMapper.Defaults.FIELDDATA_MIN_SEGMENT_SIZE);
}
return super.fielddataBuilder();
}
@Override
public Query termQuery(Object value, @Nullable QueryShardContext context) {
if (indexOptions() == IndexOptions.NONE) {
throw new AssertionError();
public FieldStats<?> stats(IndexReader reader) throws IOException {
if (reader.maxDoc() == 0) {
return null;
}
return new TypesQuery(indexedValueForSearch(value));
return new FieldStats.Text(reader.maxDoc(), reader.numDocs(), reader.maxDoc(), reader.maxDoc(),
isSearchable(), isAggregatable());
}
@Override
public void checkCompatibility(MappedFieldType other,
List<String> conflicts, boolean strict) {
super.checkCompatibility(other, conflicts, strict);
TypeFieldType otherType = (TypeFieldType) other;
if (strict) {
if (fielddata() != otherType.fielddata()) {
conflicts.add("mapper [" + name() + "] is used by multiple types. Set update_all_types to true to update [fielddata] "
+ "across all types.");
public boolean isSearchable() {
return true;
}
@Override
public Query termQuery(Object value, QueryShardContext context) {
return termsQuery(Arrays.asList(value), context);
}
@Override
public Query termsQuery(List<?> values, QueryShardContext context) {
if (context.getIndexSettings().getValue(MapperService.INDEX_MAPPING_SINGLE_TYPE_SETTING)) {
Collection<String> indexTypes = context.getMapperService().types();
if (indexTypes.isEmpty()) {
return new MatchNoDocsQuery("No types");
}
assert indexTypes.size() == 1;
BytesRef indexType = indexedValueForSearch(indexTypes.iterator().next());
if (values.stream()
.map(this::indexedValueForSearch)
.anyMatch(indexType::equals)) {
if (context.getMapperService().hasNested()) {
// type filters are expected not to match nested docs
return Queries.newNonNestedFilter();
} else {
return new MatchAllDocsQuery();
}
} else {
return new MatchNoDocsQuery("Type list does not contain the index type");
}
} else {
if (indexOptions() == IndexOptions.NONE) {
throw new AssertionError();
}
final BytesRef[] types = values.stream()
.map(this::indexedValueForSearch)
.toArray(size -> new BytesRef[size]);
return new TypesQuery(types);
}
}
}
/**
@ -261,7 +274,13 @@ public class TypeFieldMapper extends MetadataFieldMapper {
private static MappedFieldType defaultFieldType(Settings indexSettings) {
MappedFieldType defaultFieldType = Defaults.FIELD_TYPE.clone();
defaultFieldType.setHasDocValues(true);
if (MapperService.INDEX_MAPPING_SINGLE_TYPE_SETTING.get(indexSettings)) {
defaultFieldType.setIndexOptions(IndexOptions.NONE);
defaultFieldType.setHasDocValues(false);
} else {
defaultFieldType.setIndexOptions(IndexOptions.DOCS);
defaultFieldType.setHasDocValues(true);
}
return defaultFieldType;
}

View File

@ -338,10 +338,10 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
}
// wrap the query with type query
innerQuery = Queries.filtered(innerQuery, childDocMapper.typeFilter());
innerQuery = Queries.filtered(innerQuery, childDocMapper.typeFilter(context));
final ParentChildIndexFieldData parentChildIndexFieldData = context.getForField(parentFieldMapper.fieldType());
return new LateParsingQuery(parentDocMapper.typeFilter(), innerQuery, minChildren(), maxChildren(),
return new LateParsingQuery(parentDocMapper.typeFilter(context), innerQuery, minChildren(), maxChildren(),
parentType, scoreMode, parentChildIndexFieldData, context.getSearchSimilarity());
}

View File

@ -185,18 +185,18 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
Query childrenQuery;
if (childTypes.size() == 1) {
DocumentMapper documentMapper = context.getMapperService().documentMapper(childTypes.iterator().next());
childrenQuery = documentMapper.typeFilter();
childrenQuery = documentMapper.typeFilter(context);
} else {
BooleanQuery.Builder childrenFilter = new BooleanQuery.Builder();
for (String childrenTypeStr : childTypes) {
DocumentMapper documentMapper = context.getMapperService().documentMapper(childrenTypeStr);
childrenFilter.add(documentMapper.typeFilter(), BooleanClause.Occur.SHOULD);
childrenFilter.add(documentMapper.typeFilter(context), BooleanClause.Occur.SHOULD);
}
childrenQuery = childrenFilter.build();
}
// wrap the query with type query
innerQuery = Queries.filtered(innerQuery, parentDocMapper.typeFilter());
innerQuery = Queries.filtered(innerQuery, parentDocMapper.typeFilter(context));
return new HasChildQueryBuilder.LateParsingQuery(childrenQuery,
innerQuery,
HasChildQueryBuilder.DEFAULT_MIN_CHILDREN,

View File

@ -69,14 +69,14 @@ public class QueryRewriteContext {
* Returns the index settings for this context. This might return null if the
* context has not index scope.
*/
public final IndexSettings getIndexSettings() {
public IndexSettings getIndexSettings() {
return indexSettings;
}
/**
* Return the MapperService.
*/
public final MapperService getMapperService() {
public MapperService getMapperService() {
return mapperService;
}

View File

@ -132,7 +132,7 @@ public class TypeQueryBuilder extends AbstractQueryBuilder<TypeQueryBuilder> {
// no type means no documents
return new MatchNoDocsQuery();
} else {
return documentMapper.typeFilter();
return documentMapper.typeFilter(context);
}
}

View File

@ -76,6 +76,7 @@ import org.elasticsearch.search.suggest.SuggestionSearchContext;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@ -290,13 +291,13 @@ final class DefaultSearchContext extends SearchContext {
}
}
private static Query createTypeFilter(String[] types) {
private Query createTypeFilter(String[] types) {
if (types != null && types.length >= 1) {
BytesRef[] typesBytes = new BytesRef[types.length];
for (int i = 0; i < typesBytes.length; i++) {
typesBytes[i] = new BytesRef(types[i]);
MappedFieldType ft = mapperService().fullName(TypeFieldMapper.NAME);
if (ft != null) {
// ft might be null if no documents have been indexed yet
return ft.termsQuery(Arrays.asList(types), queryShardContext);
}
return new TypeFieldMapper.TypesQuery(typesBytes);
}
return null;
}

View File

@ -98,8 +98,8 @@ public class ChildrenAggregationBuilder extends ValuesSourceAggregationBuilder<P
parentType = parentFieldMapper.type();
DocumentMapper parentDocMapper = context.mapperService().documentMapper(parentType);
if (parentDocMapper != null) {
parentFilter = parentDocMapper.typeFilter();
childFilter = childDocMapper.typeFilter();
parentFilter = parentDocMapper.typeFilter(context.getQueryShardContext());
childFilter = childDocMapper.typeFilter(context.getQueryShardContext());
ParentChildIndexFieldData parentChildIndexFieldData = context.fieldData()
.getForField(parentFieldMapper.fieldType());
config.fieldContext(new FieldContext(parentFieldMapper.fieldType().name(), parentChildIndexFieldData,

View File

@ -180,7 +180,7 @@ public final class InnerHitsContext {
// Only include docs that have the current hit as parent
.add(hitQuery, Occur.FILTER)
// Only include docs that have this inner hits type
.add(documentMapper.typeFilter(), Occur.FILTER)
.add(documentMapper.typeFilter(context.getQueryShardContext()), Occur.FILTER)
.build();
if (size() == 0) {
final int count = context.searcher().count(q);

View File

@ -99,7 +99,7 @@ public class FieldNamesFieldMapperTests extends ESSingleNodeTestCase {
.bytes(),
XContentType.JSON));
assertFieldNames(set("a", "a.keyword", "b", "b.c", "_uid", "_type", "_version", "_seq_no", "_primary_term", "_source"), doc);
assertFieldNames(set("a", "a.keyword", "b", "b.c", "_uid", "_version", "_seq_no", "_primary_term", "_source"), doc);
}
public void testExplicitEnabled() throws Exception {
@ -117,7 +117,7 @@ public class FieldNamesFieldMapperTests extends ESSingleNodeTestCase {
.bytes(),
XContentType.JSON));
assertFieldNames(set("field", "field.keyword", "_uid", "_type", "_version", "_seq_no", "_primary_term", "_source"), doc);
assertFieldNames(set("field", "field.keyword", "_uid", "_version", "_seq_no", "_primary_term", "_source"), doc);
}
public void testDisabled() throws Exception {

View File

@ -290,13 +290,13 @@ public class MapperServiceTests extends ESSingleNodeTestCase {
public void testIndexSortWithNestedFields() throws IOException {
Settings settings = Settings.builder()
.put("index.sort.field", "_type")
.put("index.sort.field", "foo")
.build();
IllegalArgumentException invalidNestedException = expectThrows(IllegalArgumentException.class,
() -> createIndex("test", settings, "t", "nested_field", "type=nested"));
() -> createIndex("test", settings, "t", "nested_field", "type=nested", "foo", "type=keyword"));
assertThat(invalidNestedException.getMessage(),
containsString("cannot have nested fields when index sort is activated"));
IndexService indexService = createIndex("test", settings, "t");
IndexService indexService = createIndex("test", settings, "t", "foo", "type=keyword");
CompressedXContent nestedFieldMapping = new CompressedXContent(XContentFactory.jsonBuilder().startObject()
.startObject("properties")
.startObject("nested_field")

View File

@ -19,16 +19,31 @@
package org.elasticsearch.index.mapper;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.DocValuesType;
import org.apache.lucene.index.IndexOptions;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.index.SortedSetDocValues;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.fielddata.plain.DocValuesIndexFieldData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.fielddata.AtomicOrdinalsFieldData;
import org.elasticsearch.index.fielddata.IndexFieldDataCache;
import org.elasticsearch.index.fielddata.IndexOrdinalsFieldData;
import org.elasticsearch.index.mapper.MapperService.MergeReason;
import org.elasticsearch.indices.breaker.NoneCircuitBreakerService;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.ESSingleNodeTestCase;
import org.elasticsearch.test.InternalSettingsPlugin;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import static org.hamcrest.Matchers.instanceOf;
import java.util.Collections;
public class TypeFieldMapperTests extends ESSingleNodeTestCase {
@ -37,13 +52,60 @@ public class TypeFieldMapperTests extends ESSingleNodeTestCase {
return pluginList(InternalSettingsPlugin.class);
}
public void testDocValues() throws Exception {
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").endObject().endObject().string();
DocumentMapper docMapper = createIndex("test").mapperService().documentMapperParser().parse("type", new CompressedXContent(mapping));
TypeFieldMapper typeMapper = docMapper.metadataMapper(TypeFieldMapper.class);
assertTrue(typeMapper.fieldType().hasDocValues());
assertThat(typeMapper.fieldType().fielddataBuilder(), instanceOf(DocValuesIndexFieldData.Builder.class));
public void testDocValuesMultipleTypes() throws Exception {
testDocValues(false);
}
public void testDocValuesSingleType() throws Exception {
testDocValues(true);
}
public void testDocValues(boolean singleType) throws IOException {
Settings indexSettings = Settings.builder()
.put("index.mapping.single_type", singleType)
.build();
MapperService mapperService = createIndex("test", indexSettings).mapperService();
DocumentMapper mapper = mapperService.merge("type", new CompressedXContent("{\"type\":{}}"), MergeReason.MAPPING_UPDATE, false);
ParsedDocument document = mapper.parse(SourceToParse.source("index", "type", "id", new BytesArray("{}"), XContentType.JSON));
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, newIndexWriterConfig());
w.addDocument(document.rootDoc());
DirectoryReader r = DirectoryReader.open(w);
w.close();
MappedFieldType ft = mapperService.fullName(TypeFieldMapper.NAME);
IndexOrdinalsFieldData fd = (IndexOrdinalsFieldData) ft.fielddataBuilder().build(mapperService.getIndexSettings(),
ft, new IndexFieldDataCache.None(), new NoneCircuitBreakerService(), mapperService);
AtomicOrdinalsFieldData afd = fd.load(r.leaves().get(0));
SortedSetDocValues values = afd.getOrdinalsValues();
assertTrue(values.advanceExact(0));
assertEquals(0, values.nextOrd());
assertEquals(SortedSetDocValues.NO_MORE_ORDS, values.nextOrd());
assertEquals(new BytesRef("type"), values.lookupOrd(0));
r.close();
dir.close();
}
public void testDefaultsMultipleTypes() throws IOException {
Settings indexSettings = Settings.builder()
.put("index.mapping.single_type", false)
.build();
MapperService mapperService = createIndex("test", indexSettings).mapperService();
DocumentMapper mapper = mapperService.merge("type", new CompressedXContent("{\"type\":{}}"), MergeReason.MAPPING_UPDATE, false);
ParsedDocument document = mapper.parse(SourceToParse.source("index", "type", "id", new BytesArray("{}"), XContentType.JSON));
IndexableField[] fields = document.rootDoc().getFields(TypeFieldMapper.NAME);
assertEquals(IndexOptions.DOCS, fields[0].fieldType().indexOptions());
assertEquals(DocValuesType.SORTED_SET, fields[1].fieldType().docValuesType());
}
public void testDefaultsSingleType() throws IOException {
Settings indexSettings = Settings.builder()
.put("index.mapping.single_type", true)
.build();
MapperService mapperService = createIndex("test", indexSettings).mapperService();
DocumentMapper mapper = mapperService.merge("type", new CompressedXContent("{\"type\":{}}"), MergeReason.MAPPING_UPDATE, false);
ParsedDocument document = mapper.parse(SourceToParse.source("index", "type", "id", new BytesArray("{}"), XContentType.JSON));
assertEquals(Collections.<IndexableField>emptyList(), Arrays.asList(document.rootDoc().getFields(TypeFieldMapper.NAME)));
}
}

View File

@ -30,15 +30,25 @@ import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.IOUtils;
import org.junit.Before;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.UUIDs;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.query.QueryShardContext;
import org.mockito.Mockito;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
public class TypeFieldTypeTests extends FieldTypeTestCase {
@Override
@ -46,25 +56,62 @@ public class TypeFieldTypeTests extends FieldTypeTestCase {
return new TypeFieldMapper.TypeFieldType();
}
@Before
public void setupProperties() {
addModifier(new Modifier("fielddata", true) {
@Override
public void modify(MappedFieldType ft) {
TypeFieldMapper.TypeFieldType tft = (TypeFieldMapper.TypeFieldType) ft;
tft.setFielddata(tft.fielddata() == false);
}
});
public void testTermsQueryWhenTypesAreDisabled() throws Exception {
QueryShardContext context = Mockito.mock(QueryShardContext.class);
Settings indexSettings = Settings.builder()
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID())
.put("index.mapping.single_type", true).build();
IndexMetaData indexMetaData = IndexMetaData.builder(IndexMetaData.INDEX_UUID_NA_VALUE).settings(indexSettings).build();
IndexSettings mockSettings = new IndexSettings(indexMetaData, Settings.EMPTY);
Mockito.when(context.getIndexSettings()).thenReturn(mockSettings);
MapperService mapperService = Mockito.mock(MapperService.class);
Set<String> types = Collections.emptySet();
Mockito.when(mapperService.types()).thenReturn(types);
Mockito.when(context.getMapperService()).thenReturn(mapperService);
TypeFieldMapper.TypeFieldType ft = new TypeFieldMapper.TypeFieldType();
ft.setName(TypeFieldMapper.NAME);
Query query = ft.termQuery("my_type", context);
assertEquals(new MatchNoDocsQuery(), query);
types = Collections.singleton("my_type");
Mockito.when(mapperService.types()).thenReturn(types);
query = ft.termQuery("my_type", context);
assertEquals(new MatchAllDocsQuery(), query);
Mockito.when(mapperService.hasNested()).thenReturn(true);
query = ft.termQuery("my_type", context);
assertEquals(Queries.newNonNestedFilter(), query);
types = Collections.singleton("other_type");
Mockito.when(mapperService.types()).thenReturn(types);
query = ft.termQuery("my_type", context);
assertEquals(new MatchNoDocsQuery(), query);
}
public void testTermsQuery() throws Exception {
public void testTermsQueryWhenTypesAreEnabled() throws Exception {
Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, newIndexWriterConfig());
IndexReader reader = openReaderWithNewType("my_type", w);
QueryShardContext context = Mockito.mock(QueryShardContext.class);
Settings indexSettings = Settings.builder()
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID())
.put("index.mapping.single_type", false).build();
IndexMetaData indexMetaData = IndexMetaData.builder(IndexMetaData.INDEX_UUID_NA_VALUE).settings(indexSettings).build();
IndexSettings mockSettings = new IndexSettings(indexMetaData, Settings.EMPTY);
Mockito.when(context.getIndexSettings()).thenReturn(mockSettings);
TypeFieldMapper.TypeFieldType ft = new TypeFieldMapper.TypeFieldType();
ft.setName(TypeFieldMapper.NAME);
Query query = ft.termQuery("my_type", null);
Query query = ft.termQuery("my_type", context);
assertEquals(new MatchAllDocsQuery(), query.rewrite(reader));
// Make sure that Lucene actually simplifies the query when there is a single type

View File

@ -52,6 +52,7 @@ import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.search.aggregations.AggregatorTestCase;
import org.elasticsearch.search.aggregations.metrics.min.InternalMin;
import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder;
import org.mockito.Mockito;
import java.io.IOException;
import java.util.Arrays;
@ -165,8 +166,8 @@ public class ParentToChildrenAggregatorTests extends AggregatorTestCase {
when(mapperService.documentMapper(CHILD_TYPE)).thenReturn(childDocMapper);
when(mapperService.documentMapper(PARENT_TYPE)).thenReturn(parentDocMapper);
when(mapperService.docMappers(false)).thenReturn(Arrays.asList(new DocumentMapper[] { childDocMapper, parentDocMapper }));
when(parentDocMapper.typeFilter()).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(PARENT_TYPE)));
when(childDocMapper.typeFilter()).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(CHILD_TYPE)));
when(parentDocMapper.typeFilter(Mockito.any())).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(PARENT_TYPE)));
when(childDocMapper.typeFilter(Mockito.any())).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(CHILD_TYPE)));
return mapperService;
}

View File

@ -227,13 +227,13 @@ Response:
"index": "twitter",
"shard": 0,
"valid": true,
"explanation": "+MatchNoDocsQuery(\"empty BooleanQuery\") #ConstantScore(MatchNoDocsQuery(\"empty BooleanQuery\"))"
"explanation": "user:kimchy~2"
},
{
"index": "twitter",
"shard": 1,
"valid": true,
"explanation": "+MatchNoDocsQuery(\"empty BooleanQuery\") #ConstantScore(MatchNoDocsQuery(\"empty BooleanQuery\"))"
"explanation": "user:kimchy~2"
},
{
"index": "twitter",
@ -251,7 +251,7 @@ Response:
"index": "twitter",
"shard": 4,
"valid": true,
"explanation": "+MatchNoDocsQuery(\"empty BooleanQuery\") #ConstantScore(MatchNoDocsQuery(\"empty BooleanQuery\"))"
"explanation": "user:kimchy~2"
}
]
}