From 73d52cd686a69187af57c598c189411290972e21 Mon Sep 17 00:00:00 2001 From: Peter-Josef Meisch Date: Sat, 17 Apr 2021 14:53:12 +0200 Subject: [PATCH] DynamicMapping annotation should be applicable to any object field. Original Pull Request #1779 Closes #1767 --- .../core/index/MappingBuilder.java | 21 ++++++--- .../index/MappingBuilderIntegrationTests.java | 44 ++++++++++++++++--- .../core/index/MappingBuilderUnitTests.java | 30 +++++++++---- 3 files changed, 76 insertions(+), 19 deletions(-) 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 757888170..0c3e6a252 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 @@ -216,6 +216,7 @@ public class MappingBuilder { Field fieldAnnotation = property.findAnnotation(Field.class); boolean isCompletionProperty = property.isCompletionProperty(); boolean isNestedOrObjectProperty = isNestedOrObjectProperty(property); + DynamicMapping dynamicMapping = property.findAnnotation(DynamicMapping.class); if (!isCompletionProperty && property.isEntity() && hasRelevantAnnotation(property)) { @@ -230,7 +231,7 @@ public class MappingBuilder { : null; mapEntity(builder, persistentEntity, false, property.getFieldName(), true, fieldAnnotation.type(), - fieldAnnotation, property.findAnnotation(DynamicMapping.class)); + fieldAnnotation, dynamicMapping); return; } } @@ -245,9 +246,9 @@ public class MappingBuilder { if (isRootObject && fieldAnnotation != null && property.isIdProperty()) { applyDefaultIdFieldMapping(builder, property); } else if (multiField != null) { - addMultiFieldMapping(builder, property, multiField, isNestedOrObjectProperty); + addMultiFieldMapping(builder, property, multiField, isNestedOrObjectProperty, dynamicMapping); } else if (fieldAnnotation != null) { - addSingleFieldMapping(builder, property, fieldAnnotation, isNestedOrObjectProperty); + addSingleFieldMapping(builder, property, fieldAnnotation, isNestedOrObjectProperty, dynamicMapping); } } @@ -328,7 +329,7 @@ public class MappingBuilder { * @throws IOException */ private void addSingleFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property, - Field annotation, boolean nestedOrObjectField) throws IOException { + Field annotation, boolean nestedOrObjectField, @Nullable DynamicMapping dynamicMapping) throws IOException { // build the property json, if empty skip it as this is no valid mapping XContentBuilder propertyBuilder = jsonBuilder().startObject(); @@ -340,6 +341,11 @@ public class MappingBuilder { } builder.startObject(property.getFieldName()); + + if (nestedOrObjectField && dynamicMapping != null) { + builder.field(TYPE_DYNAMIC, dynamicMapping.value().name().toLowerCase()); + } + addFieldMappingParameters(builder, annotation, nestedOrObjectField); builder.endObject(); } @@ -380,10 +386,15 @@ public class MappingBuilder { * @throws IOException */ private void addMultiFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property, - MultiField annotation, boolean nestedOrObjectField) throws IOException { + MultiField annotation, boolean nestedOrObjectField, @Nullable DynamicMapping dynamicMapping) throws IOException { // main field builder.startObject(property.getFieldName()); + + if (nestedOrObjectField && dynamicMapping != null) { + builder.field(TYPE_DYNAMIC, dynamicMapping.value().name().toLowerCase()); + } + addFieldMappingParameters(builder, annotation.mainField(), nestedOrObjectField); // inner fields diff --git a/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderIntegrationTests.java b/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderIntegrationTests.java index 008d6a942..c937a4f24 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderIntegrationTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 the original author or authors. + * Copyright 2013-2021 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. @@ -30,6 +30,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import java.lang.Integer; +import java.lang.Object; import java.math.BigDecimal; import java.util.Collection; import java.util.Collections; @@ -263,11 +264,21 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests { /** * @author Xiao Yu */ - @Setter - @Getter - @NoArgsConstructor - @AllArgsConstructor - @Builder + @Test // #1767 + @DisplayName("should write dynamic mapping entries") + void shouldWriteDynamicMappingEntries() { + + IndexOperations indexOps = operations.indexOps(DynamicMappingEntity.class); + indexOps.create(); + indexOps.putMapping(); + indexOps.delete(); + } + + @Setter + @Getter + @NoArgsConstructor + @AllArgsConstructor + @Builder @Document(indexName = "ignore-above-index") static class IgnoreAboveEntity { @@ -647,4 +658,25 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests { @Field(type = FieldType.Text, termVector = TermVector.with_positions_offsets_payloads) private String with_positions_offsets_payloads; } + + @Document(indexName = "dynamic-mapping") + @DynamicMapping(DynamicMappingValue.False) + static class DynamicMappingEntity { + + @Nullable @DynamicMapping(DynamicMappingValue.Strict) @Field(type = FieldType.Object) private Author author; + @Nullable @DynamicMapping(DynamicMappingValue.False) @Field( + type = FieldType.Object) private Map objectMap; + @Nullable @DynamicMapping(DynamicMappingValue.False) @Field( + type = FieldType.Nested) private List> nestedObjectMap; + + @Nullable + public Author getAuthor() { + return author; + } + + public void setAuthor(Author author) { + this.author = author; + } + } + } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderUnitTests.java b/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderUnitTests.java index de1a249d2..ea66c2031 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderUnitTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderUnitTests.java @@ -37,6 +37,7 @@ import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -410,19 +411,28 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests { assertEquals(expected, mapping, true); } - @Test + @Test // DATAES-148, #1767 void shouldWriteDynamicMappingSettings() throws JSONException { String expected = "{\n" + // - " \"dynamic\": \"false\",\n" + // - " \"properties\": {\n" + // - " \"author\": {\n" + // - " \"dynamic\": \"strict\",\n" + // - " \"type\": \"object\",\n" + // - " \"properties\": {}\n" + // + " \"dynamic\": \"false\",\n" + // + " \"properties\": {\n" + // + " \"author\": {\n" + // + " \"type\": \"object\",\n" + // + " \"dynamic\": \"strict\",\n" + // + " \"properties\": {\n" + // " }\n" + // + " },\n" + // + " \"objectMap\": {\n" + // + " \"type\": \"object\",\n" + // + " \"dynamic\": \"false\"\n" + // + " },\n" + // + " \"nestedObjectMap\": {\n" + // + " \"type\": \"nested\",\n" + // + " \"dynamic\": \"false\"\n" + // " }\n" + // - "}\n"; + " }\n" + // + "}"; // String mapping = getMappingBuilder().buildPropertyMapping(ConfigureDynamicMappingEntity.class); @@ -898,6 +908,10 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests { static class ConfigureDynamicMappingEntity { @Nullable @DynamicMapping(DynamicMappingValue.Strict) @Field(type = FieldType.Object) private Author author; + @Nullable @DynamicMapping(DynamicMappingValue.False) @Field( + type = FieldType.Object) private Map objectMap; + @Nullable @DynamicMapping(DynamicMappingValue.False) @Field( + type = FieldType.Nested) private List> nestedObjectMap; @Nullable public Author getAuthor() {