From 8bc133d9551ac4a8a3417952c32f07140447e06b Mon Sep 17 00:00:00 2001 From: Peter-Josef Meisch Date: Sat, 9 Nov 2019 20:54:44 +0100 Subject: [PATCH] DATAES-148 - Dynamic mapping setting. Original PR: #344 --- .../annotations/DynamicMapping.java | 37 +++++++++++++++++++ .../annotations/DynamicMappingValue.java | 26 +++++++++++++ .../core/index/MappingBuilder.java | 30 ++++++++------- .../core/index/MappingBuilderTests.java | 36 ++++++++++++++++++ 4 files changed, 116 insertions(+), 13 deletions(-) create mode 100644 src/main/java/org/springframework/data/elasticsearch/annotations/DynamicMapping.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/annotations/DynamicMappingValue.java diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/DynamicMapping.java b/src/main/java/org/springframework/data/elasticsearch/annotations/DynamicMapping.java new file mode 100644 index 000000000..3d79732ec --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/DynamicMapping.java @@ -0,0 +1,37 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.data.elasticsearch.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to set the dynamic mapping mode + * {@see elasticsearch doc} + * + * @author Peter-Josef Meisch + * @since 4.0 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.FIELD }) +@Documented +public @interface DynamicMapping { + + DynamicMappingValue value() default DynamicMappingValue.True; +} diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/DynamicMappingValue.java b/src/main/java/org/springframework/data/elasticsearch/annotations/DynamicMappingValue.java new file mode 100644 index 000000000..3cec02048 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/DynamicMappingValue.java @@ -0,0 +1,26 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.data.elasticsearch.annotations; + +/** + * values for the {@link DynamicMapping annotation} + * + * @author Peter-Josef Meisch + * @since 4.0 + */ +public enum DynamicMappingValue { + True, False, Strict +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/index/MappingBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/index/MappingBuilder.java index 7e3753176..7355a9511 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/index/MappingBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/index/MappingBuilder.java @@ -33,6 +33,7 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.data.annotation.Transient; import org.springframework.data.elasticsearch.annotations.CompletionContext; import org.springframework.data.elasticsearch.annotations.CompletionField; +import org.springframework.data.elasticsearch.annotations.DynamicMapping; import org.springframework.data.elasticsearch.annotations.DynamicTemplates; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; @@ -86,6 +87,7 @@ public class MappingBuilder { private static final String COMPLETION_MAX_INPUT_LENGTH = "max_input_length"; private static final String COMPLETION_CONTEXTS = "contexts"; + private static final String TYPE_DYNAMIC = "dynamic"; private static final String TYPE_VALUE_KEYWORD = "keyword"; private static final String TYPE_VALUE_GEO_POINT = "geo_point"; private static final String TYPE_VALUE_COMPLETION = "completion"; @@ -120,13 +122,9 @@ public class MappingBuilder { builder.startObject(FIELD_PARENT).field(FIELD_PARAM_TYPE, parentType).endObject(); } - // Properties - builder.startObject(FIELD_PROPERTIES); + mapEntity(builder, entity, true, "", false, FieldType.Auto, null, entity.findAnnotation(DynamicMapping.class)); - mapEntity(builder, entity, true, "", false, FieldType.Auto, null); - - builder.endObject() // FIELD_PROPERTIES - .endObject() // indexType + builder.endObject() // indexType .endObject() // root object .close(); @@ -135,7 +133,7 @@ public class MappingBuilder { private void mapEntity(XContentBuilder builder, @Nullable ElasticsearchPersistentEntity entity, boolean isRootObject, String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType, - @Nullable Field parentFieldAnnotation) throws IOException { + @Nullable Field parentFieldAnnotation, @Nullable DynamicMapping dynamicMapping) throws IOException { boolean writeNestedProperties = !isRootObject && (isAnyPropertyAnnotatedWithField(entity) || nestedOrObjectField); if (writeNestedProperties) { @@ -146,14 +144,17 @@ public class MappingBuilder { if (nestedOrObjectField && FieldType.Nested == fieldType && parentFieldAnnotation != null && parentFieldAnnotation.includeInParent()) { - builder.field("include_in_parent", parentFieldAnnotation.includeInParent()); } - - builder.startObject(FIELD_PROPERTIES); } - if (entity != null) { + if (dynamicMapping != null) { + builder.field(TYPE_DYNAMIC, dynamicMapping.value().name().toLowerCase()); + } + + builder.startObject(FIELD_PROPERTIES); + + if (entity != null) { entity.doWithProperties((PropertyHandler) property -> { try { if (property.isAnnotationPresent(Transient.class) || isInIgnoreFields(property, parentFieldAnnotation)) { @@ -167,9 +168,12 @@ public class MappingBuilder { }); } + builder.endObject(); + if (writeNestedProperties) { - builder.endObject().endObject(); + builder.endObject(); } + } private void buildPropertyMapping(XContentBuilder builder, boolean isRootObject, @@ -205,7 +209,7 @@ public class MappingBuilder { : null; mapEntity(builder, persistentEntity, false, property.getFieldName(), isNestedOrObjectProperty, - fieldAnnotation.type(), fieldAnnotation); + fieldAnnotation.type(), fieldAnnotation, property.findAnnotation(DynamicMapping.class)); if (isNestedOrObjectProperty) { return; diff --git a/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderTests.java b/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderTests.java index f2bbab71c..f6cdd63bc 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderTests.java @@ -514,6 +514,27 @@ public class MappingBuilderTests extends MappingContextBaseTests { assertEquals(expected, mapping, true); } + @Test + void shouldWriteDynamicMappingSettings() throws IOException, JSONException { + + String expected = "{\n" + // + " \"dms\": {\n" + // + " \"dynamic\": \"false\",\n" + // + " \"properties\": {\n" + // + " \"author\": {\n" + // + " \"dynamic\": \"strict\",\n" + // + " \"type\": \"object\",\n" + // + " \"properties\": {}\n" + // + " }\n" + // + " }\n" + // + " }\n" + // + "}\n"; + + String mapping = getMappingBuilder().buildPropertyMapping(ConfigureDynamicMappingEntity.class); + + assertEquals(expected, mapping, true); + } + /** * @author Xiao Yu */ @@ -948,4 +969,19 @@ public class MappingBuilderTests extends MappingContextBaseTests { @Field(termVector = TermVector.with_offsets) private String termVectorWithOffsets; @Field(type = FieldType.Scaled_Float, scalingFactor = 100.0) Double scaledFloat; } + + @Document(indexName = "test-index-configure-dynamic-mapping", type = "dms") + @DynamicMapping(DynamicMappingValue.False) + static class ConfigureDynamicMappingEntity { + + @DynamicMapping(DynamicMappingValue.Strict) @Field(type = FieldType.Object) private Author author; + + public Author getAuthor() { + return author; + } + + public void setAuthor(Author author) { + this.author = author; + } + } }