
org.elasticsearch.index.mapper.NestedObjectMapper Maven / Gradle / Ivy
/*
* 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 - 2025 Weber Informatics LLC | Privacy Policy