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.index.Term;
import org.apache.lucene.queries.ExtendedCommonTermsQuery; import org.apache.lucene.queries.ExtendedCommonTermsQuery;
import org.apache.lucene.search.AutomatonQuery;
import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery; 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.PrefixQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef; 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.common.Nullable;
import org.elasticsearch.index.mapper.TypeFieldMapper; import org.elasticsearch.index.mapper.TypeFieldMapper;
@ -42,29 +38,6 @@ import java.util.regex.Pattern;
public class Queries { 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() { public static Query newMatchAllQuery() {
return new MatchAllDocsQuery(); return new MatchAllDocsQuery();
} }
@ -79,9 +52,11 @@ public class Queries {
} }
public static Query newNonNestedFilter() { public static Query newNonNestedFilter() {
// we use this automaton query rather than a negation of newNestedFilter // TODO: this is slow, make it a positive query
// since purely negative queries against high-cardinality clauses are costly return new BooleanQuery.Builder()
return new NonNestedQuery(); .add(new MatchAllDocsQuery(), Occur.FILTER)
.add(newNestedFilter(), Occur.MUST_NOT)
.build();
} }
public static BooleanQuery filtered(@Nullable Query query, @Nullable Query filter) { 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.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; 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 { public static class Builder implements IndexFieldData.Builder {
private final Function<MapperService, String> valueFunction;
public Builder(Function<MapperService, String> valueFunction) {
this.valueFunction = valueFunction;
}
@Override @Override
public IndexFieldData<?> build(IndexSettings indexSettings, MappedFieldType fieldType, IndexFieldDataCache cache, public IndexFieldData<?> build(IndexSettings indexSettings, MappedFieldType fieldType, IndexFieldDataCache cache,
CircuitBreakerService breakerService, MapperService mapperService) { 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); super(DEFAULT_SCRIPT_FUNCTION);
this.index = index; this.value = value;
} }
@Override @Override
@ -78,7 +85,7 @@ public class IndexIndexFieldData extends AbstractIndexOrdinalsFieldData {
@Override @Override
public SortedSetDocValues getOrdinalsValues() { public SortedSetDocValues getOrdinalsValues() {
final BytesRef term = new BytesRef(index); final BytesRef term = new BytesRef(value);
final SortedDocValues sortedValues = new AbstractSortedDocValues() { final SortedDocValues sortedValues = new AbstractSortedDocValues() {
private int docID = -1; private int docID = -1;
@ -120,12 +127,12 @@ public class IndexIndexFieldData extends AbstractIndexOrdinalsFieldData {
private final AtomicOrdinalsFieldData atomicFieldData; private final AtomicOrdinalsFieldData atomicFieldData;
private IndexIndexFieldData(IndexSettings indexSettings, String name) { private ConstantIndexFieldData(IndexSettings indexSettings, String name, String value) {
super(indexSettings, name, null, null, super(indexSettings, name, null, null,
TextFieldMapper.Defaults.FIELDDATA_MIN_FREQUENCY, TextFieldMapper.Defaults.FIELDDATA_MIN_FREQUENCY,
TextFieldMapper.Defaults.FIELDDATA_MAX_FREQUENCY, TextFieldMapper.Defaults.FIELDDATA_MAX_FREQUENCY,
TextFieldMapper.Defaults.FIELDDATA_MIN_SEGMENT_SIZE); TextFieldMapper.Defaults.FIELDDATA_MIN_SEGMENT_SIZE);
atomicFieldData = new IndexAtomicFieldData(index().getName()); atomicFieldData = new ConstantAtomicFieldData(value);
} }
@Override @Override
@ -144,7 +151,8 @@ public class IndexIndexFieldData extends AbstractIndexOrdinalsFieldData {
} }
@Override @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); final XFieldComparatorSource source = new BytesRefFieldComparatorSource(this, missingValue, sortMode, nested);
return new SortField(getFieldName(), source, reverse); 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.Scorer;
import org.apache.lucene.search.Weight; import org.apache.lucene.search.Weight;
import org.elasticsearch.ElasticsearchGenerationException; import org.elasticsearch.ElasticsearchGenerationException;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text; import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.analysis.IndexAnalyzers; import org.elasticsearch.index.analysis.IndexAnalyzers;
import org.elasticsearch.index.mapper.MetadataFieldMapper.TypeParser; import org.elasticsearch.index.mapper.MetadataFieldMapper.TypeParser;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException; import java.io.IOException;
@ -241,8 +240,8 @@ public class DocumentMapper implements ToXContent {
return metadataMapper(IndexFieldMapper.class); return metadataMapper(IndexFieldMapper.class);
} }
public Query typeFilter() { public Query typeFilter(QueryShardContext context) {
return typeMapper().fieldType().termQuery(type, null); return typeMapper().fieldType().termQuery(type, context);
} }
public boolean hasNestedObjects() { 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.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.fielddata.IndexFieldData; 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 org.elasticsearch.index.query.QueryShardContext;
import java.io.IOException; import java.io.IOException;
@ -157,7 +157,7 @@ public class IndexFieldMapper extends MetadataFieldMapper {
@Override @Override
public IndexFieldData.Builder fielddataBuilder() { 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.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TermInSetQuery; import org.apache.lucene.search.TermInSetQuery;
import org.apache.lucene.util.BytesRef; 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.Lucene;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.fielddata.plain.DocValuesIndexFieldData; 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 org.elasticsearch.index.query.QueryShardContext;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
public class TypeFieldMapper extends MetadataFieldMapper { public class TypeFieldMapper extends MetadataFieldMapper {
@ -88,29 +91,12 @@ public class TypeFieldMapper extends MetadataFieldMapper {
} }
static final class TypeFieldType extends StringFieldType { static final class TypeFieldType extends StringFieldType {
private boolean fielddata;
TypeFieldType() { TypeFieldType() {
this.fielddata = false;
} }
protected TypeFieldType(TypeFieldType ref) { protected TypeFieldType(TypeFieldType ref) {
super(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 @Override
@ -123,49 +109,76 @@ public class TypeFieldMapper extends MetadataFieldMapper {
return CONTENT_TYPE; return CONTENT_TYPE;
} }
public boolean fielddata() {
return fielddata;
}
public void setFielddata(boolean fielddata) {
checkIfFrozen();
this.fielddata = fielddata;
}
@Override @Override
public IndexFieldData.Builder fielddataBuilder() { public IndexFieldData.Builder fielddataBuilder() {
if (hasDocValues()) { if (hasDocValues()) {
return new DocValuesIndexFieldData.Builder(); 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();
} }
assert indexOptions() != IndexOptions.NONE; // If we reach here, there is necessarily one type since we were able to find a `_type` field
if (fielddata) { String type = types.iterator().next();
return new PagedBytesIndexFieldData.Builder(TextFieldMapper.Defaults.FIELDDATA_MIN_FREQUENCY, return type;
TextFieldMapper.Defaults.FIELDDATA_MAX_FREQUENCY, };
TextFieldMapper.Defaults.FIELDDATA_MIN_SEGMENT_SIZE); return new ConstantIndexFieldData.Builder(typeFunction);
} }
return super.fielddataBuilder();
} }
@Override @Override
public Query termQuery(Object value, @Nullable QueryShardContext context) { public FieldStats<?> stats(IndexReader reader) throws IOException {
if (reader.maxDoc() == 0) {
return null;
}
return new FieldStats.Text(reader.maxDoc(), reader.numDocs(), reader.maxDoc(), reader.maxDoc(),
isSearchable(), isAggregatable());
}
@Override
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) { if (indexOptions() == IndexOptions.NONE) {
throw new AssertionError(); throw new AssertionError();
} }
return new TypesQuery(indexedValueForSearch(value)); final BytesRef[] types = values.stream()
.map(this::indexedValueForSearch)
.toArray(size -> new BytesRef[size]);
return new TypesQuery(types);
}
} }
@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.");
}
}
}
} }
/** /**
@ -261,7 +274,13 @@ public class TypeFieldMapper extends MetadataFieldMapper {
private static MappedFieldType defaultFieldType(Settings indexSettings) { private static MappedFieldType defaultFieldType(Settings indexSettings) {
MappedFieldType defaultFieldType = Defaults.FIELD_TYPE.clone(); MappedFieldType defaultFieldType = Defaults.FIELD_TYPE.clone();
if (MapperService.INDEX_MAPPING_SINGLE_TYPE_SETTING.get(indexSettings)) {
defaultFieldType.setIndexOptions(IndexOptions.NONE);
defaultFieldType.setHasDocValues(false);
} else {
defaultFieldType.setIndexOptions(IndexOptions.DOCS);
defaultFieldType.setHasDocValues(true); defaultFieldType.setHasDocValues(true);
}
return defaultFieldType; return defaultFieldType;
} }

View File

@ -338,10 +338,10 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
} }
// wrap the query with type query // 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()); 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()); parentType, scoreMode, parentChildIndexFieldData, context.getSearchSimilarity());
} }

View File

@ -185,18 +185,18 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
Query childrenQuery; Query childrenQuery;
if (childTypes.size() == 1) { if (childTypes.size() == 1) {
DocumentMapper documentMapper = context.getMapperService().documentMapper(childTypes.iterator().next()); DocumentMapper documentMapper = context.getMapperService().documentMapper(childTypes.iterator().next());
childrenQuery = documentMapper.typeFilter(); childrenQuery = documentMapper.typeFilter(context);
} else { } else {
BooleanQuery.Builder childrenFilter = new BooleanQuery.Builder(); BooleanQuery.Builder childrenFilter = new BooleanQuery.Builder();
for (String childrenTypeStr : childTypes) { for (String childrenTypeStr : childTypes) {
DocumentMapper documentMapper = context.getMapperService().documentMapper(childrenTypeStr); DocumentMapper documentMapper = context.getMapperService().documentMapper(childrenTypeStr);
childrenFilter.add(documentMapper.typeFilter(), BooleanClause.Occur.SHOULD); childrenFilter.add(documentMapper.typeFilter(context), BooleanClause.Occur.SHOULD);
} }
childrenQuery = childrenFilter.build(); childrenQuery = childrenFilter.build();
} }
// wrap the query with type query // wrap the query with type query
innerQuery = Queries.filtered(innerQuery, parentDocMapper.typeFilter()); innerQuery = Queries.filtered(innerQuery, parentDocMapper.typeFilter(context));
return new HasChildQueryBuilder.LateParsingQuery(childrenQuery, return new HasChildQueryBuilder.LateParsingQuery(childrenQuery,
innerQuery, innerQuery,
HasChildQueryBuilder.DEFAULT_MIN_CHILDREN, 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 * Returns the index settings for this context. This might return null if the
* context has not index scope. * context has not index scope.
*/ */
public final IndexSettings getIndexSettings() { public IndexSettings getIndexSettings() {
return indexSettings; return indexSettings;
} }
/** /**
* Return the MapperService. * Return the MapperService.
*/ */
public final MapperService getMapperService() { public MapperService getMapperService() {
return mapperService; return mapperService;
} }

View File

@ -132,7 +132,7 @@ public class TypeQueryBuilder extends AbstractQueryBuilder<TypeQueryBuilder> {
// no type means no documents // no type means no documents
return new MatchNoDocsQuery(); return new MatchNoDocsQuery();
} else { } 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.IOException;
import java.io.UncheckedIOException; import java.io.UncheckedIOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; 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) { if (types != null && types.length >= 1) {
BytesRef[] typesBytes = new BytesRef[types.length]; MappedFieldType ft = mapperService().fullName(TypeFieldMapper.NAME);
for (int i = 0; i < typesBytes.length; i++) { if (ft != null) {
typesBytes[i] = new BytesRef(types[i]); // 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; return null;
} }

View File

@ -98,8 +98,8 @@ public class ChildrenAggregationBuilder extends ValuesSourceAggregationBuilder<P
parentType = parentFieldMapper.type(); parentType = parentFieldMapper.type();
DocumentMapper parentDocMapper = context.mapperService().documentMapper(parentType); DocumentMapper parentDocMapper = context.mapperService().documentMapper(parentType);
if (parentDocMapper != null) { if (parentDocMapper != null) {
parentFilter = parentDocMapper.typeFilter(); parentFilter = parentDocMapper.typeFilter(context.getQueryShardContext());
childFilter = childDocMapper.typeFilter(); childFilter = childDocMapper.typeFilter(context.getQueryShardContext());
ParentChildIndexFieldData parentChildIndexFieldData = context.fieldData() ParentChildIndexFieldData parentChildIndexFieldData = context.fieldData()
.getForField(parentFieldMapper.fieldType()); .getForField(parentFieldMapper.fieldType());
config.fieldContext(new FieldContext(parentFieldMapper.fieldType().name(), parentChildIndexFieldData, 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 // Only include docs that have the current hit as parent
.add(hitQuery, Occur.FILTER) .add(hitQuery, Occur.FILTER)
// Only include docs that have this inner hits type // Only include docs that have this inner hits type
.add(documentMapper.typeFilter(), Occur.FILTER) .add(documentMapper.typeFilter(context.getQueryShardContext()), Occur.FILTER)
.build(); .build();
if (size() == 0) { if (size() == 0) {
final int count = context.searcher().count(q); final int count = context.searcher().count(q);

View File

@ -99,7 +99,7 @@ public class FieldNamesFieldMapperTests extends ESSingleNodeTestCase {
.bytes(), .bytes(),
XContentType.JSON)); 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 { public void testExplicitEnabled() throws Exception {
@ -117,7 +117,7 @@ public class FieldNamesFieldMapperTests extends ESSingleNodeTestCase {
.bytes(), .bytes(),
XContentType.JSON)); 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 { public void testDisabled() throws Exception {

View File

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

View File

@ -19,16 +19,31 @@
package org.elasticsearch.index.mapper; 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.compress.CompressedXContent;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.fielddata.plain.DocValuesIndexFieldData; 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.plugins.Plugin;
import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.test.ESSingleNodeTestCase;
import org.elasticsearch.test.InternalSettingsPlugin; import org.elasticsearch.test.InternalSettingsPlugin;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import static org.hamcrest.Matchers.instanceOf;
public class TypeFieldMapperTests extends ESSingleNodeTestCase { public class TypeFieldMapperTests extends ESSingleNodeTestCase {
@ -37,13 +52,60 @@ public class TypeFieldMapperTests extends ESSingleNodeTestCase {
return pluginList(InternalSettingsPlugin.class); return pluginList(InternalSettingsPlugin.class);
} }
public void testDocValues() throws Exception { public void testDocValuesMultipleTypes() throws Exception {
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").endObject().endObject().string(); testDocValues(false);
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 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.ConstantScoreQuery;
import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.Directory; import org.apache.lucene.store.Directory;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.IOUtils; 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.io.IOException;
import java.util.Collections;
import java.util.Set;
public class TypeFieldTypeTests extends FieldTypeTestCase { public class TypeFieldTypeTests extends FieldTypeTestCase {
@Override @Override
@ -46,25 +56,62 @@ public class TypeFieldTypeTests extends FieldTypeTestCase {
return new TypeFieldMapper.TypeFieldType(); return new TypeFieldMapper.TypeFieldType();
} }
@Before public void testTermsQueryWhenTypesAreDisabled() throws Exception {
public void setupProperties() { QueryShardContext context = Mockito.mock(QueryShardContext.class);
addModifier(new Modifier("fielddata", true) { Settings indexSettings = Settings.builder()
@Override .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
public void modify(MappedFieldType ft) { .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
TypeFieldMapper.TypeFieldType tft = (TypeFieldMapper.TypeFieldType) ft; .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
tft.setFielddata(tft.fielddata() == false); .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(); Directory dir = newDirectory();
IndexWriter w = new IndexWriter(dir, newIndexWriterConfig()); IndexWriter w = new IndexWriter(dir, newIndexWriterConfig());
IndexReader reader = openReaderWithNewType("my_type", w); 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(); TypeFieldMapper.TypeFieldType ft = new TypeFieldMapper.TypeFieldType();
ft.setName(TypeFieldMapper.NAME); ft.setName(TypeFieldMapper.NAME);
Query query = ft.termQuery("my_type", null); Query query = ft.termQuery("my_type", context);
assertEquals(new MatchAllDocsQuery(), query.rewrite(reader)); assertEquals(new MatchAllDocsQuery(), query.rewrite(reader));
// Make sure that Lucene actually simplifies the query when there is a single type // 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.AggregatorTestCase;
import org.elasticsearch.search.aggregations.metrics.min.InternalMin; import org.elasticsearch.search.aggregations.metrics.min.InternalMin;
import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder;
import org.mockito.Mockito;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
@ -165,8 +166,8 @@ public class ParentToChildrenAggregatorTests extends AggregatorTestCase {
when(mapperService.documentMapper(CHILD_TYPE)).thenReturn(childDocMapper); when(mapperService.documentMapper(CHILD_TYPE)).thenReturn(childDocMapper);
when(mapperService.documentMapper(PARENT_TYPE)).thenReturn(parentDocMapper); when(mapperService.documentMapper(PARENT_TYPE)).thenReturn(parentDocMapper);
when(mapperService.docMappers(false)).thenReturn(Arrays.asList(new DocumentMapper[] { childDocMapper, parentDocMapper })); when(mapperService.docMappers(false)).thenReturn(Arrays.asList(new DocumentMapper[] { childDocMapper, parentDocMapper }));
when(parentDocMapper.typeFilter()).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(PARENT_TYPE))); when(parentDocMapper.typeFilter(Mockito.any())).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(PARENT_TYPE)));
when(childDocMapper.typeFilter()).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(CHILD_TYPE))); when(childDocMapper.typeFilter(Mockito.any())).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(CHILD_TYPE)));
return mapperService; return mapperService;
} }

View File

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