All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.index.mapper.FieldMapper Maven / Gradle / Ivy

The newest version!
/*
 * 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;

import com.carrotsearch.hppc.cursors.ObjectCursor;
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
import com.google.common.base.Function;
import com.google.common.collect.Iterators;

import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.index.IndexOptions;
import org.elasticsearch.Version;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.analysis.NamedAnalyzer;
import org.elasticsearch.index.fielddata.FieldDataType;
import org.elasticsearch.index.mapper.core.TypeParsers;
import org.elasticsearch.index.mapper.internal.AllFieldMapper;
import org.elasticsearch.index.similarity.SimilarityLookupService;
import org.elasticsearch.index.similarity.SimilarityProvider;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

public abstract class FieldMapper extends Mapper implements Cloneable {

    public abstract static class Builder extends Mapper.Builder {

        protected final MappedFieldType fieldType;
        protected final MappedFieldType defaultFieldType;
        private final IndexOptions defaultOptions;
        protected boolean omitNormsSet = false;
        protected String indexName;
        protected Boolean includeInAll;
        protected boolean indexOptionsSet = false;
        protected boolean docValuesSet = false;
        @Nullable
        protected Settings fieldDataSettings;
        protected final MultiFields.Builder multiFieldsBuilder;
        protected CopyTo copyTo;

        protected Builder(String name, MappedFieldType fieldType, MappedFieldType defaultFieldType) {
            super(name);
            this.fieldType = fieldType.clone();
            this.defaultFieldType = defaultFieldType.clone();
            this.defaultOptions = fieldType.indexOptions(); // we have to store it the fieldType is mutable
            multiFieldsBuilder = new MultiFields.Builder();
        }

        public MappedFieldType fieldType() {
            return fieldType;
        }

        public T index(boolean index) {
            if (index) {
                if (fieldType.indexOptions() == IndexOptions.NONE) {
                    /*
                     * the logic here is to reset to the default options only if we are not indexed ie. options are null
                     * if the fieldType has a non-null option we are all good it might have been set through a different
                     * call.
                     */
                    IndexOptions options = getDefaultIndexOption();
                    if (options == IndexOptions.NONE) {
                        // can happen when an existing type on the same index has disabled indexing
                        // since we inherit the default field type from the first mapper that is
                        // created on an index
                        throw new IllegalArgumentException("mapper [" + name + "] has different [index] values from other types of the same index");
                    }
                    fieldType.setIndexOptions(options);
                }
            } else {
                fieldType.setIndexOptions(IndexOptions.NONE);
            }
            return builder;
        }

        protected IndexOptions getDefaultIndexOption() {
            return defaultOptions;
        }

        public T store(boolean store) {
            this.fieldType.setStored(store);
            return builder;
        }

        public T docValues(boolean docValues) {
            this.fieldType.setHasDocValues(docValues);
            this.docValuesSet = true;
            return builder;
        }

        public T storeTermVectors(boolean termVectors) {
            if (termVectors != this.fieldType.storeTermVectors()) {
                this.fieldType.setStoreTermVectors(termVectors);
            } // don't set it to false, it is default and might be flipped by a more specific option
            return builder;
        }

        public T storeTermVectorOffsets(boolean termVectorOffsets) {
            if (termVectorOffsets) {
                this.fieldType.setStoreTermVectors(termVectorOffsets);
            }
            this.fieldType.setStoreTermVectorOffsets(termVectorOffsets);
            return builder;
        }

        public T storeTermVectorPositions(boolean termVectorPositions) {
            if (termVectorPositions) {
                this.fieldType.setStoreTermVectors(termVectorPositions);
            }
            this.fieldType.setStoreTermVectorPositions(termVectorPositions);
            return builder;
        }

        public T storeTermVectorPayloads(boolean termVectorPayloads) {
            if (termVectorPayloads) {
                this.fieldType.setStoreTermVectors(termVectorPayloads);
            }
            this.fieldType.setStoreTermVectorPayloads(termVectorPayloads);
            return builder;
        }

        public T tokenized(boolean tokenized) {
            this.fieldType.setTokenized(tokenized);
            return builder;
        }

        public T boost(float boost) {
            this.fieldType.setBoost(boost);
            return builder;
        }

        public T omitNorms(boolean omitNorms) {
            this.fieldType.setOmitNorms(omitNorms);
            this.omitNormsSet = true;
            return builder;
        }

        public T indexOptions(IndexOptions indexOptions) {
            this.fieldType.setIndexOptions(indexOptions);
            this.indexOptionsSet = true;
            return builder;
        }

        public T indexName(String indexName) {
            this.indexName = indexName;
            return builder;
        }

        public T indexAnalyzer(NamedAnalyzer indexAnalyzer) {
            this.fieldType.setIndexAnalyzer(indexAnalyzer);
            return builder;
        }

        public T searchAnalyzer(NamedAnalyzer searchAnalyzer) {
            this.fieldType.setSearchAnalyzer(searchAnalyzer);
            return builder;
        }

        public T includeInAll(Boolean includeInAll) {
            this.includeInAll = includeInAll;
            return builder;
        }

        public T similarity(SimilarityProvider similarity) {
            this.fieldType.setSimilarity(similarity);
            return builder;
        }

        public T normsLoading(MappedFieldType.Loading normsLoading) {
            this.fieldType.setNormsLoading(normsLoading);
            return builder;
        }

        public T fieldDataSettings(Settings settings) {
            this.fieldDataSettings = settings;
            return builder;
        }

        public Builder nullValue(Object nullValue) {
            this.fieldType.setNullValue(nullValue);
            return this;
        }

        public T multiFieldPathType(ContentPath.Type pathType) {
            multiFieldsBuilder.pathType(pathType);
            return builder;
        }

        public T addMultiField(Mapper.Builder mapperBuilder) {
            multiFieldsBuilder.add(mapperBuilder);
            return builder;
        }

        public T copyTo(CopyTo copyTo) {
            this.copyTo = copyTo;
            return builder;
        }

        protected MappedFieldType.Names buildNames(BuilderContext context) {
            return new MappedFieldType.Names(buildIndexName(context), buildIndexNameClean(context), buildFullName(context));
        }

        protected String buildIndexName(BuilderContext context) {
            if (context.indexCreatedVersion().onOrAfter(Version.V_2_0_0_beta1)) {
                return buildFullName(context);
            }
            String actualIndexName = indexName == null ? name : indexName;
            return context.path().pathAsText(actualIndexName);
        }

        protected String buildIndexNameClean(BuilderContext context) {
            if (context.indexCreatedVersion().onOrAfter(Version.V_2_0_0_beta1)) {
                return buildFullName(context);
            }
            return indexName == null ? name : indexName;
        }

        protected String buildFullName(BuilderContext context) {
            return context.path().fullPathAsText(name);
        }

        protected void setupFieldType(BuilderContext context) {
            fieldType.setNames(buildNames(context));
            if (fieldType.indexAnalyzer() == null && fieldType.tokenized() == false && fieldType.indexOptions() != IndexOptions.NONE) {
                fieldType.setIndexAnalyzer(Lucene.KEYWORD_ANALYZER);
                fieldType.setSearchAnalyzer(Lucene.KEYWORD_ANALYZER);
            }
            if (fieldDataSettings != null) {
                Settings settings = Settings.builder().put(fieldType.fieldDataType().getSettings()).put(fieldDataSettings).build();
                fieldType.setFieldDataType(new FieldDataType(fieldType.fieldDataType().getType(), settings));
            }
            boolean defaultDocValues = false; // pre 2.0
            if (context.indexCreatedVersion().onOrAfter(Version.V_2_0_0_beta1)) {
                defaultDocValues = fieldType.tokenized() == false && fieldType.indexOptions() != IndexOptions.NONE;
            }
            // backcompat for "fielddata: format: docvalues" for now...
            boolean fieldDataDocValues = fieldType.fieldDataType() != null
                && FieldDataType.DOC_VALUES_FORMAT_VALUE.equals(fieldType.fieldDataType().getFormat(context.indexSettings()));
            if (fieldDataDocValues && docValuesSet && fieldType.hasDocValues() == false) {
                // this forces the doc_values setting to be written, so fielddata does not mask the original setting
                defaultDocValues = true;
            }
            defaultFieldType.setHasDocValues(defaultDocValues);
            if (docValuesSet == false) {
                fieldType.setHasDocValues(defaultDocValues || fieldDataDocValues);
            }
        }
    }

    protected MappedFieldType fieldType;
    protected final MappedFieldType defaultFieldType;
    protected MultiFields multiFields;
    protected CopyTo copyTo;
    protected final boolean indexCreatedBefore2x;

    protected FieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType, Settings indexSettings, MultiFields multiFields, CopyTo copyTo) {
        super(simpleName);
        assert indexSettings != null;
        this.indexCreatedBefore2x = Version.indexCreated(indexSettings).before(Version.V_2_0_0_beta1);
        fieldType.freeze();
        this.fieldType = fieldType;
        defaultFieldType.freeze();
        this.defaultFieldType = defaultFieldType;
        this.multiFields = multiFields;
        this.copyTo = copyTo;
    }

    @Override
    public String name() {
        return fieldType().names().fullName();
    }

    public MappedFieldType fieldType() {
        return fieldType;
    }

    /**
     * List of fields where this field should be copied to
     */
    public CopyTo copyTo() {
        return copyTo;
    }

    /**
     * Parse using the provided {@link ParseContext} and return a mapping
     * update if dynamic mappings modified the mappings, or {@code null} if
     * mappings were not modified.
     */
    public Mapper parse(ParseContext context) throws IOException {
        final List fields = new ArrayList<>(2);
        try {
            parseCreateField(context, fields);
            for (Field field : fields) {
                if (!customBoost()) {
                    field.setBoost(fieldType().boost());
                }
                context.doc().add(field);
            }
        } catch (Exception e) {
            throw new MapperParsingException("failed to parse [" + fieldType().names().fullName() + "]", e);
        }
        multiFields.parse(this, context);
        return null;
    }

    /**
     * Parse the field value and populate fields.
     */
    protected abstract void parseCreateField(ParseContext context, List fields) throws IOException;

    /**
     * Derived classes can override it to specify that boost value is set by derived classes.
     */
    protected boolean customBoost() {
        return false;
    }

    @Override
    public Iterator iterator() {
        return multiFields.iterator();
    }

    @Override
    protected FieldMapper clone() {
        try {
            return (FieldMapper) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError(e);
        }
    }

    @Override
    public FieldMapper merge(Mapper mergeWith, boolean updateAllTypes) {
        FieldMapper merged = clone();
        merged.doMerge(mergeWith, updateAllTypes);
        return merged;
    }

    /**
     * Merge changes coming from {@code mergeWith} in place.
     * @param updateAllTypes TODO
     */
    protected void doMerge(Mapper mergeWith, boolean updateAllTypes) {
        if (!this.getClass().equals(mergeWith.getClass())) {
            String mergedType = mergeWith.getClass().getSimpleName();
            if (mergeWith instanceof FieldMapper) {
                mergedType = ((FieldMapper) mergeWith).contentType();
            }
            throw new IllegalArgumentException("mapper [" + fieldType().names().fullName() + "] of different type, current_type [" + contentType() + "], merged_type [" + mergedType + "]");
        }
        FieldMapper fieldMergeWith = (FieldMapper) mergeWith;
        multiFields = multiFields.merge(fieldMergeWith.multiFields);

        // apply changeable values
        this.fieldType = fieldMergeWith.fieldType;
        this.copyTo = fieldMergeWith.copyTo;
    }

    @Override
    public FieldMapper updateFieldType(Map fullNameToFieldType) {
        final MappedFieldType newFieldType = fullNameToFieldType.get(fieldType.names().fullName());
        if (newFieldType == null) {
            // this field does not exist in the mappings yet
            // this can happen if this mapper represents a mapping update
            return this;
        } else if (fieldType.getClass() != newFieldType.getClass()) {
            throw new IllegalStateException("Mixing up field types: " +
                fieldType.getClass() + " != " + newFieldType.getClass() + " on field " + fieldType.names().fullName());
        }
        MultiFields updatedMultiFields = multiFields.updateFieldType(fullNameToFieldType);
        if (fieldType == newFieldType && multiFields == updatedMultiFields) {
            return this; // no change
        }
        FieldMapper updated = clone();
        updated.fieldType = newFieldType;
        updated.multiFields = updatedMultiFields;
        return updated;
    }

    @Override
    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
        builder.startObject(simpleName());
        boolean includeDefaults = params.paramAsBoolean("include_defaults", false);
        doXContentBody(builder, includeDefaults, params);
        return builder.endObject();
    }

    protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException {

        builder.field("type", contentType());
        if (indexCreatedBefore2x && (includeDefaults || !simpleName().equals(fieldType().names().originalIndexName()))) {
            builder.field("index_name", fieldType().names().originalIndexName());
        }

        if (includeDefaults || fieldType().boost() != 1.0f) {
            builder.field("boost", fieldType().boost());
        }

        boolean indexed =  fieldType().indexOptions() != IndexOptions.NONE;
        boolean defaultIndexed = defaultFieldType.indexOptions() != IndexOptions.NONE;
        if (includeDefaults || indexed != defaultIndexed ||
            fieldType().tokenized() != defaultFieldType.tokenized()) {
            builder.field("index", indexTokenizeOptionToString(indexed, fieldType().tokenized()));
        }
        if (includeDefaults || fieldType().stored() != defaultFieldType.stored()) {
            builder.field("store", fieldType().stored());
        }
        doXContentDocValues(builder, includeDefaults);
        if (includeDefaults || fieldType().storeTermVectors() != defaultFieldType.storeTermVectors()) {
            builder.field("term_vector", termVectorOptionsToString(fieldType()));
        }
        if (includeDefaults || fieldType().omitNorms() != defaultFieldType.omitNorms() || fieldType().normsLoading() != null) {
            builder.startObject("norms");
            if (includeDefaults || fieldType().omitNorms() != defaultFieldType.omitNorms()) {
                builder.field("enabled", !fieldType().omitNorms());
            }
            if (fieldType().normsLoading() != null) {
                builder.field(MappedFieldType.Loading.KEY, fieldType().normsLoading());
            }
            builder.endObject();
        }
        if (indexed && (includeDefaults || fieldType().indexOptions() != defaultFieldType.indexOptions())) {
            builder.field("index_options", indexOptionToString(fieldType().indexOptions()));
        }

        if (fieldType().similarity() != null) {
            builder.field("similarity", fieldType().similarity().name());
        } else if (includeDefaults) {
            builder.field("similarity", SimilarityLookupService.DEFAULT_SIMILARITY);
        }

        if (includeDefaults || hasCustomFieldDataSettings()) {
            builder.field("fielddata", fieldType().fieldDataType().getSettings().getAsMap());
        }
        multiFields.toXContent(builder, params);

        if (copyTo != null) {
            copyTo.toXContent(builder, params);
        }
    }

    protected final void doXContentAnalyzers(XContentBuilder builder, boolean includeDefaults) throws IOException {
        if (fieldType.tokenized() == false) {
            return;
        }
        if (fieldType().indexAnalyzer() == null) {
            if (includeDefaults) {
                builder.field("analyzer", "default");
            }
        } else {
            boolean hasDefaultIndexAnalyzer = fieldType().indexAnalyzer().name().equals("default");
            boolean hasDifferentSearchAnalyzer = fieldType().searchAnalyzer().name().equals(fieldType().indexAnalyzer().name()) == false;
            boolean hasDifferentSearchQuoteAnalyzer = fieldType().searchAnalyzer().name().equals(fieldType().searchQuoteAnalyzer().name()) == false;
            if (includeDefaults || hasDefaultIndexAnalyzer == false || hasDifferentSearchAnalyzer || hasDifferentSearchQuoteAnalyzer) {
                builder.field("analyzer", fieldType().indexAnalyzer().name());
                if (hasDifferentSearchAnalyzer || hasDifferentSearchQuoteAnalyzer) {
                    builder.field("search_analyzer", fieldType().searchAnalyzer().name());
                    if (hasDifferentSearchQuoteAnalyzer) {
                        builder.field("search_quote_analyzer", fieldType().searchQuoteAnalyzer().name());
                    }
                }
            }
        }
    }

    protected void doXContentDocValues(XContentBuilder builder, boolean includeDefaults) throws IOException {
        if (includeDefaults || defaultFieldType.hasDocValues() != fieldType().hasDocValues()) {
            builder.field("doc_values", fieldType().hasDocValues());
        }
    }

    protected static String indexOptionToString(IndexOptions indexOption) {
        switch (indexOption) {
            case DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS:
                return TypeParsers.INDEX_OPTIONS_OFFSETS;
            case DOCS_AND_FREQS:
                return TypeParsers.INDEX_OPTIONS_FREQS;
            case DOCS_AND_FREQS_AND_POSITIONS:
                return TypeParsers.INDEX_OPTIONS_POSITIONS;
            case DOCS:
                return TypeParsers.INDEX_OPTIONS_DOCS;
            default:
                throw new IllegalArgumentException("Unknown IndexOptions [" + indexOption + "]");
        }
    }

    public static String termVectorOptionsToString(FieldType fieldType) {
        if (!fieldType.storeTermVectors()) {
            return "no";
        } else if (!fieldType.storeTermVectorOffsets() && !fieldType.storeTermVectorPositions()) {
            return "yes";
        } else if (fieldType.storeTermVectorOffsets() && !fieldType.storeTermVectorPositions()) {
            return "with_offsets";
        } else {
            StringBuilder builder = new StringBuilder("with");
            if (fieldType.storeTermVectorPositions()) {
                builder.append("_positions");
            }
            if (fieldType.storeTermVectorOffsets()) {
                builder.append("_offsets");
            }
            if (fieldType.storeTermVectorPayloads()) {
                builder.append("_payloads");
            }
            return builder.toString();
        }
    }

    protected static String indexTokenizeOptionToString(boolean indexed, boolean tokenized) {
        if (!indexed) {
            return "no";
        } else if (tokenized) {
            return "analyzed";
        } else {
            return "not_analyzed";
        }
    }

    protected boolean hasCustomFieldDataSettings() {
        return fieldType().fieldDataType() != null && fieldType().fieldDataType().equals(defaultFieldType.fieldDataType()) == false;
    }

    protected abstract String contentType();

    public static class MultiFields {

        public static MultiFields empty() {
            return new MultiFields(ContentPath.Type.FULL, ImmutableOpenMap.of());
        }

        public static class Builder {

            private final ImmutableOpenMap.Builder mapperBuilders = ImmutableOpenMap.builder();
            private ContentPath.Type pathType = ContentPath.Type.FULL;

            public Builder pathType(ContentPath.Type pathType) {
                this.pathType = pathType;
                return this;
            }

            public Builder add(Mapper.Builder builder) {
                mapperBuilders.put(builder.name(), builder);
                return this;
            }

            @SuppressWarnings("unchecked")
            public MultiFields build(FieldMapper.Builder mainFieldBuilder, BuilderContext context) {
                if (pathType == ContentPath.Type.FULL && mapperBuilders.isEmpty()) {
                    return empty();
                } else if (mapperBuilders.isEmpty()) {
                    return new MultiFields(pathType, ImmutableOpenMap.of());
                } else {
                    ContentPath.Type origPathType = context.path().pathType();
                    context.path().pathType(pathType);
                    context.path().add(mainFieldBuilder.name());
                    ImmutableOpenMap.Builder mapperBuilders = this.mapperBuilders;
                    for (ObjectObjectCursor cursor : this.mapperBuilders) {
                        String key = cursor.key;
                        Mapper.Builder value = cursor.value;
                        Mapper mapper = value.build(context);
                        assert mapper instanceof FieldMapper;
                        mapperBuilders.put(key, mapper);
                    }
                    context.path().remove();
                    context.path().pathType(origPathType);
                    ImmutableOpenMap.Builder mappers = mapperBuilders.cast();
                    return new MultiFields(pathType, mappers.build());
                }
            }
        }

        private final ContentPath.Type pathType;
        private final ImmutableOpenMap mappers;

        private MultiFields(ContentPath.Type pathType, ImmutableOpenMap mappers) {
            this.pathType = pathType;
            ImmutableOpenMap.Builder builder = new ImmutableOpenMap.Builder<>();
            // we disable the all in multi-field mappers
            for (ObjectObjectCursor cursor : mappers) {
                FieldMapper mapper = cursor.value;
                if (mapper instanceof AllFieldMapper.IncludeInAll) {
                    mapper = (FieldMapper) ((AllFieldMapper.IncludeInAll) mapper).unsetIncludeInAll();
                }
                builder.put(cursor.key, mapper);
            }
            this.mappers = builder.build();
        }

        public void parse(FieldMapper mainField, ParseContext context) throws IOException {
            // TODO: multi fields are really just copy fields, we just need to expose "sub fields" or something that can be part of the mappings
            if (mappers.isEmpty()) {
                return;
            }

            context = context.createMultiFieldContext();

            ContentPath.Type origPathType = context.path().pathType();
            context.path().pathType(pathType);

            context.path().add(mainField.simpleName());
            for (ObjectCursor cursor : mappers.values()) {
                cursor.value.parse(context);
            }
            context.path().remove();
            context.path().pathType(origPathType);
        }

        public MultiFields merge(MultiFields mergeWith) {
            if (pathType != mergeWith.pathType) {
                throw new IllegalArgumentException("Can't change path type from [" + pathType + "] to [" + mergeWith.pathType + "]");
            }
            ImmutableOpenMap.Builder newMappersBuilder = ImmutableOpenMap.builder(mappers);

            for (ObjectCursor cursor : mergeWith.mappers.values()) {
                FieldMapper mergeWithMapper = cursor.value;
                FieldMapper mergeIntoMapper = mappers.get(mergeWithMapper.simpleName());
                if (mergeIntoMapper == null) {
                    // we disable the all in multi-field mappers
                    if (mergeWithMapper instanceof AllFieldMapper.IncludeInAll) {
                        mergeWithMapper = (FieldMapper) ((AllFieldMapper.IncludeInAll) mergeWithMapper).unsetIncludeInAll();
                    }
                    newMappersBuilder.put(mergeWithMapper.simpleName(), mergeWithMapper);
                } else {
                    FieldMapper merged = mergeIntoMapper.merge(mergeWithMapper, false);
                    newMappersBuilder.put(merged.simpleName(), merged); // override previous definition
                }
            }

            ImmutableOpenMap mappers = newMappersBuilder.build();
            return new MultiFields(pathType, mappers);
        }

        public MultiFields updateFieldType(Map fullNameToFieldType) {
            ImmutableOpenMap.Builder newMappersBuilder = null;

            for (ObjectCursor cursor : mappers.values()) {
                FieldMapper updated = cursor.value.updateFieldType(fullNameToFieldType);
                if (updated != cursor.value) {
                    if (newMappersBuilder == null) {
                        newMappersBuilder = ImmutableOpenMap.builder(mappers);
                    }
                    newMappersBuilder.put(updated.simpleName(), updated);
                }
            }

            if (newMappersBuilder == null) {
                return this;
            }

            ImmutableOpenMap mappers = newMappersBuilder.build();
            return new MultiFields(pathType, mappers);
        }

        public Iterator iterator() {
            return Iterators.transform(mappers.values().iterator(), new Function, Mapper>() {
                @Override
                public Mapper apply(@Nullable ObjectCursor cursor) {
                    return cursor.value;
                }
            });
        }

        public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
            if (pathType != ContentPath.Type.FULL) {
                builder.field("path", pathType.name().toLowerCase(Locale.ROOT));
            }
            if (!mappers.isEmpty()) {
                // sort the mappers so we get consistent serialization format
                Mapper[] sortedMappers = mappers.values().toArray(Mapper.class);
                Arrays.sort(sortedMappers, new Comparator() {
                    @Override
                    public int compare(Mapper o1, Mapper o2) {
                        return o1.name().compareTo(o2.name());
                    }
                });
                builder.startObject("fields");
                for (Mapper mapper : sortedMappers) {
                    mapper.toXContent(builder, params);
                }
                builder.endObject();
            }
            return builder;
        }
    }

    /**
     * Represents a list of fields with optional boost factor where the current field should be copied to
     */
    public static class CopyTo {

        private final List copyToFields;

        private CopyTo(List copyToFields) {
            this.copyToFields = copyToFields;
        }

        public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
            if (!copyToFields.isEmpty()) {
                builder.startArray("copy_to");
                for (String field : copyToFields) {
                    builder.value(field);
                }
                builder.endArray();
            }
            return builder;
        }

        public static class Builder {
            private final List copyToBuilders = new ArrayList<>();

            public Builder add(String field) {
                copyToBuilders.add(field);
                return this;
            }

            public CopyTo build() {
                return new CopyTo(Collections.unmodifiableList(copyToBuilders));
            }
        }

        public List copyToFields() {
            return copyToFields;
        }
    }

    /**
     * Fields might not be available before indexing, for example _all, token_count,...
     * When get is called and these fields are requested, this case needs special treatment.
     *
     * @return If the field is available before indexing or not.
     */
    public boolean isGenerated() {
        return false;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy