DATAES-535 - Add mapping annotation @DynamicTemplates.

Original pull request: #238
This commit is contained in:
Petr Kukrál 2019-03-12 21:07:31 +01:00 committed by xhaggi
parent ab7458d7d7
commit 73bd06340e
7 changed files with 194 additions and 0 deletions

View File

@ -0,0 +1,27 @@
package org.springframework.data.elasticsearch.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.annotation.Persistent;
/**
* Elasticsearch dynamic templates mapping.
* This annotation is handy if you prefer apply dynamic templates on fields with annotation e.g. {@link Field}
* with type = FieldType.Object etc. instead of static mapping on Document via {@link Mapping} annotation.
* DynamicTemplates annotation is ommited if {@link Mapping} annotation is used.
*
* @author Petr Kukral
*/
@Persistent
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface DynamicTemplates {
String mappingPath() default "";
}

View File

@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.core;
import static org.elasticsearch.common.xcontent.XContentFactory.*;
import static org.springframework.util.StringUtils.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@ -32,6 +33,7 @@ import org.springframework.data.annotation.Transient;
import org.springframework.data.elasticsearch.annotations.CompletionContext;
import org.springframework.data.elasticsearch.annotations.CompletionField;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.DynamicTemplates;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.GeoPointField;
@ -45,6 +47,9 @@ import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.StringUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
@ -57,6 +62,7 @@ import org.springframework.util.StringUtils;
* @author Sascha Woo
* @author Nordine Bittich
* @author Robert Gruendler
* @author Petr Kukral
*/
class MappingBuilder {
@ -74,6 +80,7 @@ class MappingBuilder {
public static final String FIELD_CONTEXT_NAME = "name";
public static final String FIELD_CONTEXT_TYPE = "type";
public static final String FIELD_CONTEXT_PRECISION = "precision";
public static final String FIELD_DYNAMIC_TEMPLATES = "dynamic_templates";
public static final String COMPLETION_PRESERVE_SEPARATORS = "preserve_separators";
public static final String COMPLETION_PRESERVE_POSITION_INCREMENTS = "preserve_position_increments";
@ -91,6 +98,10 @@ class MappingBuilder {
static XContentBuilder buildMapping(Class<?> clazz, String indexType, String idFieldName, String parentType) throws IOException {
XContentBuilder mapping = jsonBuilder().startObject().startObject(indexType);
// Dynamic templates
addDynamicTemplatesMapping(mapping, clazz);
// Parent
if (hasText(parentType)) {
mapping.startObject(FIELD_PARENT).field(FIELD_TYPE, parentType).endObject();
@ -355,6 +366,28 @@ class MappingBuilder {
}
}
/**
* Apply mapping for dynamic templates.
*
* @throws IOException
*/
private static void addDynamicTemplatesMapping(XContentBuilder builder, Class<?> clazz) throws IOException {
if (clazz.isAnnotationPresent(DynamicTemplates.class)){
String mappingPath = ((DynamicTemplates) clazz.getAnnotation(DynamicTemplates.class)).mappingPath();
if (hasText(mappingPath)) {
String jsonString = ElasticsearchTemplate.readFileFromClasspath(mappingPath);
if (hasText(jsonString)) {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(jsonString).get("dynamic_templates");
if (jsonNode != null && jsonNode.isArray()){
String json = objectMapper.writeValueAsString(jsonNode);
builder.rawField(FIELD_DYNAMIC_TEMPLATES, new ByteArrayInputStream(json.getBytes()), XContentType.JSON);
}
}
}
}
}
protected static boolean isEntity(java.lang.reflect.Field field) {
TypeInformation<?> typeInformation = ClassTypeInformation.from(field.getType());
Class<?> clazz = getFieldType(field);

View File

@ -0,0 +1,49 @@
package org.springframework.data.elasticsearch.core;
import java.io.IOException;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.elasticsearch.entities.SampleDynamicTemplatesEntity;
import org.springframework.data.elasticsearch.entities.SampleDynamicTemplatesEntityTwo;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Dynamic templates tests
*
* @author Petr Kukral
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
public class SimpleDynamicTemplatesMappingTests {
@Test
public void testCorrectDynamicTemplatesMappings() throws IOException {
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(SampleDynamicTemplatesEntity.class,
"test-dynamictemplatestype", "id", null);
String EXPECTED_MAPPING_ONE = "{\"test-dynamictemplatestype\":{\"dynamic_templates\":" +
"[{\"with_custom_analyzer\":{" +
"\"mapping\":{\"type\":\"string\",\"analyzer\":\"standard_lowercase_asciifolding\"}," +
"\"path_match\":\"names.*\"}}]," +
"\"properties\":{\"names\":{\"type\":\"object\"}}}}";
Assert.assertEquals(EXPECTED_MAPPING_ONE, xContentBuilder.string());
}
@Test
public void testCorrectDynamicTemplatesMappingsTwo() throws IOException {
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(SampleDynamicTemplatesEntityTwo.class,
"test-dynamictemplatestype", "id", null);
String EXPECTED_MAPPING_TWO = "{\"test-dynamictemplatestype\":{\"dynamic_templates\":" +
"[{\"with_custom_analyzer\":{" +
"\"mapping\":{\"type\":\"string\",\"analyzer\":\"standard_lowercase_asciifolding\"}," +
"\"path_match\":\"names.*\"}}," +
"{\"participantA1_with_custom_analyzer\":{" +
"\"mapping\":{\"type\":\"string\",\"analyzer\":\"standard_lowercase_asciifolding\"}," +
"\"path_match\":\"participantA1.*\"}}]," +
"\"properties\":{\"names\":{\"type\":\"object\"}}}}";
Assert.assertEquals(EXPECTED_MAPPING_TWO, xContentBuilder.string());
}
}

View File

@ -0,0 +1,25 @@
package org.springframework.data.elasticsearch.entities;
import java.util.HashMap;
import java.util.Map;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.DynamicTemplates;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
/**
* @author Petr Kukral
*/
@Document(indexName = "test-dynamictemplates", type = "test-dynamictemplatestype", indexStoreType = "memory", shards = 1,
replicas = 0, refreshInterval = "-1")
@DynamicTemplates(mappingPath = "/mappings/test-dynamic_templates_mappings.json")
public class SampleDynamicTemplatesEntity {
@Id
private String id;
@Field(type = FieldType.Object)
private Map<String, String> names = new HashMap<String, String>();
}

View File

@ -0,0 +1,25 @@
package org.springframework.data.elasticsearch.entities;
import java.util.HashMap;
import java.util.Map;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.DynamicTemplates;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
/**
* @author Petr Kukral
*/
@Document(indexName = "test-dynamictemplates", type = "test-dynamictemplatestype", indexStoreType = "memory", shards = 1,
replicas = 0, refreshInterval = "-1")
@DynamicTemplates(mappingPath = "/mappings/test-dynamic_templates_mappings_two.json")
public class SampleDynamicTemplatesEntityTwo {
@Id
private String id;
@Field(type = FieldType.Object)
private Map<String, String> names = new HashMap<String, String>();
}

View File

@ -0,0 +1,13 @@
{
"dynamic_templates": [
{
"with_custom_analyzer": {
"mapping": {
"type": "string",
"analyzer": "standard_lowercase_asciifolding"
},
"path_match": "names.*"
}
}
]
}

View File

@ -0,0 +1,22 @@
{
"dynamic_templates": [
{
"with_custom_analyzer": {
"mapping": {
"type": "string",
"analyzer": "standard_lowercase_asciifolding"
},
"path_match": "names.*"
}
},
{
"participantA1_with_custom_analyzer": {
"mapping": {
"type": "string",
"analyzer": "standard_lowercase_asciifolding"
},
"path_match": "participantA1.*"
}
}
]
}