Skip to content

Commit f339fda

Browse files
committed
DynamicMapping annotation should be applicable to any object field.
Original Pull Request #1779 Closes #1767
1 parent b2a480d commit f339fda

File tree

3 files changed

+114
-49
lines changed

3 files changed

+114
-49
lines changed

Diff for: src/main/java/org/springframework/data/elasticsearch/core/index/MappingBuilder.java

+16-5
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ private void buildPropertyMapping(XContentBuilder builder, boolean isRootObject,
245245
Field fieldAnnotation = property.findAnnotation(Field.class);
246246
boolean isCompletionProperty = property.isCompletionProperty();
247247
boolean isNestedOrObjectProperty = isNestedOrObjectProperty(property);
248+
DynamicMapping dynamicMapping = property.findAnnotation(DynamicMapping.class);
248249

249250
if (!isCompletionProperty && property.isEntity() && hasRelevantAnnotation(property)) {
250251

@@ -259,7 +260,7 @@ private void buildPropertyMapping(XContentBuilder builder, boolean isRootObject,
259260
: null;
260261

261262
mapEntity(builder, persistentEntity, false, property.getFieldName(), true, fieldAnnotation.type(),
262-
fieldAnnotation, property.findAnnotation(DynamicMapping.class));
263+
fieldAnnotation, dynamicMapping);
263264
return;
264265
}
265266
}
@@ -274,9 +275,9 @@ private void buildPropertyMapping(XContentBuilder builder, boolean isRootObject,
274275
if (isRootObject && fieldAnnotation != null && property.isIdProperty()) {
275276
applyDefaultIdFieldMapping(builder, property);
276277
} else if (multiField != null) {
277-
addMultiFieldMapping(builder, property, multiField, isNestedOrObjectProperty);
278+
addMultiFieldMapping(builder, property, multiField, isNestedOrObjectProperty, dynamicMapping);
278279
} else if (fieldAnnotation != null) {
279-
addSingleFieldMapping(builder, property, fieldAnnotation, isNestedOrObjectProperty);
280+
addSingleFieldMapping(builder, property, fieldAnnotation, isNestedOrObjectProperty, dynamicMapping);
280281
}
281282
}
282283

@@ -377,7 +378,7 @@ private void applyDisabledPropertyMapping(XContentBuilder builder, Elasticsearch
377378
* @throws IOException
378379
*/
379380
private void addSingleFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property,
380-
Field annotation, boolean nestedOrObjectField) throws IOException {
381+
Field annotation, boolean nestedOrObjectField, @Nullable DynamicMapping dynamicMapping) throws IOException {
381382

382383
// build the property json, if empty skip it as this is no valid mapping
383384
XContentBuilder propertyBuilder = jsonBuilder().startObject();
@@ -389,6 +390,11 @@ private void addSingleFieldMapping(XContentBuilder builder, ElasticsearchPersist
389390
}
390391

391392
builder.startObject(property.getFieldName());
393+
394+
if (nestedOrObjectField && dynamicMapping != null) {
395+
builder.field(TYPE_DYNAMIC, dynamicMapping.value().name().toLowerCase());
396+
}
397+
392398
addFieldMappingParameters(builder, annotation, nestedOrObjectField);
393399
builder.endObject();
394400
}
@@ -429,10 +435,15 @@ private void addJoinFieldMapping(XContentBuilder builder, ElasticsearchPersisten
429435
* @throws IOException
430436
*/
431437
private void addMultiFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property,
432-
MultiField annotation, boolean nestedOrObjectField) throws IOException {
438+
MultiField annotation, boolean nestedOrObjectField, @Nullable DynamicMapping dynamicMapping) throws IOException {
433439

434440
// main field
435441
builder.startObject(property.getFieldName());
442+
443+
if (nestedOrObjectField && dynamicMapping != null) {
444+
builder.field(TYPE_DYNAMIC, dynamicMapping.value().name().toLowerCase());
445+
}
446+
436447
addFieldMappingParameters(builder, annotation.mainField(), nestedOrObjectField);
437448

438449
// inner fields

Diff for: src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderIntegrationTests.java

+31
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,16 @@ void shouldWriteMappingForDisabledProperty() {
296296
indexOps.delete();
297297
}
298298

299+
@Test // #1767
300+
@DisplayName("should write dynamic mapping entries")
301+
void shouldWriteDynamicMappingEntries() {
302+
303+
IndexOperations indexOps = operations.indexOps(DynamicMappingEntity.class);
304+
indexOps.create();
305+
indexOps.putMapping();
306+
indexOps.delete();
307+
}
308+
299309
@Document(indexName = "ignore-above-index")
300310
static class IgnoreAboveEntity {
301311
@Nullable @Id private String id;
@@ -1082,4 +1092,25 @@ public void setDense_vector(@Nullable float[] dense_vector) {
10821092
this.dense_vector = dense_vector;
10831093
}
10841094
}
1095+
1096+
@Document(indexName = "dynamic-mapping")
1097+
@DynamicMapping(DynamicMappingValue.False)
1098+
static class DynamicMappingEntity {
1099+
1100+
@Nullable @DynamicMapping(DynamicMappingValue.Strict) @Field(type = FieldType.Object) private Author author;
1101+
@Nullable @DynamicMapping(DynamicMappingValue.False) @Field(
1102+
type = FieldType.Object) private Map<String, Object> objectMap;
1103+
@Nullable @DynamicMapping(DynamicMappingValue.False) @Field(
1104+
type = FieldType.Nested) private List<Map<String, Object>> nestedObjectMap;
1105+
1106+
@Nullable
1107+
public Author getAuthor() {
1108+
return author;
1109+
}
1110+
1111+
public void setAuthor(Author author) {
1112+
this.author = author;
1113+
}
1114+
}
1115+
10851116
}

Diff for: src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderUnitTests.java

+67-44
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.util.Collection;
3232
import java.util.Date;
3333
import java.util.HashMap;
34+
import java.util.List;
3435
import java.util.Map;
3536

3637
import org.elasticsearch.search.suggest.completion.context.ContextMapping;
@@ -415,23 +416,42 @@ public void shouldSetFieldMappingProperties() throws JSONException {
415416
assertEquals(expected, mapping, false);
416417
}
417418

418-
@Test
419+
@Test // DATAES-148, #1767
419420
void shouldWriteDynamicMappingSettings() throws JSONException {
420421

421422
String expected = "{\n" + //
422-
" \"dynamic\": \"false\",\n" + //
423-
" \"properties\": {\n" + //
424-
" \"author\": {\n" + //
425-
" \"dynamic\": \"strict\",\n" + //
426-
" \"type\": \"object\",\n" + //
427-
" \"properties\": {}\n" + //
423+
" \"dynamic\": \"false\",\n" + //
424+
" \"properties\": {\n" + //
425+
" \"_class\": {\n" + //
426+
" \"type\": \"keyword\",\n" + //
427+
" \"index\": false,\n" + //
428+
" \"doc_values\": false\n" + //
429+
" },\n" + //
430+
" \"author\": {\n" + //
431+
" \"type\": \"object\",\n" + //
432+
" \"dynamic\": \"strict\",\n" + //
433+
" \"properties\": {\n" + //
434+
" \"_class\": {\n" + //
435+
" \"type\": \"keyword\",\n" + //
436+
" \"index\": false,\n" + //
437+
" \"doc_values\": false\n" + //
438+
" }\n" + //
428439
" }\n" + //
440+
" },\n" + //
441+
" \"objectMap\": {\n" + //
442+
" \"type\": \"object\",\n" + //
443+
" \"dynamic\": \"false\"\n" + //
444+
" },\n" + //
445+
" \"nestedObjectMap\": {\n" + //
446+
" \"type\": \"nested\",\n" + //
447+
" \"dynamic\": \"false\"\n" + //
429448
" }\n" + //
430-
"}\n";
449+
" }\n" + //
450+
"}"; //
431451

432452
String mapping = getMappingBuilder().buildPropertyMapping(ConfigureDynamicMappingEntity.class);
433453

434-
assertEquals(expected, mapping, false);
454+
assertEquals(expected, mapping, true);
435455
}
436456

437457
@Test // DATAES-784
@@ -608,39 +628,38 @@ void shouldWriteTypeHintEntries() throws JSONException {
608628
assertEquals(expected, mapping, false);
609629
}
610630

611-
@Test // #1727
612-
@DisplayName("should map according to the annotated properties")
613-
void shouldMapAccordingToTheAnnotatedProperties() throws JSONException {
614-
615-
String expected = "{\n" +
616-
" \"properties\": {\n" + //
617-
" \"field1\": {\n" + //
618-
" \"type\": \"date\",\n" + //
619-
" \"format\": \"date_optional_time||epoch_millis\"\n" + //
620-
" },\n" + //
621-
" \"field2\": {\n" + //
622-
" \"type\": \"date\",\n" + //
623-
" \"format\": \"basic_date\"\n" + //
624-
" },\n" + //
625-
" \"field3\": {\n" + //
626-
" \"type\": \"date\",\n" + //
627-
" \"format\": \"basic_date||basic_time\"\n" + //
628-
" },\n" + //
629-
" \"field4\": {\n" + //
630-
" \"type\": \"date\",\n" + //
631-
" \"format\": \"date_optional_time||epoch_millis||dd.MM.uuuu\"\n" + //
632-
" },\n" + //
633-
" \"field5\": {\n" + //
634-
" \"type\": \"date\",\n" + //
635-
" \"format\": \"dd.MM.uuuu\"\n" + //
636-
" }\n" + //
637-
" }\n" + //
638-
"}"; //
639-
640-
String mapping = getMappingBuilder().buildPropertyMapping(DateFormatsEntity.class);
641-
642-
assertEquals(expected, mapping, false);
643-
}
631+
@Test // #1727
632+
@DisplayName("should map according to the annotated properties")
633+
void shouldMapAccordingToTheAnnotatedProperties() throws JSONException {
634+
635+
String expected = "{\n" + " \"properties\": {\n" + //
636+
" \"field1\": {\n" + //
637+
" \"type\": \"date\",\n" + //
638+
" \"format\": \"date_optional_time||epoch_millis\"\n" + //
639+
" },\n" + //
640+
" \"field2\": {\n" + //
641+
" \"type\": \"date\",\n" + //
642+
" \"format\": \"basic_date\"\n" + //
643+
" },\n" + //
644+
" \"field3\": {\n" + //
645+
" \"type\": \"date\",\n" + //
646+
" \"format\": \"basic_date||basic_time\"\n" + //
647+
" },\n" + //
648+
" \"field4\": {\n" + //
649+
" \"type\": \"date\",\n" + //
650+
" \"format\": \"date_optional_time||epoch_millis||dd.MM.uuuu\"\n" + //
651+
" },\n" + //
652+
" \"field5\": {\n" + //
653+
" \"type\": \"date\",\n" + //
654+
" \"format\": \"dd.MM.uuuu\"\n" + //
655+
" }\n" + //
656+
" }\n" + //
657+
"}"; //
658+
659+
String mapping = getMappingBuilder().buildPropertyMapping(DateFormatsEntity.class);
660+
661+
assertEquals(expected, mapping, false);
662+
}
644663

645664
@Document(indexName = "ignore-above-index")
646665
static class IgnoreAboveEntity {
@@ -666,7 +685,6 @@ public void setMessage(@Nullable String message) {
666685
}
667686
}
668687

669-
670688
static class FieldNameEntity {
671689

672690
@Document(indexName = "fieldname-index")
@@ -1199,6 +1217,10 @@ static class FieldMappingParameters {
11991217
static class ConfigureDynamicMappingEntity {
12001218

12011219
@Nullable @DynamicMapping(DynamicMappingValue.Strict) @Field(type = FieldType.Object) private Author author;
1220+
@Nullable @DynamicMapping(DynamicMappingValue.False) @Field(
1221+
type = FieldType.Object) private Map<String, Object> objectMap;
1222+
@Nullable @DynamicMapping(DynamicMappingValue.False) @Field(
1223+
type = FieldType.Nested) private List<Map<String, Object>> nestedObjectMap;
12021224

12031225
@Nullable
12041226
public Author getAuthor() {
@@ -1474,7 +1496,8 @@ static class DateFormatsEntity {
14741496
@Nullable @Id private String id;
14751497
@Nullable @Field(type = FieldType.Date) private LocalDateTime field1;
14761498
@Nullable @Field(type = FieldType.Date, format = DateFormat.basic_date) private LocalDateTime field2;
1477-
@Nullable @Field(type = FieldType.Date, format = { DateFormat.basic_date, DateFormat.basic_time }) private LocalDateTime field3;
1499+
@Nullable @Field(type = FieldType.Date,
1500+
format = { DateFormat.basic_date, DateFormat.basic_time }) private LocalDateTime field3;
14781501
@Nullable @Field(type = FieldType.Date, pattern = "dd.MM.uuuu") private LocalDateTime field4;
14791502
@Nullable @Field(type = FieldType.Date, format = {}, pattern = "dd.MM.uuuu") private LocalDateTime field5;
14801503

0 commit comments

Comments
 (0)