Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for DynamicTemplates. #2969

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,11 @@
* @since 5.4
*/
String mappedTypeName() default "";

/**
* Maps your data beyond the dynamic field mapping rules.
*
* @since 5.4
*/
boolean dynamicTemplate() default false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ public IndicesTemplate(ElasticsearchIndicesClient client, ClusterTemplate cluste
this.elasticsearchConverter = elasticsearchConverter;
this.boundClass = boundClass;
this.boundIndex = null;

}

public IndicesTemplate(ElasticsearchIndicesClient client, ClusterTemplate clusterTemplate,
Expand All @@ -95,7 +94,6 @@ public IndicesTemplate(ElasticsearchIndicesClient client, ClusterTemplate cluste
this.elasticsearchConverter = elasticsearchConverter;
this.boundClass = null;
this.boundIndex = boundIndex;

}

protected Class<?> checkForBoundClass() {
Expand Down Expand Up @@ -145,6 +143,8 @@ protected boolean doCreate(IndexCoordinates indexCoordinates, Map<String, Object

CreateIndexRequest createIndexRequest = requestConverter.indicesCreateRequest(indexSettings);
CreateIndexResponse createIndexResponse = execute(client -> client.create(createIndexRequest));
// refresh cached mappings
refreshMapping();
return Boolean.TRUE.equals(createIndexResponse.acknowledged());
}

Expand Down Expand Up @@ -241,6 +241,28 @@ public Map<String, Object> getMapping() {
return responseConverter.indicesGetMapping(getMappingResponse, indexCoordinates);
}

/**
* Refreshes the mapping for the current entity.
* <p>
* This method is responsible for retrieving and updating the metadata related to the current entity.
*/
private void refreshMapping() {
if (boundClass == null) {
return;
}

ElasticsearchPersistentEntity<?> entity = this.elasticsearchConverter.getMappingContext()
.getPersistentEntity(boundClass);
if (entity == null) {
return;
}

Object dynamicTemplates = getMapping().get("dynamic_templates");
if (dynamicTemplates instanceof List<?> value) {
entity.buildDynamicTemplates(value);
}
}

@Override
public Settings createSettings() {
return createSettings(checkForBoundClass());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import reactor.core.publisher.Mono;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
Expand Down Expand Up @@ -142,7 +143,12 @@ private Mono<Boolean> doCreate(IndexCoordinates indexCoordinates, Map<String, Ob

CreateIndexRequest createIndexRequest = requestConverter.indicesCreateRequest(indexSettings);
Mono<CreateIndexResponse> createIndexResponse = Mono.from(execute(client -> client.create(createIndexRequest)));
return createIndexResponse.map(CreateIndexResponse::acknowledged);
return createIndexResponse
.doOnNext((result) -> {
// refresh cached mappings
refreshMapping();
})
.map(CreateIndexResponse::acknowledged);
}

@Override
Expand Down Expand Up @@ -218,6 +224,30 @@ public Mono<Document> getMapping() {
return getMappingResponse.map(response -> responseConverter.indicesGetMapping(response, indexCoordinates));
}

/**
* Refreshes the mapping for the current entity.
* <p>
* This method is responsible for retrieving and updating the metadata related to the current entity.
*/
private void refreshMapping() {
if (boundClass == null) {
return;
}

ElasticsearchPersistentEntity<?> entity = this.elasticsearchConverter.getMappingContext()
.getPersistentEntity(boundClass);
if (entity == null) {
return;
}

getMapping().subscribe((mappings) -> {
Object dynamicTemplates = mappings.get("dynamic_templates");
if (dynamicTemplates instanceof List<?> value) {
entity.buildDynamicTemplates(value);
}
});
}

@Override
public Mono<Settings> createSettings() {
return createSettings(checkForBoundClass());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package org.springframework.data.elasticsearch.core.convert;

import static org.springframework.util.PatternMatchUtils.simpleMatch;

import java.time.temporal.TemporalAccessor;
import java.util.*;
import java.util.Map.Entry;
Expand Down Expand Up @@ -42,6 +44,7 @@
import org.springframework.data.elasticsearch.annotations.ScriptedField;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.mapping.DynamicTemplate;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
Expand Down Expand Up @@ -388,6 +391,10 @@ private <R> R readEntity(ElasticsearchPersistentEntity<?> entity, Map<String, Ob
targetEntity.getPropertyAccessor(result).setProperty(property, seqNoPrimaryTerm);
}
}

if (targetEntity.hasDynamicTemplates()) {
populateFieldsUsingDynamicTemplates(targetEntity, result, document);
}
}

if (source instanceof SearchDocument searchDocument) {
Expand Down Expand Up @@ -664,6 +671,28 @@ private <T> void populateScriptFields(ElasticsearchPersistentEntity<?> entity, T
});
}

private <R> void populateFieldsUsingDynamicTemplates(ElasticsearchPersistentEntity<?> targetEntity, R result,
Document document) {
for (Entry<String, DynamicTemplate> templateEntry : targetEntity.getDynamicTemplates().entrySet()) {
ElasticsearchPersistentProperty property = targetEntity
.getPersistentPropertyWithFieldName(templateEntry.getKey());
if (property != null && property.isDynamicFieldMapping()) {
// prepare value
Map<String, Object> values = new HashMap<>();
// TODO: Path match and unmatched
document.entrySet().stream()
.filter(fieldKey -> templateEntry.getValue().getMatch().stream()
.anyMatch(regex -> simpleMatch(regex, fieldKey.getKey()))
&& templateEntry.getValue().getUnmatch().stream()
.noneMatch(regex -> simpleMatch(regex, fieldKey.getKey())))
.forEach(entry -> values.put(entry.getKey(), entry.getValue()));

// set property
targetEntity.getPropertyAccessor(result).setProperty(property, read(property.getType(), Document.from(values)));
}
}
}

/**
* Compute the type to use by checking the given entity against the store type;
*/
Expand Down Expand Up @@ -1035,7 +1064,14 @@ protected void writeProperty(ElasticsearchPersistentProperty property, Object va

if (valueType.isMap()) {
Map<String, Object> mapDbObj = createMap((Map<?, ?>) value, property);
sink.set(property, mapDbObj);
if (property.isDynamicFieldMapping()) {
for (Entry<String, Object> entry : mapDbObj.entrySet()) {
sink.set(entry.getKey(), entry.getValue());
}
} else {
sink.set(property, mapDbObj);
}

return;
}

Expand All @@ -1058,7 +1094,14 @@ protected void writeProperty(ElasticsearchPersistentProperty property, Object va

addCustomTypeKeyIfNecessary(value, document, TypeInformation.of(property.getRawType()));
writeInternal(value, document, entity);
sink.set(property, document);
if (property.isDynamicFieldMapping()) {
// flatten
for (Entry<String, Object> entry : document.entrySet()) {
sink.set(entry.getKey(), entry.getValue());
}
} else {
sink.set(property, document);
}
}

/**
Expand Down Expand Up @@ -1499,7 +1542,11 @@ public void set(ElasticsearchPersistentProperty property, @Nullable Object value
}
}

target.put(property.getFieldName(), value);
set(property.getFieldName(), value);
}

public void set(String key, @Nullable Object value) {
target.put(key, value);
}

private Map<String, Object> getAsMap(Object result) {
Expand Down
Loading