Skip to content

Commit

Permalink
Get templates from cluster.
Browse files Browse the repository at this point in the history
Signed-off-by: Youssef Aouichaoui <[email protected]>
  • Loading branch information
youssef3wi committed Aug 27, 2024
1 parent f198fb4 commit 5a582ac
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -602,6 +603,15 @@ protected <T> SearchDocumentResponse.EntityCreator<T> getEntityCreator(ReadDocum
// region Entity callbacks
protected <T> T maybeCallbackBeforeConvert(T entity, IndexCoordinates index) {

// get entity metadata
Map<String, Object> mapping = indexOps(index).getMapping();
if (mapping.containsKey("dynamic_templates")) {
Object dynamicTemplates = mapping.get("dynamic_templates");
if (dynamicTemplates instanceof List<?> value) {
getRequiredPersistentEntity(entity.getClass()).buildDynamicTemplates(value);
}
}

if (entityCallbacks != null) {
return entityCallbacks.callback(BeforeConvertCallback.class, entity, index);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package org.springframework.data.elasticsearch.core.convert;

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

import java.time.temporal.TemporalAccessor;
import java.util.*;
Expand All @@ -41,12 +40,11 @@
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.elasticsearch.annotations.DynamicTemplates;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.ScriptedField;
import org.springframework.data.elasticsearch.core.ResourceUtil;
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 All @@ -58,7 +56,6 @@
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
import org.springframework.data.elasticsearch.core.query.SourceFilter;
import org.springframework.data.elasticsearch.support.DefaultStringObjectMap;
import org.springframework.data.mapping.InstanceCreatorMetadata;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.Parameter;
Expand Down Expand Up @@ -395,7 +392,7 @@ private <R> R readEntity(ElasticsearchPersistentEntity<?> entity, Map<String, Ob
}
}

if (targetEntity.isAnnotationPresent(DynamicTemplates.class)) {
if (targetEntity.hasDynamicTemplates()) {
populateFieldsUsingDynamicTemplates(targetEntity, result, document);
}
}
Expand Down Expand Up @@ -674,36 +671,19 @@ private <T> void populateScriptFields(ElasticsearchPersistentEntity<?> entity, T
});
}

private <R> void populateFieldsUsingDynamicTemplates(ElasticsearchPersistentEntity<?> targetEntity, R result, Document document) {
String mappingPath = targetEntity.getRequiredAnnotation(DynamicTemplates.class).mappingPath();
if (hasText(mappingPath)) {
String jsonString = ResourceUtil.readFileFromClasspath(mappingPath);
if (hasText(jsonString)) {
Object templates = new DefaultStringObjectMap<>().fromJson(jsonString).get("dynamic_templates");
if (templates instanceof List<?> array) {
for (Object node : array) {
if (node instanceof Map<?, ?> entry) {
Entry<?, ?> templateEntry = entry.entrySet().stream().findFirst().orElse(null);
if (templateEntry != null) {
ElasticsearchPersistentProperty property = targetEntity
.getPersistentPropertyWithFieldName((String) templateEntry.getKey());
if (property != null && property.isDynamicFieldMapping()) {
targetEntity.getPropertyAccessor(result).getProperty(property);
targetEntity.getPropertyAccessor(result).setProperty(property,
document.entrySet().stream().filter(fieldKey -> {
if (templateEntry.getValue() instanceof Map<?, ?> templateValue) {
if (templateValue.containsKey("match")) {
return simpleMatch((String) templateValue.get("match"), fieldKey.getKey());
}
}

return false;
}).collect(Collectors.toMap(Entry::getKey, Entry::getValue)));
}
}
}
}
}
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()) {
targetEntity.getPropertyAccessor(result).setProperty(property,
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())))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package org.springframework.data.elasticsearch.core.mapping;

import java.util.ArrayList;
import java.util.List;

/**
* Immutable Value object encapsulating dynamic template(s).
* {@see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-templates.html">Elastic
* docs</a>}
*
* @author Youssef Aouichaoui
* @since 5.4
*/
public class DynamicTemplate {
/**
* Patterns to match on the field name.
*/
private final List<String> match;

/**
* Path patterns for a nested type to match the field name.
*/
private final List<String> pathMatch;

/**
* Patterns that do not match the field name.
*/
private final List<String> unmatch;

/**
* Path patterns for a nested type that do not match the field name.
*/
private final List<String> pathUnmatch;

/**
* Data types that correspond to the field.
*/
private final List<String> matchMappingType;

/**
* Data types that do not match to the field.
*/
private final List<String> unmatchMappingType;

private DynamicTemplate(Builder builder) {
this.match = builder.match;
this.pathMatch = builder.pathMatch;

this.unmatch = builder.unmatch;
this.pathUnmatch = builder.pathUnmatch;

this.matchMappingType = builder.matchMappingType;
this.unmatchMappingType = builder.unmatchMappingType;
}

public List<String> getMatch() {
return match;
}

public List<String> getPathMatch() {
return pathMatch;
}

public List<String> getUnmatch() {
return unmatch;
}

public List<String> getPathUnmatch() {
return pathUnmatch;
}

public List<String> getMatchMappingType() {
return matchMappingType;
}

public List<String> getUnmatchMappingType() {
return unmatchMappingType;
}

public boolean isRegexMatching() {
return false;
}

public static Builder builder() {
return new Builder();
}

public static class Builder {
private final List<String> match = new ArrayList<>();
private final List<String> pathMatch = new ArrayList<>();

private final List<String> unmatch = new ArrayList<>();
private final List<String> pathUnmatch = new ArrayList<>();

private final List<String> matchMappingType = new ArrayList<>();
private final List<String> unmatchMappingType = new ArrayList<>();

private Builder() {}

/**
* Patterns to match on the field name.
*/
public Builder withMatch(String... match) {
for (String value : match) {
if (value != null) {
parseValues(value, this.match);
}
}

return this;
}

/**
* Path patterns for a nested type to match the field name.
*/
public Builder withPathMatch(String... pathMatch) {
for (String value : pathMatch) {
if (value != null) {
parseValues(value, this.pathMatch);
}
}

return this;
}

/**
* Patterns that do not match the field name.
*/
public Builder withUnmatch(String... unmatch) {
for (String value : unmatch) {
if (value != null) {
parseValues(value, this.unmatch);
}
}

return this;
}

/**
* Path patterns for a nested type that do not match the field name.
*/
public Builder withPathUnmatch(String... pathUnmatch) {
for (String value : pathUnmatch) {
if (value != null) {
parseValues(value, this.pathUnmatch);
}
}

return this;
}

/**
* Data types that correspond to the field.
*/
public Builder withMatchMappingType(String... matchMappingType) {
for (String value : matchMappingType) {
if (value != null) {
parseValues(value, this.matchMappingType);
}
}

return this;
}

/**
* Data types that do not match to the field.
*/
public Builder withUnmatchMappingType(String... unmatchMappingType) {
for (String value : unmatchMappingType) {
if (value != null) {
parseValues(value, this.unmatchMappingType);
}
}

return this;
}

private void parseValues(String source, List<String> target) {
if (source.startsWith("[")) {
target.addAll(List.of(source.replace("[", "").replace("]", "").split(",", -1)));
} else {
target.add(source);
}
}

public DynamicTemplate build() {
return new DynamicTemplate(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package org.springframework.data.elasticsearch.core.mapping;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.data.elasticsearch.annotations.Document;
Expand Down Expand Up @@ -203,4 +205,25 @@ default ElasticsearchPersistentProperty getRequiredSeqNoPrimaryTermProperty() {
* @since 5.2
*/
boolean isAlwaysWriteMapping();

/**
* Retrieves the dynamic templates defined for the current document.
*
* @since 5.4
*/
Map<String, DynamicTemplate> getDynamicTemplates();

/**
* if the mapping should be written to the index on repository bootstrap even if the index already exists.
*
* @since 5.4
*/
boolean hasDynamicTemplates();

/**
* Building the dynamic templates for the current document.
*
* @since 5.4
*/
void buildDynamicTemplates(List<?> dynamicTemplates);
}
Loading

0 comments on commit 5a582ac

Please sign in to comment.