diff --git a/core/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java b/core/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java index ff90dd7af98..6534bca7c80 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java @@ -113,7 +113,7 @@ public class DocumentMapper implements ToXContent { this.rootMappers.put(TimestampFieldMapper.class, new TimestampFieldMapper(indexSettings, mapperService.fullName(TimestampFieldMapper.NAME))); this.rootMappers.put(TTLFieldMapper.class, new TTLFieldMapper(indexSettings)); this.rootMappers.put(VersionFieldMapper.class, new VersionFieldMapper(indexSettings)); - this.rootMappers.put(ParentFieldMapper.class, new ParentFieldMapper(indexSettings, mapperService.fullName(ParentFieldMapper.NAME))); + this.rootMappers.put(ParentFieldMapper.class, new ParentFieldMapper(indexSettings, mapperService.fullName(ParentFieldMapper.NAME), /* parent type */builder.name())); // _field_names last so that it can see all other fields this.rootMappers.put(FieldNamesFieldMapper.class, new FieldNamesFieldMapper(indexSettings, mapperService.fullName(FieldNamesFieldMapper.NAME))); } diff --git a/core/src/main/java/org/elasticsearch/index/mapper/DocumentMapperParser.java b/core/src/main/java/org/elasticsearch/index/mapper/DocumentMapperParser.java index 33281dc86f4..e0e4051d802 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/DocumentMapperParser.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/DocumentMapperParser.java @@ -147,8 +147,8 @@ public class DocumentMapperParser { } } - public Mapper.TypeParser.ParserContext parserContext() { - return new Mapper.TypeParser.ParserContext(analysisService, similarityLookupService, mapperService, typeParsers, indexVersionCreated, parseFieldMatcher); + public Mapper.TypeParser.ParserContext parserContext(String type) { + return new Mapper.TypeParser.ParserContext(type, analysisService, similarityLookupService, mapperService, typeParsers, indexVersionCreated, parseFieldMatcher); } public DocumentMapper parse(String source) throws MapperParsingException { @@ -206,7 +206,7 @@ public class DocumentMapperParser { } - Mapper.TypeParser.ParserContext parserContext = parserContext(); + Mapper.TypeParser.ParserContext parserContext = parserContext(type); // parse RootObjectMapper DocumentMapper.Builder docBuilder = doc(indexSettings, (RootObjectMapper.Builder) rootObjectTypeParser.parse(type, mapping, parserContext), mapperService); // Add default mapping for the plugged-in meta mappers diff --git a/core/src/main/java/org/elasticsearch/index/mapper/Mapper.java b/core/src/main/java/org/elasticsearch/index/mapper/Mapper.java index eaeea7ae396..ab561461f83 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/Mapper.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/Mapper.java @@ -81,6 +81,8 @@ public abstract class Mapper implements ToXContent, Iterable { class ParserContext { + private final String type; + private final AnalysisService analysisService; private final SimilarityLookupService similarityLookupService; @@ -93,9 +95,10 @@ public abstract class Mapper implements ToXContent, Iterable { private final ParseFieldMatcher parseFieldMatcher; - public ParserContext(AnalysisService analysisService, SimilarityLookupService similarityLookupService, + public ParserContext(String type, AnalysisService analysisService, SimilarityLookupService similarityLookupService, MapperService mapperService, ImmutableMap typeParsers, - Version indexVersionCreated, ParseFieldMatcher parseFieldMatcher) { + Version indexVersionCreated, ParseFieldMatcher parseFieldMatcher) { + this.type = type; this.analysisService = analysisService; this.similarityLookupService = similarityLookupService; this.mapperService = mapperService; @@ -104,6 +107,10 @@ public abstract class Mapper implements ToXContent, Iterable { this.parseFieldMatcher = parseFieldMatcher; } + public String type() { + return type; + } + public AnalysisService analysisService() { return analysisService; } diff --git a/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java b/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java index a5496a5715c..7173b437685 100755 --- a/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java @@ -70,7 +70,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.function.Predicate; import static org.elasticsearch.common.collect.MapBuilder.newMapBuilder; @@ -556,7 +555,7 @@ public class MapperService extends AbstractIndexComponent implements Closeable { final ImmutableMap unmappedFieldMappers = this.unmappedFieldTypes; MappedFieldType fieldType = unmappedFieldMappers.get(type); if (fieldType == null) { - final Mapper.TypeParser.ParserContext parserContext = documentMapperParser().parserContext(); + final Mapper.TypeParser.ParserContext parserContext = documentMapperParser().parserContext(type); Mapper.TypeParser typeParser = parserContext.typeParser(type); if (typeParser == null) { throw new IllegalArgumentException("No mapper found for type [" + type + "]"); diff --git a/core/src/main/java/org/elasticsearch/index/mapper/internal/ParentFieldMapper.java b/core/src/main/java/org/elasticsearch/index/mapper/internal/ParentFieldMapper.java index 2ba19b4ef15..9d3d9d383e4 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/internal/ParentFieldMapper.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/internal/ParentFieldMapper.java @@ -20,6 +20,7 @@ package org.elasticsearch.index.mapper.internal; import org.apache.lucene.document.Field; import org.apache.lucene.document.SortedDocValuesField; +import org.apache.lucene.index.DocValuesType; import org.apache.lucene.index.IndexOptions; import org.apache.lucene.queries.TermsQuery; import org.apache.lucene.search.Query; @@ -33,15 +34,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.loader.SettingsLoader; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.fielddata.FieldDataType; -import org.elasticsearch.index.mapper.DocumentMapper; -import org.elasticsearch.index.mapper.MappedFieldType; -import org.elasticsearch.index.mapper.Mapper; -import org.elasticsearch.index.mapper.MapperParsingException; -import org.elasticsearch.index.mapper.MergeMappingException; -import org.elasticsearch.index.mapper.MergeResult; -import org.elasticsearch.index.mapper.MetadataFieldMapper; -import org.elasticsearch.index.mapper.ParseContext; -import org.elasticsearch.index.mapper.Uid; +import org.elasticsearch.index.mapper.*; import org.elasticsearch.index.query.QueryParseContext; import java.io.IOException; @@ -67,6 +60,7 @@ public class ParentFieldMapper extends MetadataFieldMapper { public static final String NAME = ParentFieldMapper.NAME; public static final MappedFieldType FIELD_TYPE = new ParentFieldType(); + public static final MappedFieldType JOIN_FIELD_TYPE = new ParentFieldType(); static { FIELD_TYPE.setIndexOptions(IndexOptions.DOCS); @@ -77,41 +71,66 @@ public class ParentFieldMapper extends MetadataFieldMapper { FIELD_TYPE.setSearchAnalyzer(Lucene.KEYWORD_ANALYZER); FIELD_TYPE.setNames(new MappedFieldType.Names(NAME)); FIELD_TYPE.freeze(); + + JOIN_FIELD_TYPE.setHasDocValues(true); + JOIN_FIELD_TYPE.setDocValuesType(DocValuesType.SORTED); + JOIN_FIELD_TYPE.freeze(); } } public static class Builder extends MetadataFieldMapper.Builder { + private String parentType; + protected String indexName; - private String type; + private final String documentType; - public Builder() { + private final MappedFieldType parentJoinFieldType = Defaults.JOIN_FIELD_TYPE.clone(); + + private final MappedFieldType childJoinFieldType = Defaults.JOIN_FIELD_TYPE.clone(); + + public Builder(String documentType) { super(Defaults.NAME, Defaults.FIELD_TYPE); this.indexName = name; + this.documentType = documentType; builder = this; } public Builder type(String type) { - this.type = type; + this.parentType = type; return builder; } + @Override + public Builder fieldDataSettings(Settings fieldDataSettings) { + Settings settings = Settings.builder().put(childJoinFieldType.fieldDataType().getSettings()).put(fieldDataSettings).build(); + childJoinFieldType.setFieldDataType(new FieldDataType(childJoinFieldType.fieldDataType().getType(), settings)); + return this; + } + @Override public ParentFieldMapper build(BuilderContext context) { - if (type == null) { + if (parentType == null) { throw new MapperParsingException("[_parent] field mapping must contain the [type] option"); } - setupFieldType(context); - fieldType.setHasDocValues(context.indexCreatedVersion().onOrAfter(Version.V_2_0_0_beta1)); - return new ParentFieldMapper(fieldType, type, context.indexSettings()); + parentJoinFieldType.setNames(new MappedFieldType.Names(joinField(documentType))); + parentJoinFieldType.setFieldDataType(null); + childJoinFieldType.setNames(new MappedFieldType.Names(joinField(parentType))); + if (context.indexCreatedVersion().before(Version.V_2_0_0_beta1)) { + childJoinFieldType.setHasDocValues(false); + childJoinFieldType.setDocValuesType(DocValuesType.NONE); + parentJoinFieldType.setHasDocValues(false); + parentJoinFieldType.setDocValuesType(DocValuesType.NONE); + } + return new ParentFieldMapper(fieldType, parentJoinFieldType, childJoinFieldType, parentType, context.indexSettings()); } } public static class TypeParser implements Mapper.TypeParser { @Override public Mapper.Builder parse(String name, Map node, ParserContext parserContext) throws MapperParsingException { - Builder builder = new Builder(); + Builder builder = new Builder(parserContext.type()); for (Iterator> iterator = node.entrySet().iterator(); iterator.hasNext();) { Map.Entry entry = iterator.next(); String fieldName = Strings.toUnderscoreCase(entry.getKey()); @@ -222,25 +241,50 @@ public class ParentFieldMapper extends MetadataFieldMapper { } } - private final String type; + private final String parentType; + // determines the field data settings + private MappedFieldType childJoinFieldType; + // has no impact of field data settings, is just here for creating a join field, the parent field mapper in the child type pointing to this type determines the field data settings for this join field + private final MappedFieldType parentJoinFieldType; - protected ParentFieldMapper(MappedFieldType fieldType, String type, Settings indexSettings) { - super(NAME, setupDocValues(indexSettings, fieldType), setupDocValues(indexSettings, Defaults.FIELD_TYPE), indexSettings); - this.type = type; + protected ParentFieldMapper(MappedFieldType fieldType, MappedFieldType parentJoinFieldType, MappedFieldType childJoinFieldType, String parentType, Settings indexSettings) { + super(NAME, fieldType, Defaults.FIELD_TYPE, indexSettings); + this.parentType = parentType; + this.parentJoinFieldType = parentJoinFieldType; + this.parentJoinFieldType.freeze(); + this.childJoinFieldType = childJoinFieldType; + if (childJoinFieldType != null) { + this.childJoinFieldType.freeze(); + } } - public ParentFieldMapper(Settings indexSettings, MappedFieldType existing) { - this(existing == null ? Defaults.FIELD_TYPE.clone() : existing.clone(), null, indexSettings); + public ParentFieldMapper(Settings indexSettings, MappedFieldType existing, String parentType) { + this(existing == null ? Defaults.FIELD_TYPE.clone() : existing.clone(), joinFieldTypeForParentType(parentType, indexSettings), null, null, indexSettings); } - static MappedFieldType setupDocValues(Settings indexSettings, MappedFieldType fieldType) { - fieldType = fieldType.clone(); - fieldType.setHasDocValues(Version.indexCreated(indexSettings).onOrAfter(Version.V_2_0_0_beta1)); - return fieldType; + private static MappedFieldType joinFieldTypeForParentType(String parentType, Settings indexSettings) { + MappedFieldType parentJoinFieldType = Defaults.JOIN_FIELD_TYPE.clone(); + parentJoinFieldType.setNames(new MappedFieldType.Names(joinField(parentType))); + + Version indexCreated = Version.indexCreated(indexSettings); + if (indexCreated.before(Version.V_2_0_0_beta1)) { + parentJoinFieldType.setHasDocValues(false); + parentJoinFieldType.setDocValuesType(DocValuesType.NONE); + } + parentJoinFieldType.freeze(); + return parentJoinFieldType; + } + + public MappedFieldType getParentJoinFieldType() { + return parentJoinFieldType; + } + + public MappedFieldType getChildJoinFieldType() { + return childJoinFieldType; } public String type() { - return type; + return parentType; } @Override @@ -257,8 +301,8 @@ public class ParentFieldMapper extends MetadataFieldMapper { @Override protected void parseCreateField(ParseContext context, List fields) throws IOException { boolean parent = context.docMapper().isParent(context.type()); - if (parent && fieldType().hasDocValues()) { - fields.add(createJoinField(context.type(), context.id())); + if (parent) { + addJoinFieldIfNeeded(fields, parentJoinFieldType, context.id()); } if (!active()) { @@ -269,10 +313,8 @@ public class ParentFieldMapper extends MetadataFieldMapper { // we are in the parsing of _parent phase String parentId = context.parser().text(); context.sourceToParse().parent(parentId); - fields.add(new Field(fieldType().names().indexName(), Uid.createUid(context.stringBuilder(), type, parentId), fieldType())); - if (fieldType().hasDocValues()) { - fields.add(createJoinField(type, parentId)); - } + fields.add(new Field(fieldType().names().indexName(), Uid.createUid(context.stringBuilder(), parentType, parentId), fieldType())); + addJoinFieldIfNeeded(fields, childJoinFieldType, parentId); } else { // otherwise, we are running it post processing of the xcontent String parsedParentId = context.doc().get(Defaults.NAME); @@ -283,11 +325,9 @@ public class ParentFieldMapper extends MetadataFieldMapper { throw new MapperParsingException("No parent id provided, not within the document, and not externally"); } // we did not add it in the parsing phase, add it now - fields.add(new Field(fieldType().names().indexName(), Uid.createUid(context.stringBuilder(), type, parentId), fieldType())); - if (fieldType().hasDocValues()) { - fields.add(createJoinField(type, parentId)); - } - } else if (parentId != null && !parsedParentId.equals(Uid.createUid(context.stringBuilder(), type, parentId))) { + fields.add(new Field(fieldType().names().indexName(), Uid.createUid(context.stringBuilder(), parentType, parentId), fieldType())); + addJoinFieldIfNeeded(fields, childJoinFieldType, parentId); + } else if (parentId != null && !parsedParentId.equals(Uid.createUid(context.stringBuilder(), parentType, parentId))) { throw new MapperParsingException("Parent id mismatch, document value is [" + Uid.createUid(parsedParentId).id() + "], while external value is [" + parentId + "]"); } } @@ -295,9 +335,10 @@ public class ParentFieldMapper extends MetadataFieldMapper { // we have parent mapping, yet no value was set, ignore it... } - private SortedDocValuesField createJoinField(String parentType, String id) { - String joinField = joinField(parentType); - return new SortedDocValuesField(joinField, new BytesRef(id)); + private void addJoinFieldIfNeeded(List fields, MappedFieldType fieldType, String id) { + if (fieldType.hasDocValues()) { + fields.add(new SortedDocValuesField(fieldType.names().indexName(), new BytesRef(id))); + } } public static String joinField(String parentType) { @@ -309,6 +350,10 @@ public class ParentFieldMapper extends MetadataFieldMapper { return CONTENT_TYPE; } + private boolean joinFieldHasCustomFieldDataSettings() { + return childJoinFieldType != null && childJoinFieldType.fieldDataType() != null && childJoinFieldType.fieldDataType().equals(Defaults.JOIN_FIELD_TYPE.fieldDataType()) == false; + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { if (!active()) { @@ -317,9 +362,9 @@ public class ParentFieldMapper extends MetadataFieldMapper { boolean includeDefaults = params.paramAsBoolean("include_defaults", false); builder.startObject(CONTENT_TYPE); - builder.field("type", type); - if (includeDefaults || hasCustomFieldDataSettings()) { - builder.field("fielddata", (Map) fieldType().fieldDataType().getSettings().getAsMap()); + builder.field("type", parentType); + if (includeDefaults || joinFieldHasCustomFieldDataSettings()) { + builder.field("fielddata", (Map) childJoinFieldType.fieldDataType().getSettings().getAsMap()); } builder.endObject(); return builder; @@ -329,8 +374,23 @@ public class ParentFieldMapper extends MetadataFieldMapper { public void merge(Mapper mergeWith, MergeResult mergeResult) throws MergeMappingException { super.merge(mergeWith, mergeResult); ParentFieldMapper fieldMergeWith = (ParentFieldMapper) mergeWith; - if (Objects.equals(type, fieldMergeWith.type) == false) { - mergeResult.addConflict("The _parent field's type option can't be changed: [" + type + "]->[" + fieldMergeWith.type + "]"); + if (Objects.equals(parentType, fieldMergeWith.parentType) == false) { + mergeResult.addConflict("The _parent field's type option can't be changed: [" + parentType + "]->[" + fieldMergeWith.parentType + "]"); + } + + List conflicts = new ArrayList<>(); + fieldType().checkCompatibility(fieldMergeWith.fieldType(), conflicts, true); // always strict, this cannot change + parentJoinFieldType.checkCompatibility(fieldMergeWith.parentJoinFieldType, conflicts, true); // same here + if (childJoinFieldType != null) { + // TODO: this can be set to false when the old parent/child impl is removed, we can do eager global ordinals loading per type. + childJoinFieldType.checkCompatibility(fieldMergeWith.childJoinFieldType, conflicts, mergeResult.updateAllTypes() == false); + } + for (String conflict : conflicts) { + mergeResult.addConflict(conflict); + } + + if (active() && mergeResult.simulate() == false && mergeResult.hasConflicts() == false) { + childJoinFieldType = fieldMergeWith.childJoinFieldType.clone(); } } @@ -338,7 +398,7 @@ public class ParentFieldMapper extends MetadataFieldMapper { * @return Whether the _parent field is actually configured. */ public boolean active() { - return type != null; + return parentType != null; } } diff --git a/core/src/main/java/org/elasticsearch/index/mapper/object/RootObjectMapper.java b/core/src/main/java/org/elasticsearch/index/mapper/object/RootObjectMapper.java index cb0815aaee1..160c8ed61f9 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/object/RootObjectMapper.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/object/RootObjectMapper.java @@ -245,7 +245,7 @@ public class RootObjectMapper extends ObjectMapper { if (dynamicTemplate == null) { return null; } - Mapper.TypeParser.ParserContext parserContext = context.docMapperParser().parserContext(); + Mapper.TypeParser.ParserContext parserContext = context.docMapperParser().parserContext(name); String mappingType = dynamicTemplate.mappingType(dynamicType); Mapper.TypeParser typeParser = parserContext.typeParser(mappingType); if (typeParser == null) { diff --git a/core/src/main/java/org/elasticsearch/search/SearchService.java b/core/src/main/java/org/elasticsearch/search/SearchService.java index ea1eb710115..59ec671dd8f 100644 --- a/core/src/main/java/org/elasticsearch/search/SearchService.java +++ b/core/src/main/java/org/elasticsearch/search/SearchService.java @@ -62,6 +62,7 @@ import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MappedFieldType.Loading; import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.index.mapper.internal.ParentFieldMapper; import org.elasticsearch.index.query.TemplateQueryParser; import org.elasticsearch.index.search.stats.ShardSearchStats; import org.elasticsearch.index.search.stats.StatsGroupsParseElement; @@ -910,7 +911,22 @@ public class SearchService extends AbstractLifecycleComponent { final Map warmUp = new HashMap<>(); for (DocumentMapper docMapper : mapperService.docMappers(false)) { for (FieldMapper fieldMapper : docMapper.mappers()) { - final FieldDataType fieldDataType = fieldMapper.fieldType().fieldDataType(); + final FieldDataType fieldDataType; + final String indexName; + if (fieldMapper instanceof ParentFieldMapper) { + MappedFieldType joinFieldType = ((ParentFieldMapper) fieldMapper).getChildJoinFieldType(); + if (joinFieldType == null) { + continue; + } + fieldDataType = joinFieldType.fieldDataType(); + // TODO: this can be removed in 3.0 when the old parent/child impl is removed: + // related to: https://github.com/elastic/elasticsearch/pull/12418 + indexName = fieldMapper.fieldType().names().indexName(); + } else { + fieldDataType = fieldMapper.fieldType().fieldDataType(); + indexName = fieldMapper.fieldType().names().indexName(); + } + if (fieldDataType == null) { continue; } @@ -918,7 +934,6 @@ public class SearchService extends AbstractLifecycleComponent { continue; } - final String indexName = fieldMapper.fieldType().names().indexName(); if (warmUp.containsKey(indexName)) { continue; } @@ -964,14 +979,27 @@ public class SearchService extends AbstractLifecycleComponent { final Map warmUpGlobalOrdinals = new HashMap<>(); for (DocumentMapper docMapper : mapperService.docMappers(false)) { for (FieldMapper fieldMapper : docMapper.mappers()) { - final FieldDataType fieldDataType = fieldMapper.fieldType().fieldDataType(); + final FieldDataType fieldDataType; + final String indexName; + if (fieldMapper instanceof ParentFieldMapper) { + MappedFieldType joinFieldType = ((ParentFieldMapper) fieldMapper).getChildJoinFieldType(); + if (joinFieldType == null) { + continue; + } + fieldDataType = joinFieldType.fieldDataType(); + // TODO: this can be removed in 3.0 when the old parent/child impl is removed: + // related to: https://github.com/elastic/elasticsearch/pull/12418 + indexName = fieldMapper.fieldType().names().indexName(); + } else { + fieldDataType = fieldMapper.fieldType().fieldDataType(); + indexName = fieldMapper.fieldType().names().indexName(); + } if (fieldDataType == null) { continue; } if (fieldDataType.getLoading() != Loading.EAGER_GLOBAL_ORDINALS) { continue; } - final String indexName = fieldMapper.fieldType().names().indexName(); if (warmUpGlobalOrdinals.containsKey(indexName)) { continue; } diff --git a/core/src/test/java/org/elasticsearch/index/fielddata/AbstractFieldDataTestCase.java b/core/src/test/java/org/elasticsearch/index/fielddata/AbstractFieldDataTestCase.java index d42c86cbc03..5980688bfbe 100644 --- a/core/src/test/java/org/elasticsearch/index/fielddata/AbstractFieldDataTestCase.java +++ b/core/src/test/java/org/elasticsearch/index/fielddata/AbstractFieldDataTestCase.java @@ -88,7 +88,7 @@ public abstract class AbstractFieldDataTestCase extends ESSingleNodeTestCase { } else if (type.getType().equals("geo_point")) { fieldType = MapperBuilders.geoPointField(fieldName).docValues(docValues).fieldDataSettings(type.getSettings()).build(context).fieldType(); } else if (type.getType().equals("_parent")) { - fieldType = new ParentFieldMapper.Builder().type(fieldName).build(context).fieldType(); + fieldType = new ParentFieldMapper.Builder("_type").type(fieldName).build(context).fieldType(); } else if (type.getType().equals("binary")) { fieldType = MapperBuilders.binaryField(fieldName).docValues(docValues).fieldDataSettings(type.getSettings()).build(context).fieldType(); } else { diff --git a/core/src/test/java/org/elasticsearch/index/mapper/internal/ParentFieldMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/internal/ParentFieldMapperTests.java new file mode 100644 index 00000000000..b094c1f0e77 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/index/mapper/internal/ParentFieldMapperTests.java @@ -0,0 +1,158 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.index.mapper.internal; + +import org.apache.lucene.index.DocValuesType; +import org.elasticsearch.Version; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.fielddata.FieldDataType; +import org.elasticsearch.index.mapper.ContentPath; +import org.elasticsearch.index.mapper.MappedFieldType.Loading; +import org.elasticsearch.index.mapper.Mapper; +import org.elasticsearch.test.ESTestCase; + +import static org.elasticsearch.common.settings.Settings.settingsBuilder; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +public class ParentFieldMapperTests extends ESTestCase { + + public void testPost2Dot0LazyLoading() { + ParentFieldMapper.Builder builder = new ParentFieldMapper.Builder("child"); + builder.type("parent"); + builder.fieldDataSettings(createFDSettings(Loading.LAZY)); + + ParentFieldMapper parentFieldMapper = builder.build(new Mapper.BuilderContext(post2Dot0IndexSettings(), new ContentPath(0))); + + assertThat(parentFieldMapper.getParentJoinFieldType().names().indexName(), equalTo("_parent#child")); + assertThat(parentFieldMapper.getParentJoinFieldType().fieldDataType(), nullValue()); + assertThat(parentFieldMapper.getParentJoinFieldType().hasDocValues(), is(true)); + assertThat(parentFieldMapper.getParentJoinFieldType().docValuesType(), equalTo(DocValuesType.SORTED)); + + assertThat(parentFieldMapper.getChildJoinFieldType().names().indexName(), equalTo("_parent#parent")); + assertThat(parentFieldMapper.getChildJoinFieldType().fieldDataType().getLoading(), equalTo(Loading.LAZY)); + assertThat(parentFieldMapper.getChildJoinFieldType().hasDocValues(), is(true)); + assertThat(parentFieldMapper.getChildJoinFieldType().docValuesType(), equalTo(DocValuesType.SORTED)); + } + + public void testPost2Dot0EagerLoading() { + ParentFieldMapper.Builder builder = new ParentFieldMapper.Builder("child"); + builder.type("parent"); + builder.fieldDataSettings(createFDSettings(Loading.EAGER)); + + ParentFieldMapper parentFieldMapper = builder.build(new Mapper.BuilderContext(post2Dot0IndexSettings(), new ContentPath(0))); + + assertThat(parentFieldMapper.getParentJoinFieldType().names().indexName(), equalTo("_parent#child")); + assertThat(parentFieldMapper.getParentJoinFieldType().fieldDataType(), nullValue()); + assertThat(parentFieldMapper.getParentJoinFieldType().hasDocValues(), is(true)); + assertThat(parentFieldMapper.getParentJoinFieldType().docValuesType(), equalTo(DocValuesType.SORTED)); + + assertThat(parentFieldMapper.getChildJoinFieldType().names().indexName(), equalTo("_parent#parent")); + assertThat(parentFieldMapper.getChildJoinFieldType().fieldDataType().getLoading(), equalTo(Loading.EAGER)); + assertThat(parentFieldMapper.getChildJoinFieldType().hasDocValues(), is(true)); + assertThat(parentFieldMapper.getChildJoinFieldType().docValuesType(), equalTo(DocValuesType.SORTED)); + } + + public void testPost2Dot0EagerGlobalOrdinalsLoading() { + ParentFieldMapper.Builder builder = new ParentFieldMapper.Builder("child"); + builder.type("parent"); + builder.fieldDataSettings(createFDSettings(Loading.EAGER_GLOBAL_ORDINALS)); + + ParentFieldMapper parentFieldMapper = builder.build(new Mapper.BuilderContext(post2Dot0IndexSettings(), new ContentPath(0))); + + assertThat(parentFieldMapper.getParentJoinFieldType().names().indexName(), equalTo("_parent#child")); + assertThat(parentFieldMapper.getParentJoinFieldType().fieldDataType(), nullValue()); + assertThat(parentFieldMapper.getParentJoinFieldType().hasDocValues(), is(true)); + assertThat(parentFieldMapper.getParentJoinFieldType().docValuesType(), equalTo(DocValuesType.SORTED)); + + assertThat(parentFieldMapper.getChildJoinFieldType().names().indexName(), equalTo("_parent#parent")); + assertThat(parentFieldMapper.getChildJoinFieldType().fieldDataType().getLoading(), equalTo(Loading.EAGER_GLOBAL_ORDINALS)); + assertThat(parentFieldMapper.getChildJoinFieldType().hasDocValues(), is(true)); + assertThat(parentFieldMapper.getChildJoinFieldType().docValuesType(), equalTo(DocValuesType.SORTED)); + } + + public void testPre2Dot0LazyLoading() { + ParentFieldMapper.Builder builder = new ParentFieldMapper.Builder("child"); + builder.type("parent"); + builder.fieldDataSettings(createFDSettings(Loading.LAZY)); + + ParentFieldMapper parentFieldMapper = builder.build(new Mapper.BuilderContext(pre2Dot0IndexSettings(), new ContentPath(0))); + + assertThat(parentFieldMapper.getParentJoinFieldType().names().indexName(), equalTo("_parent#child")); + assertThat(parentFieldMapper.getParentJoinFieldType().fieldDataType(), nullValue()); + assertThat(parentFieldMapper.getParentJoinFieldType().hasDocValues(), is(false)); + assertThat(parentFieldMapper.getParentJoinFieldType().docValuesType(), equalTo(DocValuesType.NONE)); + + assertThat(parentFieldMapper.getChildJoinFieldType().names().indexName(), equalTo("_parent#parent")); + assertThat(parentFieldMapper.getChildJoinFieldType().fieldDataType().getLoading(), equalTo(Loading.LAZY)); + assertThat(parentFieldMapper.getChildJoinFieldType().hasDocValues(), is(false)); + assertThat(parentFieldMapper.getChildJoinFieldType().docValuesType(), equalTo(DocValuesType.NONE)); + } + + public void testPre2Dot0EagerLoading() { + ParentFieldMapper.Builder builder = new ParentFieldMapper.Builder("child"); + builder.type("parent"); + builder.fieldDataSettings(createFDSettings(Loading.EAGER)); + + ParentFieldMapper parentFieldMapper = builder.build(new Mapper.BuilderContext(pre2Dot0IndexSettings(), new ContentPath(0))); + + assertThat(parentFieldMapper.getParentJoinFieldType().names().indexName(), equalTo("_parent#child")); + assertThat(parentFieldMapper.getParentJoinFieldType().fieldDataType(), nullValue()); + assertThat(parentFieldMapper.getParentJoinFieldType().hasDocValues(), is(false)); + assertThat(parentFieldMapper.getParentJoinFieldType().docValuesType(), equalTo(DocValuesType.NONE)); + + assertThat(parentFieldMapper.getChildJoinFieldType().names().indexName(), equalTo("_parent#parent")); + assertThat(parentFieldMapper.getChildJoinFieldType().fieldDataType().getLoading(), equalTo(Loading.EAGER)); + assertThat(parentFieldMapper.getChildJoinFieldType().hasDocValues(), is(false)); + assertThat(parentFieldMapper.getChildJoinFieldType().docValuesType(), equalTo(DocValuesType.NONE)); + } + + public void testPre2Dot0EagerGlobalOrdinalsLoading() { + ParentFieldMapper.Builder builder = new ParentFieldMapper.Builder("child"); + builder.type("parent"); + builder.fieldDataSettings(createFDSettings(Loading.EAGER_GLOBAL_ORDINALS)); + + ParentFieldMapper parentFieldMapper = builder.build(new Mapper.BuilderContext(pre2Dot0IndexSettings(), new ContentPath(0))); + + assertThat(parentFieldMapper.getParentJoinFieldType().names().indexName(), equalTo("_parent#child")); + assertThat(parentFieldMapper.getParentJoinFieldType().fieldDataType(), nullValue()); + assertThat(parentFieldMapper.getParentJoinFieldType().hasDocValues(), is(false)); + assertThat(parentFieldMapper.getParentJoinFieldType().docValuesType(), equalTo(DocValuesType.NONE)); + + assertThat(parentFieldMapper.getChildJoinFieldType().names().indexName(), equalTo("_parent#parent")); + assertThat(parentFieldMapper.getChildJoinFieldType().fieldDataType().getLoading(), equalTo(Loading.EAGER_GLOBAL_ORDINALS)); + assertThat(parentFieldMapper.getChildJoinFieldType().hasDocValues(), is(false)); + assertThat(parentFieldMapper.getChildJoinFieldType().docValuesType(), equalTo(DocValuesType.NONE)); + } + + private static Settings pre2Dot0IndexSettings() { + return Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_1_6_3).build(); + } + + private static Settings post2Dot0IndexSettings() { + return Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_2_1_0).build(); + } + + private static Settings createFDSettings(Loading loading) { + return new FieldDataType("child", settingsBuilder().put(Loading.KEY, loading)).getSettings(); + } + +} diff --git a/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java b/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java index 66a9e9f1c51..fbf934951d9 100644 --- a/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java +++ b/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java @@ -1199,7 +1199,7 @@ public class ChildQuerySearchIT extends ESIntegTestCase { .endObject().endObject()).get(); fail(); } catch (MergeMappingException e) { - assertThat(e.toString(), containsString("Merge failed with failures {[The _parent field's type option can't be changed: [null]->[parent]]}")); + assertThat(e.toString(), containsString("Merge failed with failures {[The _parent field's type option can't be changed: [null]->[parent]")); } } diff --git a/core/src/test/java/org/elasticsearch/search/child/ParentFieldLoadingBwcIT.java b/core/src/test/java/org/elasticsearch/search/child/ParentFieldLoadingBwcIT.java index feb18286838..31de3e4aac4 100644 --- a/core/src/test/java/org/elasticsearch/search/child/ParentFieldLoadingBwcIT.java +++ b/core/src/test/java/org/elasticsearch/search/child/ParentFieldLoadingBwcIT.java @@ -129,14 +129,14 @@ public class ParentFieldLoadingBwcIT extends ESIntegTestCase { assertAcked(prepareCreate("test") .setSettings(indexSettings) .addMapping("parent") - .addMapping("child", childMapping(MappedFieldType.Loading.LAZY)) - .setUpdateAllTypes(true)); + .addMapping("child", childMapping(MappedFieldType.Loading.LAZY))); ensureGreen(); client().prepareIndex("test", "parent", "1").setSource("{}").get(); client().prepareIndex("test", "child", "1").setParent("1").setSource("{}").get(); refresh(); + IndicesStatsResponse r = client().admin().indices().prepareStats("test").setFieldData(true).setFieldDataFields("*").get(); ClusterStatsResponse response = client().admin().cluster().prepareClusterStats().get(); assertThat(response.getIndicesStats().getFieldData().getMemorySizeInBytes(), equalTo(0l)); @@ -145,8 +145,7 @@ public class ParentFieldLoadingBwcIT extends ESIntegTestCase { assertAcked(prepareCreate("test") .setSettings(indexSettings) .addMapping("parent") - .addMapping("child", "_parent", "type=parent") - .setUpdateAllTypes(true)); + .addMapping("child", "_parent", "type=parent")); ensureGreen(); client().prepareIndex("test", "parent", "1").setSource("{}").get(); @@ -162,8 +161,7 @@ public class ParentFieldLoadingBwcIT extends ESIntegTestCase { assertAcked(prepareCreate("test") .setSettings(indexSettings) .addMapping("parent") - .addMapping("child", childMapping(MappedFieldType.Loading.EAGER)) - .setUpdateAllTypes(true)); + .addMapping("child", childMapping(MappedFieldType.Loading.EAGER))); ensureGreen(); client().prepareIndex("test", "parent", "1").setSource("{}").get(); @@ -178,8 +176,7 @@ public class ParentFieldLoadingBwcIT extends ESIntegTestCase { assertAcked(prepareCreate("test") .setSettings(indexSettings) .addMapping("parent") - .addMapping("child", childMapping(MappedFieldType.Loading.EAGER_GLOBAL_ORDINALS)) - .setUpdateAllTypes(true)); + .addMapping("child", childMapping(MappedFieldType.Loading.EAGER_GLOBAL_ORDINALS))); ensureGreen(); // Need to do 2 separate refreshes, otherwise we have 1 segment and then we can't measure if global ordinals @@ -227,7 +224,7 @@ public class ParentFieldLoadingBwcIT extends ESIntegTestCase { MapperService mapperService = indexService.mapperService(); DocumentMapper documentMapper = mapperService.documentMapper("child"); if (documentMapper != null) { - verified = documentMapper.parentFieldMapper().fieldType().fieldDataType().getLoading() == MappedFieldType.Loading.EAGER_GLOBAL_ORDINALS; + verified = documentMapper.parentFieldMapper().getChildJoinFieldType().fieldDataType().getLoading() == MappedFieldType.Loading.EAGER_GLOBAL_ORDINALS; } } assertTrue(verified); diff --git a/core/src/test/java/org/elasticsearch/search/child/ParentFieldLoadingIT.java b/core/src/test/java/org/elasticsearch/search/child/ParentFieldLoadingIT.java index 8a57e22297f..729b6acd8f8 100644 --- a/core/src/test/java/org/elasticsearch/search/child/ParentFieldLoadingIT.java +++ b/core/src/test/java/org/elasticsearch/search/child/ParentFieldLoadingIT.java @@ -57,8 +57,7 @@ public class ParentFieldLoadingIT extends ESIntegTestCase { assertAcked(prepareCreate("test") .setSettings(indexSettings) .addMapping("parent") - .addMapping("child", childMapping(MappedFieldType.Loading.LAZY)) - .setUpdateAllTypes(true)); + .addMapping("child", childMapping(MappedFieldType.Loading.LAZY))); ensureGreen(); client().prepareIndex("test", "parent", "1").setSource("{}").get(); @@ -73,8 +72,7 @@ public class ParentFieldLoadingIT extends ESIntegTestCase { assertAcked(prepareCreate("test") .setSettings(indexSettings) .addMapping("parent") - .addMapping("child", "_parent", "type=parent") - .setUpdateAllTypes(true)); + .addMapping("child", "_parent", "type=parent")); ensureGreen(); client().prepareIndex("test", "parent", "1").setSource("{}").get(); @@ -89,8 +87,7 @@ public class ParentFieldLoadingIT extends ESIntegTestCase { assertAcked(prepareCreate("test") .setSettings(indexSettings) .addMapping("parent") - .addMapping("child", childMapping(MappedFieldType.Loading.EAGER)) - .setUpdateAllTypes(true)); + .addMapping("child", childMapping(MappedFieldType.Loading.EAGER))); ensureGreen(); client().prepareIndex("test", "parent", "1").setSource("{}").get(); @@ -105,8 +102,7 @@ public class ParentFieldLoadingIT extends ESIntegTestCase { assertAcked(prepareCreate("test") .setSettings(indexSettings) .addMapping("parent") - .addMapping("child", childMapping(MappedFieldType.Loading.EAGER_GLOBAL_ORDINALS)) - .setUpdateAllTypes(true)); + .addMapping("child", childMapping(MappedFieldType.Loading.EAGER_GLOBAL_ORDINALS))); ensureGreen(); // Need to do 2 separate refreshes, otherwise we have 1 segment and then we can't measure if global ordinals @@ -153,7 +149,7 @@ public class ParentFieldLoadingIT extends ESIntegTestCase { MapperService mapperService = indexService.mapperService(); DocumentMapper documentMapper = mapperService.documentMapper("child"); if (documentMapper != null) { - verified = documentMapper.parentFieldMapper().fieldType().fieldDataType().getLoading() == MappedFieldType.Loading.EAGER_GLOBAL_ORDINALS; + verified = documentMapper.parentFieldMapper().getChildJoinFieldType().fieldDataType().getLoading() == MappedFieldType.Loading.EAGER_GLOBAL_ORDINALS; } } assertTrue(verified);