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:
parent
d928ae210d
commit
977016ba25
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
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);
|
||||
// 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);
|
||||
}
|
||||
return super.fielddataBuilder();
|
||||
}
|
||||
|
||||
@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) {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
return defaultFieldType;
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue