Skip to content

Commit d641f45

Browse files
committed
Get templates from cluster.
Signed-off-by: Youssef Aouichaoui <[email protected]>
1 parent fbc7b2c commit d641f45

File tree

5 files changed

+286
-35
lines changed

5 files changed

+286
-35
lines changed

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

+10
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.Collections;
2121
import java.util.Iterator;
2222
import java.util.List;
23+
import java.util.Map;
2324
import java.util.Objects;
2425
import java.util.concurrent.CompletableFuture;
2526
import java.util.stream.Collectors;
@@ -602,6 +603,15 @@ protected <T> SearchDocumentResponse.EntityCreator<T> getEntityCreator(ReadDocum
602603
// region Entity callbacks
603604
protected <T> T maybeCallbackBeforeConvert(T entity, IndexCoordinates index) {
604605

606+
// get entity metadata
607+
Map<String, Object> mapping = indexOps(index).getMapping();
608+
if (mapping.containsKey("dynamic_templates")) {
609+
Object dynamicTemplates = mapping.get("dynamic_templates");
610+
if (dynamicTemplates instanceof List<?> value) {
611+
getRequiredPersistentEntity(entity.getClass()).buildDynamicTemplates(value);
612+
}
613+
}
614+
605615
if (entityCallbacks != null) {
606616
return entityCallbacks.callback(BeforeConvertCallback.class, entity, index);
607617
}

Diff for: src/main/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverter.java

+15-35
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package org.springframework.data.elasticsearch.core.convert;
1717

1818
import static org.springframework.util.PatternMatchUtils.simpleMatch;
19-
import static org.springframework.util.StringUtils.hasText;
2019

2120
import java.time.temporal.TemporalAccessor;
2221
import java.util.*;
@@ -41,12 +40,11 @@
4140
import org.springframework.core.env.EnvironmentCapable;
4241
import org.springframework.core.env.StandardEnvironment;
4342
import org.springframework.data.convert.CustomConversions;
44-
import org.springframework.data.elasticsearch.annotations.DynamicTemplates;
4543
import org.springframework.data.elasticsearch.annotations.FieldType;
4644
import org.springframework.data.elasticsearch.annotations.ScriptedField;
47-
import org.springframework.data.elasticsearch.core.ResourceUtil;
4845
import org.springframework.data.elasticsearch.core.document.Document;
4946
import org.springframework.data.elasticsearch.core.document.SearchDocument;
47+
import org.springframework.data.elasticsearch.core.mapping.DynamicTemplate;
5048
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
5149
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
5250
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
@@ -58,7 +56,6 @@
5856
import org.springframework.data.elasticsearch.core.query.Query;
5957
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
6058
import org.springframework.data.elasticsearch.core.query.SourceFilter;
61-
import org.springframework.data.elasticsearch.support.DefaultStringObjectMap;
6259
import org.springframework.data.mapping.InstanceCreatorMetadata;
6360
import org.springframework.data.mapping.MappingException;
6461
import org.springframework.data.mapping.Parameter;
@@ -395,7 +392,7 @@ private <R> R readEntity(ElasticsearchPersistentEntity<?> entity, Map<String, Ob
395392
}
396393
}
397394

398-
if (targetEntity.isAnnotationPresent(DynamicTemplates.class)) {
395+
if (targetEntity.hasDynamicTemplates()) {
399396
populateFieldsUsingDynamicTemplates(targetEntity, result, document);
400397
}
401398
}
@@ -674,36 +671,19 @@ private <T> void populateScriptFields(ElasticsearchPersistentEntity<?> entity, T
674671
});
675672
}
676673

677-
private <R> void populateFieldsUsingDynamicTemplates(ElasticsearchPersistentEntity<?> targetEntity, R result, Document document) {
678-
String mappingPath = targetEntity.getRequiredAnnotation(DynamicTemplates.class).mappingPath();
679-
if (hasText(mappingPath)) {
680-
String jsonString = ResourceUtil.readFileFromClasspath(mappingPath);
681-
if (hasText(jsonString)) {
682-
Object templates = new DefaultStringObjectMap<>().fromJson(jsonString).get("dynamic_templates");
683-
if (templates instanceof List<?> array) {
684-
for (Object node : array) {
685-
if (node instanceof Map<?, ?> entry) {
686-
Entry<?, ?> templateEntry = entry.entrySet().stream().findFirst().orElse(null);
687-
if (templateEntry != null) {
688-
ElasticsearchPersistentProperty property = targetEntity
689-
.getPersistentPropertyWithFieldName((String) templateEntry.getKey());
690-
if (property != null && property.isDynamicFieldMapping()) {
691-
targetEntity.getPropertyAccessor(result).getProperty(property);
692-
targetEntity.getPropertyAccessor(result).setProperty(property,
693-
document.entrySet().stream().filter(fieldKey -> {
694-
if (templateEntry.getValue() instanceof Map<?, ?> templateValue) {
695-
if (templateValue.containsKey("match")) {
696-
return simpleMatch((String) templateValue.get("match"), fieldKey.getKey());
697-
}
698-
}
699-
700-
return false;
701-
}).collect(Collectors.toMap(Entry::getKey, Entry::getValue)));
702-
}
703-
}
704-
}
705-
}
706-
}
674+
private <R> void populateFieldsUsingDynamicTemplates(ElasticsearchPersistentEntity<?> targetEntity, R result,
675+
Document document) {
676+
for (Entry<String, DynamicTemplate> templateEntry : targetEntity.getDynamicTemplates().entrySet()) {
677+
ElasticsearchPersistentProperty property = targetEntity
678+
.getPersistentPropertyWithFieldName(templateEntry.getKey());
679+
if (property != null && property.isDynamicFieldMapping()) {
680+
targetEntity.getPropertyAccessor(result).setProperty(property,
681+
document.entrySet().stream()
682+
.filter(fieldKey -> templateEntry.getValue().getMatch().stream()
683+
.anyMatch(regex -> simpleMatch(regex, fieldKey.getKey()))
684+
&& templateEntry.getValue().getUnmatch().stream()
685+
.noneMatch(regex -> simpleMatch(regex, fieldKey.getKey())))
686+
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)));
707687
}
708688
}
709689
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
package org.springframework.data.elasticsearch.core.mapping;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
/**
7+
* Immutable Value object encapsulating dynamic template(s).
8+
* {@see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-templates.html">Elastic
9+
* docs</a>}
10+
*
11+
* @author Youssef Aouichaoui
12+
* @since 5.4
13+
*/
14+
public class DynamicTemplate {
15+
/**
16+
* Patterns to match on the field name.
17+
*/
18+
private final List<String> match;
19+
20+
/**
21+
* Path patterns for a nested type to match the field name.
22+
*/
23+
private final List<String> pathMatch;
24+
25+
/**
26+
* Patterns that do not match the field name.
27+
*/
28+
private final List<String> unmatch;
29+
30+
/**
31+
* Path patterns for a nested type that do not match the field name.
32+
*/
33+
private final List<String> pathUnmatch;
34+
35+
/**
36+
* Data types that correspond to the field.
37+
*/
38+
private final List<String> matchMappingType;
39+
40+
/**
41+
* Data types that do not match to the field.
42+
*/
43+
private final List<String> unmatchMappingType;
44+
45+
private DynamicTemplate(Builder builder) {
46+
this.match = builder.match;
47+
this.pathMatch = builder.pathMatch;
48+
49+
this.unmatch = builder.unmatch;
50+
this.pathUnmatch = builder.pathUnmatch;
51+
52+
this.matchMappingType = builder.matchMappingType;
53+
this.unmatchMappingType = builder.unmatchMappingType;
54+
}
55+
56+
public List<String> getMatch() {
57+
return match;
58+
}
59+
60+
public List<String> getPathMatch() {
61+
return pathMatch;
62+
}
63+
64+
public List<String> getUnmatch() {
65+
return unmatch;
66+
}
67+
68+
public List<String> getPathUnmatch() {
69+
return pathUnmatch;
70+
}
71+
72+
public List<String> getMatchMappingType() {
73+
return matchMappingType;
74+
}
75+
76+
public List<String> getUnmatchMappingType() {
77+
return unmatchMappingType;
78+
}
79+
80+
public boolean isRegexMatching() {
81+
return false;
82+
}
83+
84+
public static Builder builder() {
85+
return new Builder();
86+
}
87+
88+
public static class Builder {
89+
private final List<String> match = new ArrayList<>();
90+
private final List<String> pathMatch = new ArrayList<>();
91+
92+
private final List<String> unmatch = new ArrayList<>();
93+
private final List<String> pathUnmatch = new ArrayList<>();
94+
95+
private final List<String> matchMappingType = new ArrayList<>();
96+
private final List<String> unmatchMappingType = new ArrayList<>();
97+
98+
private Builder() {}
99+
100+
/**
101+
* Patterns to match on the field name.
102+
*/
103+
public Builder withMatch(String... match) {
104+
for (String value : match) {
105+
if (value != null) {
106+
parseValues(value, this.match);
107+
}
108+
}
109+
110+
return this;
111+
}
112+
113+
/**
114+
* Path patterns for a nested type to match the field name.
115+
*/
116+
public Builder withPathMatch(String... pathMatch) {
117+
for (String value : pathMatch) {
118+
if (value != null) {
119+
parseValues(value, this.pathMatch);
120+
}
121+
}
122+
123+
return this;
124+
}
125+
126+
/**
127+
* Patterns that do not match the field name.
128+
*/
129+
public Builder withUnmatch(String... unmatch) {
130+
for (String value : unmatch) {
131+
if (value != null) {
132+
parseValues(value, this.unmatch);
133+
}
134+
}
135+
136+
return this;
137+
}
138+
139+
/**
140+
* Path patterns for a nested type that do not match the field name.
141+
*/
142+
public Builder withPathUnmatch(String... pathUnmatch) {
143+
for (String value : pathUnmatch) {
144+
if (value != null) {
145+
parseValues(value, this.pathUnmatch);
146+
}
147+
}
148+
149+
return this;
150+
}
151+
152+
/**
153+
* Data types that correspond to the field.
154+
*/
155+
public Builder withMatchMappingType(String... matchMappingType) {
156+
for (String value : matchMappingType) {
157+
if (value != null) {
158+
parseValues(value, this.matchMappingType);
159+
}
160+
}
161+
162+
return this;
163+
}
164+
165+
/**
166+
* Data types that do not match to the field.
167+
*/
168+
public Builder withUnmatchMappingType(String... unmatchMappingType) {
169+
for (String value : unmatchMappingType) {
170+
if (value != null) {
171+
parseValues(value, this.unmatchMappingType);
172+
}
173+
}
174+
175+
return this;
176+
}
177+
178+
private void parseValues(String source, List<String> target) {
179+
if (source.startsWith("[")) {
180+
target.addAll(List.of(source.replace("[", "").replace("]", "").split(",", -1)));
181+
} else {
182+
target.add(source);
183+
}
184+
}
185+
186+
public DynamicTemplate build() {
187+
return new DynamicTemplate(this);
188+
}
189+
}
190+
}

Diff for: src/main/java/org/springframework/data/elasticsearch/core/mapping/ElasticsearchPersistentEntity.java

+23
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.elasticsearch.core.mapping;
1717

18+
import java.util.List;
19+
import java.util.Map;
1820
import java.util.Set;
1921

2022
import org.springframework.data.elasticsearch.annotations.Document;
@@ -203,4 +205,25 @@ default ElasticsearchPersistentProperty getRequiredSeqNoPrimaryTermProperty() {
203205
* @since 5.2
204206
*/
205207
boolean isAlwaysWriteMapping();
208+
209+
/**
210+
* Retrieves the dynamic templates defined for the current document.
211+
*
212+
* @since 5.4
213+
*/
214+
Map<String, DynamicTemplate> getDynamicTemplates();
215+
216+
/**
217+
* if the mapping should be written to the index on repository bootstrap even if the index already exists.
218+
*
219+
* @since 5.4
220+
*/
221+
boolean hasDynamicTemplates();
222+
223+
/**
224+
* Building the dynamic templates for the current document.
225+
*
226+
* @since 5.4
227+
*/
228+
void buildDynamicTemplates(List<?> dynamicTemplates);
206229
}

0 commit comments

Comments
 (0)