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

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

There is a newer version: 8.14.0
Show newest version
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

package org.elasticsearch.index.mapper;

import org.apache.lucene.index.Term;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.elasticsearch.Version;
import org.elasticsearch.common.Explicit;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.xcontent.XContentBuilder;

import java.io.IOException;
import java.util.Collections;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;

/**
 * A Mapper for nested objects
 */
public class NestedObjectMapper extends ObjectMapper {

    public static final String CONTENT_TYPE = "nested";

    public static class Builder extends ObjectMapper.Builder {

        private Explicit includeInRoot = new Explicit<>(false, false);
        private Explicit includeInParent = new Explicit<>(false, false);
        private final Version indexCreatedVersion;

        public Builder(String name, Version indexCreatedVersion) {
            super(name);
            this.indexCreatedVersion = indexCreatedVersion;
        }

        Builder includeInRoot(boolean includeInRoot) {
            this.includeInRoot = new Explicit<>(includeInRoot, true);
            return this;
        }

        Builder includeInParent(boolean includeInParent) {
            this.includeInParent = new Explicit<>(includeInParent, true);
            return this;
        }

        @Override
        public NestedObjectMapper build(MapperBuilderContext context) {
            return new NestedObjectMapper(name, context.buildFullName(name), buildMappers(false, context), this);
        }
    }

    public static class TypeParser extends ObjectMapper.TypeParser {
        @Override
        public Mapper.Builder parse(String name, Map node, MappingParserContext parserContext)
            throws MapperParsingException {
            NestedObjectMapper.Builder builder = new NestedObjectMapper.Builder(name, parserContext.indexVersionCreated());
            parseNested(name, node, builder);
            for (Iterator> iterator = node.entrySet().iterator(); iterator.hasNext();) {
                Map.Entry entry = iterator.next();
                String fieldName = entry.getKey();
                Object fieldNode = entry.getValue();
                if (parseObjectOrDocumentTypeProperties(fieldName, fieldNode, parserContext, builder)) {
                    iterator.remove();
                }
            }
            return builder;
        }

        protected static void parseNested(String name, Map node, NestedObjectMapper.Builder builder) {
            Object fieldNode = node.get("include_in_parent");
            if (fieldNode != null) {
                boolean includeInParent = XContentMapValues.nodeBooleanValue(fieldNode, name + ".include_in_parent");
                builder.includeInParent(includeInParent);
                node.remove("include_in_parent");
            }
            fieldNode = node.get("include_in_root");
            if (fieldNode != null) {
                boolean includeInRoot = XContentMapValues.nodeBooleanValue(fieldNode, name + ".include_in_root");
                builder.includeInRoot(includeInRoot);
                node.remove("include_in_root");
            }
        }
    }

    private Explicit includeInRoot;
    private Explicit includeInParent;
    private final String nestedTypePath;
    private final Query nestedTypeFilter;

    NestedObjectMapper(String name, String fullPath, Map mappers, Builder builder) {
        super(name, fullPath, builder.enabled, builder.dynamic, mappers);
        this.nestedTypePath = "__" + fullPath;
        this.nestedTypeFilter = new TermQuery(new Term(TypeFieldMapper.NAME, nestedTypePath));
        this.includeInParent = builder.includeInParent;
        this.includeInRoot = builder.includeInRoot;
    }

    public Query nestedTypeFilter() {
        return this.nestedTypeFilter;
    }

    public String nestedTypePath() {
        return this.nestedTypePath;
    }

    @Override
    public boolean isNested() {
        return true;
    }

    public boolean isIncludeInParent() {
        return this.includeInParent.value();
    }

    public void setIncludeInParent(boolean includeInParent) {
        this.includeInParent = new Explicit<>(includeInParent, true);
    }

    public boolean isIncludeInRoot() {
        return this.includeInRoot.value();
    }

    public void setIncludeInRoot(boolean includeInRoot) {
        this.includeInRoot = new Explicit<>(includeInRoot, true);
    }

    public Map getChildren() {
        return Collections.unmodifiableMap(this.mappers);
    }

    @Override
    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
        builder.startObject(simpleName());
        builder.field("type", CONTENT_TYPE);
        if (includeInParent.value()) {
            builder.field("include_in_parent", includeInParent.value());
        }
        if (includeInRoot.value()) {
            builder.field("include_in_root", includeInRoot.value());
        }
        if (dynamic != null) {
            builder.field("dynamic", dynamic.name().toLowerCase(Locale.ROOT));
        }
        if (isEnabled() != Defaults.ENABLED) {
            builder.field("enabled", enabled.value());
        }
        serializeMappers(builder, params);
        return builder.endObject();
    }

    @Override
    public ObjectMapper merge(Mapper mergeWith, MapperService.MergeReason reason) {
        if ((mergeWith instanceof NestedObjectMapper) == false) {
            throw new IllegalArgumentException("can't merge a non nested mapping [" + mergeWith.name() + "] with a nested mapping");
        }
        NestedObjectMapper mergeWithObject = (NestedObjectMapper) mergeWith;
        NestedObjectMapper toMerge = (NestedObjectMapper) clone();

        if (reason == MapperService.MergeReason.INDEX_TEMPLATE) {
            if (mergeWithObject.includeInParent.explicit()) {
                toMerge.includeInParent = mergeWithObject.includeInParent;
            }
            if (mergeWithObject.includeInRoot.explicit()) {
                toMerge.includeInRoot = mergeWithObject.includeInRoot;
            }
        } else {
            if (includeInParent.value() != mergeWithObject.includeInParent.value()) {
                throw new MapperException("the [include_in_parent] parameter can't be updated on a nested object mapping");
            }
            if (includeInRoot.value() != mergeWithObject.includeInRoot.value()) {
                throw new MapperException("the [include_in_root] parameter can't be updated on a nested object mapping");
            }
        }
        toMerge.doMerge(mergeWithObject, reason);
        return toMerge;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy