DATAES-568 - MappingBuilder must use the @Field annotation's name attribute.

Original pull request: #281.
This commit is contained in:
P.J. Meisch 2019-04-25 21:50:39 +02:00 committed by Mark Paluch
parent e5c514e385
commit 66b77ecb75
13 changed files with 559 additions and 285 deletions

View File

@ -227,6 +227,13 @@
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<version>1.5.0</version>
<scope>test</scope>
</dependency>
<!-- Upgrade xbean to 4.5 to prevent incompatibilities due to ASM versions --> <!-- Upgrade xbean to 4.5 to prevent incompatibilities due to ASM versions -->
<dependency> <dependency>
<groupId>org.apache.xbean</groupId> <groupId>org.apache.xbean</groupId>

View File

@ -17,7 +17,6 @@ package org.springframework.data.elasticsearch.core;
import static org.elasticsearch.client.Requests.*; import static org.elasticsearch.client.Requests.*;
import static org.elasticsearch.index.query.QueryBuilders.*; import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.springframework.data.elasticsearch.core.MappingBuilder.*;
import static org.springframework.util.CollectionUtils.isEmpty; import static org.springframework.util.CollectionUtils.isEmpty;
import static org.springframework.util.StringUtils.*; import static org.springframework.util.StringUtils.*;
@ -223,18 +222,12 @@ public class ElasticsearchRestTemplate
logger.info("mappingPath in @Mapping has to be defined. Building mappings using @Field"); logger.info("mappingPath in @Mapping has to be defined. Building mappings using @Field");
} }
} }
ElasticsearchPersistentEntity<T> persistentEntity = getPersistentEntityFor(clazz);
XContentBuilder xContentBuilder = null;
try { try {
MappingBuilder mappingBuilder = new MappingBuilder(elasticsearchConverter);
ElasticsearchPersistentProperty property = persistentEntity.getRequiredIdProperty(); return putMapping(clazz, mappingBuilder.buildMapping(clazz));
xContentBuilder = buildMapping(clazz, persistentEntity.getIndexType(), property.getFieldName(),
persistentEntity.getParentType());
} catch (Exception e) { } catch (Exception e) {
throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e); throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e);
} }
return putMapping(clazz, xContentBuilder);
} }
@Override @Override

View File

@ -17,7 +17,6 @@ package org.springframework.data.elasticsearch.core;
import static org.elasticsearch.client.Requests.*; import static org.elasticsearch.client.Requests.*;
import static org.elasticsearch.index.query.QueryBuilders.*; import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.springframework.data.elasticsearch.core.MappingBuilder.*;
import static org.springframework.util.CollectionUtils.*; import static org.springframework.util.CollectionUtils.*;
import java.io.BufferedReader; import java.io.BufferedReader;
@ -205,18 +204,12 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
LOGGER.info("mappingPath in @Mapping has to be defined. Building mappings using @Field"); LOGGER.info("mappingPath in @Mapping has to be defined. Building mappings using @Field");
} }
} }
ElasticsearchPersistentEntity<T> persistentEntity = getPersistentEntityFor(clazz);
XContentBuilder xContentBuilder = null;
try { try {
MappingBuilder mappingBuilder = new MappingBuilder(elasticsearchConverter);
ElasticsearchPersistentProperty property = persistentEntity.getRequiredIdProperty(); return putMapping(clazz, mappingBuilder.buildMapping(clazz));
xContentBuilder = buildMapping(clazz, persistentEntity.getIndexType(), property.getFieldName(),
persistentEntity.getParentType());
} catch (Exception e) { } catch (Exception e) {
throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e); throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e);
} }
return putMapping(clazz, xContentBuilder);
} }
@Override @Override

View File

@ -20,31 +20,25 @@ import static org.springframework.util.StringUtils.*;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.Iterator;
import java.util.Map;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.springframework.core.ResolvableType; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.data.annotation.Transient; import org.springframework.data.annotation.Transient;
import org.springframework.data.elasticsearch.annotations.CompletionContext; import org.springframework.data.elasticsearch.annotations.*;
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;
import org.springframework.data.elasticsearch.annotations.InnerField;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.annotations.MultiField;
import org.springframework.data.elasticsearch.core.completion.Completion; import org.springframework.data.elasticsearch.core.completion.Completion;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.geo.GeoPoint; import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation; import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
@ -63,6 +57,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* @author Nordine Bittich * @author Nordine Bittich
* @author Robert Gruendler * @author Robert Gruendler
* @author Petr Kukral * @author Petr Kukral
* @author Peter-Josef Meisch
*/ */
class MappingBuilder { class MappingBuilder {
@ -92,169 +87,199 @@ class MappingBuilder {
public static final String TYPE_VALUE_COMPLETION = "completion"; public static final String TYPE_VALUE_COMPLETION = "completion";
public static final String TYPE_VALUE_GEO_HASH_PREFIX = "geohash_prefix"; public static final String TYPE_VALUE_GEO_HASH_PREFIX = "geohash_prefix";
public static final String TYPE_VALUE_GEO_HASH_PRECISION = "geohash_precision"; public static final String TYPE_VALUE_GEO_HASH_PRECISION = "geohash_precision";
private static final Logger logger = LoggerFactory.getLogger(ElasticsearchRestTemplate.class);
private static SimpleTypeHolder SIMPLE_TYPE_HOLDER = SimpleTypeHolder.DEFAULT; private static SimpleTypeHolder SIMPLE_TYPE_HOLDER = SimpleTypeHolder.DEFAULT;
private final ElasticsearchConverter elasticsearchConverter;
static XContentBuilder buildMapping(Class<?> clazz, String indexType, String idFieldName, String parentType) throws IOException { MappingBuilder(ElasticsearchConverter elasticsearchConverter) {
this.elasticsearchConverter = elasticsearchConverter;
}
XContentBuilder mapping = jsonBuilder().startObject().startObject(indexType); /**
* builds the Elasticsearch mapping for the given clazz.
*
* @return JSON string
* @throws IOException
*/
String buildMapping(Class<?> clazz) throws IOException {
ElasticsearchPersistentEntity<?> entity = elasticsearchConverter.getMappingContext()
.getRequiredPersistentEntity(clazz);
XContentBuilder builder = jsonBuilder().startObject().startObject(entity.getIndexType());
// Dynamic templates // Dynamic templates
addDynamicTemplatesMapping(mapping, clazz); addDynamicTemplatesMapping(builder, entity);
// Parent // Parent
String parentType = entity.getParentType();
if (hasText(parentType)) { if (hasText(parentType)) {
mapping.startObject(FIELD_PARENT).field(FIELD_TYPE, parentType).endObject(); builder.startObject(FIELD_PARENT).field(FIELD_TYPE, parentType).endObject();
} }
// Properties // Properties
XContentBuilder xContentBuilder = mapping.startObject(FIELD_PROPERTIES); builder.startObject(FIELD_PROPERTIES);
mapEntity(xContentBuilder, clazz, true, idFieldName, "", false, FieldType.Auto, null); mapEntity(builder, entity, true, "", false, FieldType.Auto, null);
return xContentBuilder.endObject().endObject().endObject(); builder.endObject() // FIELD_PROPERTIES
.endObject() // indexType
.endObject() // root object
.close();
return builder.getOutputStream().toString();
} }
private static void mapEntity(XContentBuilder xContentBuilder, Class<?> clazz, boolean isRootObject, String idFieldName, private void mapEntity(XContentBuilder builder, @Nullable ElasticsearchPersistentEntity entity, boolean isRootObject,
String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType, Field fieldAnnotation) throws IOException { String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType,
@Nullable Field parentFieldAnnotation) throws IOException {
java.lang.reflect.Field[] fields = retrieveFields(clazz); boolean writeNestedProperties = !isRootObject && (isAnyPropertyAnnotatedWithField(entity) || nestedOrObjectField);
if (writeNestedProperties) {
if (!isRootObject && (isAnyPropertyAnnotatedAsField(fields) || nestedOrObjectField)) { String type = nestedOrObjectField ? fieldType.toString().toLowerCase()
String type = FieldType.Object.toString().toLowerCase(); : FieldType.Object.toString().toLowerCase();
if (nestedOrObjectField) { builder.startObject(nestedObjectFieldName).field(FIELD_TYPE, type);
type = fieldType.toString().toLowerCase();
if (nestedOrObjectField && FieldType.Nested == fieldType && parentFieldAnnotation != null
&& parentFieldAnnotation.includeInParent()) {
builder.field("include_in_parent", parentFieldAnnotation.includeInParent());
} }
XContentBuilder t = xContentBuilder.startObject(nestedObjectFieldName).field(FIELD_TYPE, type);
if (nestedOrObjectField && FieldType.Nested == fieldType && fieldAnnotation.includeInParent()) { builder.startObject(FIELD_PROPERTIES);
t.field("include_in_parent", fieldAnnotation.includeInParent());
}
t.startObject(FIELD_PROPERTIES);
} }
if (entity != null) {
for (java.lang.reflect.Field field : fields) { entity.doWithProperties((PropertyHandler<ElasticsearchPersistentProperty>) property -> {
try {
if (field.isAnnotationPresent(Transient.class) || isInIgnoreFields(field, fieldAnnotation)) { if (property.isAnnotationPresent(Transient.class) || isInIgnoreFields(property, parentFieldAnnotation)) {
continue; return;
}
if (field.isAnnotationPresent(Mapping.class)) {
String mappingPath = field.getAnnotation(Mapping.class).mappingPath();
if (!StringUtils.isEmpty(mappingPath)) {
ClassPathResource mappings = new ClassPathResource(mappingPath);
if (mappings.exists()) {
xContentBuilder.rawField(field.getName(), mappings.getInputStream(), XContentType.JSON);
continue;
} }
if (property.isAnnotationPresent(Mapping.class)) {
String mappingPath = property.getRequiredAnnotation(Mapping.class).mappingPath();
if (!StringUtils.isEmpty(mappingPath)) {
ClassPathResource mappings = new ClassPathResource(mappingPath);
if (mappings.exists()) {
builder.rawField(property.getFieldName(), mappings.getInputStream(), XContentType.JSON);
return;
}
}
}
boolean isGeoPointProperty = isGeoPointProperty(property);
boolean isCompletionProperty = isCompletionProperty(property);
boolean isNestedOrObjectProperty = isNestedOrObjectProperty(property);
Field fieldAnnotation = property.findAnnotation(Field.class);
if (!isGeoPointProperty && !isCompletionProperty && property.isEntity() && hasRelevantAnnotation(property)) {
if (fieldAnnotation == null) {
return;
}
Iterator<? extends TypeInformation<?>> iterator = property.getPersistentEntityTypes().iterator();
ElasticsearchPersistentEntity<?> persistentEntity = iterator.hasNext()
? elasticsearchConverter.getMappingContext().getPersistentEntity(iterator.next())
: null;
mapEntity(builder, persistentEntity, false, property.getFieldName(), isNestedOrObjectProperty,
fieldAnnotation.type(), fieldAnnotation);
if (isNestedOrObjectProperty) {
return;
}
}
MultiField multiField = property.findAnnotation(MultiField.class);
if (isGeoPointProperty) {
applyGeoPointFieldMapping(builder, property);
return;
}
if (isCompletionProperty) {
CompletionField completionField = property.findAnnotation(CompletionField.class);
applyCompletionFieldMapping(builder, property, completionField);
}
if (isRootObject && fieldAnnotation != null && property.isIdProperty()) {
applyDefaultIdFieldMapping(builder, property);
} else if (multiField != null) {
addMultiFieldMapping(builder, property, multiField, isNestedOrObjectProperty);
} else if (fieldAnnotation != null) {
addSingleFieldMapping(builder, property, fieldAnnotation, isNestedOrObjectProperty);
}
} catch (IOException e) {
logger.warn("error mapping property with name {}", property.getName(), e);
} }
} });
boolean isGeoPointField = isGeoPointField(field);
boolean isCompletionField = isCompletionField(field);
Field singleField = field.getAnnotation(Field.class);
if (!isGeoPointField && !isCompletionField && isEntity(field) && isAnnotated(field)) {
if (singleField == null) {
continue;
}
boolean nestedOrObject = isNestedOrObjectField(field);
mapEntity(xContentBuilder, getFieldType(field), false, "", field.getName(), nestedOrObject, singleField.type(), field.getAnnotation(Field.class));
if (nestedOrObject) {
continue;
}
}
MultiField multiField = field.getAnnotation(MultiField.class);
if (isGeoPointField) {
applyGeoPointFieldMapping(xContentBuilder, field);
}
if (isCompletionField) {
CompletionField completionField = field.getAnnotation(CompletionField.class);
applyCompletionFieldMapping(xContentBuilder, field, completionField);
}
if (isRootObject && singleField != null && isIdField(field, idFieldName)) {
applyDefaultIdFieldMapping(xContentBuilder, field);
} else if (multiField != null) {
addMultiFieldMapping(xContentBuilder, field, multiField, isNestedOrObjectField(field));
} else if (singleField != null) {
addSingleFieldMapping(xContentBuilder, field, singleField, isNestedOrObjectField(field));
}
} }
if (!isRootObject && isAnyPropertyAnnotatedAsField(fields) || nestedOrObjectField) { if (writeNestedProperties) {
xContentBuilder.endObject().endObject(); builder.endObject().endObject();
} }
} }
private static java.lang.reflect.Field[] retrieveFields(Class<?> clazz) { private boolean hasRelevantAnnotation(ElasticsearchPersistentProperty property) {
// Create list of fields.
List<java.lang.reflect.Field> fields = new ArrayList<>();
// Keep backing up the inheritance hierarchy. return property.findAnnotation(Field.class) != null || property.findAnnotation(MultiField.class) != null
Class<?> targetClass = clazz; || property.findAnnotation(GeoPointField.class) != null
do { || property.findAnnotation(CompletionField.class) != null;
fields.addAll(Arrays.asList(targetClass.getDeclaredFields()));
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return fields.toArray(new java.lang.reflect.Field[fields.size()]);
} }
private static boolean isAnnotated(java.lang.reflect.Field field) { private void applyGeoPointFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property)
return field.getAnnotation(Field.class) != null || throws IOException {
field.getAnnotation(MultiField.class) != null ||
field.getAnnotation(GeoPointField.class) != null || builder.startObject(property.getFieldName()).field(FIELD_TYPE, TYPE_VALUE_GEO_POINT).endObject();
field.getAnnotation(CompletionField.class) != null;
} }
private static void applyGeoPointFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field) throws IOException { private void applyCompletionFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property,
xContentBuilder.startObject(field.getName()); @Nullable CompletionField annotation) throws IOException {
xContentBuilder.field(FIELD_TYPE, TYPE_VALUE_GEO_POINT);
xContentBuilder.endObject(); builder.startObject(property.getFieldName());
} builder.field(FIELD_TYPE, TYPE_VALUE_COMPLETION);
private static void applyCompletionFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field, CompletionField annotation) throws IOException {
xContentBuilder.startObject(field.getName());
xContentBuilder.field(FIELD_TYPE, TYPE_VALUE_COMPLETION);
if (annotation != null) { if (annotation != null) {
xContentBuilder.field(COMPLETION_MAX_INPUT_LENGTH, annotation.maxInputLength());
xContentBuilder.field(COMPLETION_PRESERVE_POSITION_INCREMENTS, annotation.preservePositionIncrements()); builder.field(COMPLETION_MAX_INPUT_LENGTH, annotation.maxInputLength());
xContentBuilder.field(COMPLETION_PRESERVE_SEPARATORS, annotation.preserveSeparators()); builder.field(COMPLETION_PRESERVE_POSITION_INCREMENTS, annotation.preservePositionIncrements());
builder.field(COMPLETION_PRESERVE_SEPARATORS, annotation.preserveSeparators());
if (!StringUtils.isEmpty(annotation.searchAnalyzer())) { if (!StringUtils.isEmpty(annotation.searchAnalyzer())) {
xContentBuilder.field(FIELD_SEARCH_ANALYZER, annotation.searchAnalyzer()); builder.field(FIELD_SEARCH_ANALYZER, annotation.searchAnalyzer());
} }
if (!StringUtils.isEmpty(annotation.analyzer())) { if (!StringUtils.isEmpty(annotation.analyzer())) {
xContentBuilder.field(FIELD_INDEX_ANALYZER, annotation.analyzer()); builder.field(FIELD_INDEX_ANALYZER, annotation.analyzer());
} }
if (annotation.contexts().length > 0) { if (annotation.contexts().length > 0) {
xContentBuilder.startArray(COMPLETION_CONTEXTS);
builder.startArray(COMPLETION_CONTEXTS);
for (CompletionContext context : annotation.contexts()) { for (CompletionContext context : annotation.contexts()) {
xContentBuilder.startObject();
xContentBuilder.field(FIELD_CONTEXT_NAME, context.name()); builder.startObject();
xContentBuilder.field(FIELD_CONTEXT_TYPE, context.type().name().toLowerCase()); builder.field(FIELD_CONTEXT_NAME, context.name());
builder.field(FIELD_CONTEXT_TYPE, context.type().name().toLowerCase());
if (context.precision().length() > 0) { if (context.precision().length() > 0) {
xContentBuilder.field(FIELD_CONTEXT_PRECISION, context.precision()); builder.field(FIELD_CONTEXT_PRECISION, context.precision());
} }
xContentBuilder.endObject(); builder.endObject();
} }
xContentBuilder.endArray(); builder.endArray();
} }
} }
xContentBuilder.endObject(); builder.endObject();
} }
private static void applyDefaultIdFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field) private void applyDefaultIdFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property)
throws IOException { throws IOException {
xContentBuilder.startObject(field.getName())
.field(FIELD_TYPE, TYPE_VALUE_KEYWORD) builder.startObject(property.getFieldName()).field(FIELD_TYPE, TYPE_VALUE_KEYWORD).field(FIELD_INDEX, true)
.field(FIELD_INDEX, true); .endObject();
xContentBuilder.endObject();
} }
/** /**
@ -262,8 +287,10 @@ class MappingBuilder {
* *
* @throws IOException * @throws IOException
*/ */
private static void addSingleFieldMapping(XContentBuilder builder, java.lang.reflect.Field field, Field annotation, boolean nestedOrObjectField) throws IOException { private void addSingleFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property,
builder.startObject(field.getName()); Field annotation, boolean nestedOrObjectField) throws IOException {
builder.startObject(property.getFieldName());
addFieldMappingParameters(builder, annotation, nestedOrObjectField); addFieldMappingParameters(builder, annotation, nestedOrObjectField);
builder.endObject(); builder.endObject();
} }
@ -273,14 +300,11 @@ class MappingBuilder {
* *
* @throws IOException * @throws IOException
*/ */
private static void addMultiFieldMapping( private void addMultiFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property,
XContentBuilder builder, MultiField annotation, boolean nestedOrObjectField) throws IOException {
java.lang.reflect.Field field,
MultiField annotation,
boolean nestedOrObjectField) throws IOException {
// main field // main field
builder.startObject(field.getName()); builder.startObject(property.getFieldName());
addFieldMappingParameters(builder, annotation.mainField(), nestedOrObjectField); addFieldMappingParameters(builder, annotation.mainField(), nestedOrObjectField);
// inner fields // inner fields
@ -295,7 +319,8 @@ class MappingBuilder {
builder.endObject(); builder.endObject();
} }
private static void addFieldMappingParameters(XContentBuilder builder, Object annotation, boolean nestedOrObjectField) throws IOException { private void addFieldMappingParameters(XContentBuilder builder, Object annotation, boolean nestedOrObjectField)
throws IOException {
boolean index = true; boolean index = true;
boolean store = false; boolean store = false;
boolean fielddata = false; boolean fielddata = false;
@ -371,15 +396,19 @@ class MappingBuilder {
* *
* @throws IOException * @throws IOException
*/ */
private static void addDynamicTemplatesMapping(XContentBuilder builder, Class<?> clazz) throws IOException { private void addDynamicTemplatesMapping(XContentBuilder builder, ElasticsearchPersistentEntity<?> entity)
if (clazz.isAnnotationPresent(DynamicTemplates.class)){ throws IOException {
String mappingPath = ((DynamicTemplates) clazz.getAnnotation(DynamicTemplates.class)).mappingPath();
if (entity.isAnnotationPresent(DynamicTemplates.class)) {
String mappingPath = entity.getRequiredAnnotation(DynamicTemplates.class).mappingPath();
if (hasText(mappingPath)) { if (hasText(mappingPath)) {
String jsonString = ElasticsearchTemplate.readFileFromClasspath(mappingPath); String jsonString = ElasticsearchTemplate.readFileFromClasspath(mappingPath);
if (hasText(jsonString)) { if (hasText(jsonString)) {
ObjectMapper objectMapper = new ObjectMapper(); ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(jsonString).get("dynamic_templates"); JsonNode jsonNode = objectMapper.readTree(jsonString).get("dynamic_templates");
if (jsonNode != null && jsonNode.isArray()){ if (jsonNode != null && jsonNode.isArray()) {
String json = objectMapper.writeValueAsString(jsonNode); String json = objectMapper.writeValueAsString(jsonNode);
builder.rawField(FIELD_DYNAMIC_TEMPLATES, new ByteArrayInputStream(json.getBytes()), XContentType.JSON); builder.rawField(FIELD_DYNAMIC_TEMPLATES, new ByteArrayInputStream(json.getBytes()), XContentType.JSON);
} }
@ -388,63 +417,33 @@ class MappingBuilder {
} }
} }
protected static boolean isEntity(java.lang.reflect.Field field) { private boolean isAnyPropertyAnnotatedWithField(@Nullable ElasticsearchPersistentEntity entity) {
TypeInformation<?> typeInformation = ClassTypeInformation.from(field.getType());
Class<?> clazz = getFieldType(field); return entity != null && entity.getPersistentProperty(Field.class) != null;
boolean isComplexType = !SIMPLE_TYPE_HOLDER.isSimpleType(clazz);
return isComplexType && !Map.class.isAssignableFrom(typeInformation.getType());
} }
protected static Class<?> getFieldType(java.lang.reflect.Field field) { private boolean isInIgnoreFields(ElasticsearchPersistentProperty property, @Nullable Field parentFieldAnnotation) {
ResolvableType resolvableType = ResolvableType.forField(field);
if (resolvableType.isArray()) {
return resolvableType.getComponentType().getRawClass();
}
ResolvableType componentType = resolvableType.getGeneric(0);
if (Iterable.class.isAssignableFrom(field.getType())
&& componentType != ResolvableType.NONE) {
return componentType.getRawClass();
}
return resolvableType.getRawClass();
}
private static boolean isAnyPropertyAnnotatedAsField(java.lang.reflect.Field[] fields) {
if (fields != null) {
for (java.lang.reflect.Field field : fields) {
if (field.isAnnotationPresent(Field.class)) {
return true;
}
}
}
return false;
}
private static boolean isIdField(java.lang.reflect.Field field, String idFieldName) {
return idFieldName.equals(field.getName());
}
private static boolean isInIgnoreFields(java.lang.reflect.Field field, Field parentFieldAnnotation) {
if (null != parentFieldAnnotation) { if (null != parentFieldAnnotation) {
String[] ignoreFields = parentFieldAnnotation.ignoreFields(); String[] ignoreFields = parentFieldAnnotation.ignoreFields();
return Arrays.asList(ignoreFields).contains(field.getName()); return Arrays.asList(ignoreFields).contains(property.getFieldName());
} }
return false; return false;
} }
private static boolean isNestedOrObjectField(java.lang.reflect.Field field) { private boolean isNestedOrObjectProperty(ElasticsearchPersistentProperty property) {
Field fieldAnnotation = field.getAnnotation(Field.class);
return fieldAnnotation != null && (FieldType.Nested == fieldAnnotation.type() || FieldType.Object == fieldAnnotation.type()); Field fieldAnnotation = property.findAnnotation(Field.class);
return fieldAnnotation != null
&& (FieldType.Nested == fieldAnnotation.type() || FieldType.Object == fieldAnnotation.type());
} }
private static boolean isGeoPointField(java.lang.reflect.Field field) { private boolean isGeoPointProperty(ElasticsearchPersistentProperty property) {
return field.getType() == GeoPoint.class || field.getAnnotation(GeoPointField.class) != null; return property.getActualType() == GeoPoint.class || property.isAnnotationPresent(GeoPointField.class);
} }
private static boolean isCompletionField(java.lang.reflect.Field field) { private boolean isCompletionProperty(ElasticsearchPersistentProperty property) {
return field.getType() == Completion.class; return property.getActualType() == Completion.class;
} }
} }

View File

@ -50,8 +50,8 @@ public class ElasticsearchRestTemplateTests extends ElasticsearchTemplateTests {
// when // when
IndexRequest indexRequest = new IndexRequest(); IndexRequest indexRequest = new IndexRequest();
indexRequest.source("{}", XContentType.JSON); indexRequest.source("{}", XContentType.JSON);
UpdateQuery updateQuery = new UpdateQueryBuilder().withId(randomNumeric(5)) UpdateQuery updateQuery = new UpdateQueryBuilder().withId(randomNumeric(5)).withClass(SampleEntity.class)
.withClass(SampleEntity.class).withIndexRequest(indexRequest).build(); .withIndexRequest(indexRequest).build();
elasticsearchTemplate.update(updateQuery); elasticsearchTemplate.update(updateQuery);
} }
} }

View File

@ -90,8 +90,8 @@ import org.springframework.data.util.CloseableIterator;
* @author Peter Nowak * @author Peter Nowak
* @author Ivan Greene * @author Ivan Greene
* @author Dmitriy Yakovlev * @author Dmitriy Yakovlev
* @author Peter-Josef Meisch
*/ */
@Ignore @Ignore
public class ElasticsearchTemplateTests { public class ElasticsearchTemplateTests {
@ -128,6 +128,8 @@ public class ElasticsearchTemplateTests {
@Before @Before
public void before() { public void before() {
deleteIndices();
elasticsearchTemplate.createIndex(SampleEntity.class); elasticsearchTemplate.createIndex(SampleEntity.class);
elasticsearchTemplate.putMapping(SampleEntity.class); elasticsearchTemplate.putMapping(SampleEntity.class);
@ -137,7 +139,10 @@ public class ElasticsearchTemplateTests {
@After @After
public void after() { public void after() {
deleteIndices();
}
private void deleteIndices() {
elasticsearchTemplate.deleteIndex(SampleEntity.class); elasticsearchTemplate.deleteIndex(SampleEntity.class);
elasticsearchTemplate.deleteIndex(SampleEntityUUIDKeyed.class); elasticsearchTemplate.deleteIndex(SampleEntityUUIDKeyed.class);
elasticsearchTemplate.deleteIndex(UseServerConfigurationEntity.class); elasticsearchTemplate.deleteIndex(UseServerConfigurationEntity.class);

View File

@ -21,7 +21,6 @@ import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.springframework.data.elasticsearch.utils.IndexBuilder.*; import static org.springframework.data.elasticsearch.utils.IndexBuilder.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Arrays; import java.util.Arrays;
@ -29,25 +28,15 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.json.JSONException;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.builder.SampleInheritedEntityBuilder; import org.springframework.data.elasticsearch.builder.SampleInheritedEntityBuilder;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery; import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.data.elasticsearch.entities.Book; import org.springframework.data.elasticsearch.entities.*;
import org.springframework.data.elasticsearch.entities.CopyToEntity;
import org.springframework.data.elasticsearch.entities.GeoEntity;
import org.springframework.data.elasticsearch.entities.Group;
import org.springframework.data.elasticsearch.entities.MinimalEntity;
import org.springframework.data.elasticsearch.entities.NormalizerEntity;
import org.springframework.data.elasticsearch.entities.SampleInheritedEntity;
import org.springframework.data.elasticsearch.entities.SampleTransientEntity;
import org.springframework.data.elasticsearch.entities.SimpleRecursiveEntity;
import org.springframework.data.elasticsearch.entities.StockPrice;
import org.springframework.data.elasticsearch.entities.User;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ -59,19 +48,14 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
* @author Nordine Bittich * @author Nordine Bittich
* @author Don Wellington * @author Don Wellington
* @author Sascha Woo * @author Sascha Woo
* @author Peter-Josef Meisch
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:elasticsearch-template-test.xml") @ContextConfiguration("classpath:elasticsearch-template-test.xml")
public class MappingBuilderTests { public class MappingBuilderTests extends MappingContextBaseTests {
@Autowired private ElasticsearchTemplate elasticsearchTemplate; @Autowired private ElasticsearchTemplate elasticsearchTemplate;
private String xContentBuilderToString(XContentBuilder builder) {
builder.close();
ByteArrayOutputStream bos = (ByteArrayOutputStream) builder.getOutputStream();
return bos.toString();
}
@Test @Test
public void shouldNotFailOnCircularReference() { public void shouldNotFailOnCircularReference() {
elasticsearchTemplate.deleteIndex(SimpleRecursiveEntity.class); elasticsearchTemplate.deleteIndex(SimpleRecursiveEntity.class);
@ -81,24 +65,25 @@ public class MappingBuilderTests {
} }
@Test @Test
public void testInfiniteLoopAvoidance() throws IOException { public void testInfiniteLoopAvoidance() throws IOException, JSONException {
final String expected = "{\"mapping\":{\"properties\":{\"message\":{\"store\":true,\"" final String expected = "{\"mapping\":{\"properties\":{\"message\":{\"store\":true,\""
+ "type\":\"text\",\"index\":false," + "\"analyzer\":\"standard\"}}}}"; + "type\":\"text\",\"index\":false," + "\"analyzer\":\"standard\"}}}}";
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(SampleTransientEntity.class, "mapping", "id", null); String mapping = getMappingBuilder().buildMapping(SampleTransientEntity.class);
assertThat(xContentBuilderToString(xContentBuilder), is(expected));
JSONAssert.assertEquals(expected, mapping, false);
} }
@Test @Test
public void shouldUseValueFromAnnotationType() throws IOException { public void shouldUseValueFromAnnotationType() throws IOException, JSONException {
// Given // Given
final String expected = "{\"mapping\":{\"properties\":{\"price\":{\"store\":false,\"type\":\"double\"}}}}"; final String expected = "{\"price\":{\"properties\":{\"price\":{\"store\":false,\"type\":\"double\"}}}}";
// When // When
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(StockPrice.class, "mapping", "id", null); String mapping = getMappingBuilder().buildMapping(StockPrice.class);
// Then // Then
assertThat(xContentBuilderToString(xContentBuilder), is(expected)); JSONAssert.assertEquals(expected, mapping, false);
} }
@Test // DATAES-530 @Test // DATAES-530
@ -127,23 +112,26 @@ public class MappingBuilderTests {
} }
@Test @Test
public void shouldCreateMappingForSpecifiedParentType() throws IOException { public void shouldCreateMappingForSpecifiedParentType() throws IOException, JSONException {
final String expected = "{\"mapping\":{\"_parent\":{\"type\":\"parentType\"},\"properties\":{}}}"; final String expected = "{\"mapping\":{\"_parent\":{\"type\":\"parentType\"},\"properties\":{}}}";
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(MinimalEntity.class, "mapping", "id", "parentType");
assertThat(xContentBuilderToString(xContentBuilder), is(expected)); String mapping = getMappingBuilder().buildMapping(MinimalChildEntity.class);
JSONAssert.assertEquals(expected, mapping, false);
} }
/* /*
* DATAES-76 * DATAES-76
*/ */
@Test @Test
public void shouldBuildMappingWithSuperclass() throws IOException { public void shouldBuildMappingWithSuperclass() throws IOException, JSONException {
final String expected = "{\"mapping\":{\"properties\":{\"message\":{\"store\":true,\"" final String expected = "{\"mapping\":{\"properties\":{\"message\":{\"store\":true,\""
+ "type\":\"text\",\"index\":false,\"analyzer\":\"standard\"}" + ",\"createdDate\":{\"store\":false," + "type\":\"text\",\"index\":false,\"analyzer\":\"standard\"}" + ",\"createdDate\":{\"store\":false,"
+ "\"type\":\"date\",\"index\":false}}}}"; + "\"type\":\"date\",\"index\":false}}}}";
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(SampleInheritedEntity.class, "mapping", "id", null); String mapping = getMappingBuilder().buildMapping(SampleInheritedEntity.class);
assertThat(xContentBuilderToString(xContentBuilder), is(expected));
JSONAssert.assertEquals(expected, mapping, false);
} }
/* /*
@ -174,19 +162,18 @@ public class MappingBuilderTests {
} }
@Test @Test
public void shouldBuildMappingsForGeoPoint() throws IOException { public void shouldBuildMappingsForGeoPoint() throws IOException, JSONException {
// given // given
String expected = "{\"geo-test-index\": {\"properties\": {" + "\"pointA\":{\"type\":\"geo_point\"},"
+ "\"pointB\":{\"type\":\"geo_point\"}," + "\"pointC\":{\"type\":\"geo_point\"},"
+ "\"pointD\":{\"type\":\"geo_point\"}" + "}}}";
// when // when
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(GeoEntity.class, "mapping", "id", null); String mapping;
mapping = getMappingBuilder().buildMapping(GeoEntity.class);
// then // then
final String result = xContentBuilderToString(xContentBuilder); JSONAssert.assertEquals(expected, mapping, false);
assertThat(result, containsString("\"pointA\":{\"type\":\"geo_point\""));
assertThat(result, containsString("\"pointB\":{\"type\":\"geo_point\""));
assertThat(result, containsString("\"pointC\":{\"type\":\"geo_point\""));
assertThat(result, containsString("\"pointD\":{\"type\":\"geo_point\""));
} }
/** /**
@ -276,4 +263,103 @@ public class MappingBuilderTests {
assertThat(fieldFirstName.get("copy_to"), equalTo(copyToValue)); assertThat(fieldFirstName.get("copy_to"), equalTo(copyToValue));
assertThat(fieldLastName.get("copy_to"), equalTo(copyToValue)); assertThat(fieldLastName.get("copy_to"), equalTo(copyToValue));
} }
@Test // DATAES-568
public void shouldUseFieldNameOnId() throws IOException, JSONException {
// given
final String expected = "{\"fieldname-type\":{\"properties\":{"
+ "\"id-property\":{\"type\":\"keyword\",\"index\":true}" + "}}}";
// when
String mapping = getMappingBuilder().buildMapping(FieldNameEntity.IdEntity.class);
// then
JSONAssert.assertEquals(expected, mapping, false);
}
@Test // DATAES-568
public void shouldUseFieldNameOnText() throws IOException, JSONException {
// given
final String expected = "{\"fieldname-type\":{\"properties\":{"
+ "\"id-property\":{\"type\":\"keyword\",\"index\":true},"
+ "\"text-property\":{\"store\":false,\"type\":\"text\"}" + "}}}";
// when
String mapping = getMappingBuilder().buildMapping(FieldNameEntity.TextEntity.class);
// then
JSONAssert.assertEquals(expected, mapping, false);
}
@Test // DATAES-568
public void shouldUseFieldNameOnMapping() throws IOException, JSONException {
// given
final String expected = "{\"fieldname-type\":{\"properties\":{"
+ "\"id-property\":{\"type\":\"keyword\",\"index\":true},"
+ "\"mapping-property\":{\"type\":\"string\",\"analyzer\":\"standard_lowercase_asciifolding\"}" + "}}}";
// when
String mapping = getMappingBuilder().buildMapping(FieldNameEntity.MappingEntity.class);
// then
JSONAssert.assertEquals(expected, mapping, false);
}
@Test // DATAES-568
public void shouldUseFieldNameOnGeoPoint() throws IOException, JSONException {
// given
final String expected = "{\"fieldname-type\":{\"properties\":{"
+ "\"id-property\":{\"type\":\"keyword\",\"index\":true}," + "\"geopoint-property\":{\"type\":\"geo_point\"}"
+ "}}}";
// when
String mapping = getMappingBuilder().buildMapping(FieldNameEntity.GeoPointEntity.class);
// then
JSONAssert.assertEquals(expected, mapping, false);
}
@Test // DATAES-568
public void shouldUseFieldNameOnCircularEntity() throws IOException, JSONException {
// given
final String expected = "{\"fieldname-type\":{\"properties\":{"
+ "\"id-property\":{\"type\":\"keyword\",\"index\":true},"
+ "\"circular-property\":{\"type\":\"object\",\"properties\":{\"id-property\":{\"store\":false}}}" + "}}}";
// when
String mapping = getMappingBuilder().buildMapping(FieldNameEntity.CircularEntity.class);
// then
JSONAssert.assertEquals(expected, mapping, false);
}
@Test // DATAES-568
public void shouldUseFieldNameOnCompletion() throws IOException, JSONException {
// given
final String expected = "{\"fieldname-type\":{\"properties\":{"
+ "\"id-property\":{\"type\":\"keyword\",\"index\":true},"
+ "\"completion-property\":{\"type\":\"completion\",\"max_input_length\":100,\"preserve_position_increments\":true,\"preserve_separators\":true,\"search_analyzer\":\"simple\",\"analyzer\":\"simple\"},\"completion-property\":{\"store\":false}"
+ "}}}";
// when
String mapping = getMappingBuilder().buildMapping(FieldNameEntity.CompletionEntity.class);
// then
JSONAssert.assertEquals(expected, mapping, false);
}
@Test // DATAES-568
public void shouldUseFieldNameOnMultiField() throws IOException, JSONException {
// given
final String expected = "{\"fieldname-type\":{\"properties\":{"
+ "\"id-property\":{\"type\":\"keyword\",\"index\":true},"
+ "\"multifield-property\":{\"store\":false,\"type\":\"text\",\"analyzer\":\"whitespace\",\"fields\":{\"prefix\":{\"store\":false,\"type\":\"text\",\"analyzer\":\"stop\",\"search_analyzer\":\"standard\"}}}"
+ "}}}";
// when
String mapping = getMappingBuilder().buildMapping(FieldNameEntity.MultiFieldEntity.class);
// then
JSONAssert.assertEquals(expected, mapping, false);
}
} }

View File

@ -0,0 +1,54 @@
/* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
import java.util.Collection;
import java.util.Collections;
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.util.Lazy;
/**
* @author Peter-Josef Meisch
*/
abstract class MappingContextBaseTests {
protected final Lazy<ElasticsearchConverter> elasticsearchConverter = Lazy.of(this::setupElasticsearchConverter);
private ElasticsearchConverter setupElasticsearchConverter() {
return new MappingElasticsearchConverter(setupMappingContext());
}
private SimpleElasticsearchMappingContext setupMappingContext() {
SimpleElasticsearchMappingContext mappingContext = new ElasticsearchConfigurationSupport() {
@Override
protected Collection<String> getMappingBasePackages() {
return Collections.singletonList("org.springframework.data.elasticsearch.entities.mapping");
}
}.elasticsearchMappingContext();
mappingContext.initialize();
return mappingContext;
}
final MappingBuilder getMappingBuilder() {
return new MappingBuilder(elasticsearchConverter.get());
}
}

View File

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

View File

@ -15,11 +15,8 @@
*/ */
package org.springframework.data.elasticsearch.core; package org.springframework.data.elasticsearch.core;
import java.beans.IntrospectionException;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.elasticsearch.entities.SampleDateMappingEntity; import org.springframework.data.elasticsearch.entities.SampleDateMappingEntity;
@ -28,20 +25,20 @@ import org.springframework.data.elasticsearch.entities.SampleDateMappingEntity;
* @author Jakub Vavrik * @author Jakub Vavrik
* @author Mohsin Husen * @author Mohsin Husen
* @author Don Wellington * @author Don Wellington
* @author Peter-Josef Meisch
*/ */
public class SimpleElasticsearchDateMappingTests { public class SimpleElasticsearchDateMappingTests extends MappingContextBaseTests {
private static final String EXPECTED_MAPPING = "{\"mapping\":{\"properties\":{\"message\":{\"store\":true," + private static final String EXPECTED_MAPPING = "{\"mapping\":{\"properties\":{\"message\":{\"store\":true,"
"\"type\":\"text\",\"index\":false,\"analyzer\":\"standard\"},\"customFormatDate\":{\"store\":false,\"type\":\"date\",\"format\":\"dd.MM.yyyy hh:mm\"}," + + "\"type\":\"text\",\"index\":false,\"analyzer\":\"standard\"},\"customFormatDate\":{\"store\":false,\"type\":\"date\",\"format\":\"dd.MM.yyyy hh:mm\"},"
"\"defaultFormatDate\":{\"store\":false,\"type\":\"date\"},\"basicFormatDate\":{\"store\":false,\"" + + "\"defaultFormatDate\":{\"store\":false,\"type\":\"date\"},\"basicFormatDate\":{\"store\":false,\""
"type\":\"date\",\"format\":\"basic_date\"}}}}"; + "type\":\"date\",\"format\":\"basic_date\"}}}}";
@Test @Test
public void testCorrectDateMappings() throws NoSuchFieldException, IntrospectionException, IOException { public void testCorrectDateMappings() throws IOException {
XContentBuilder xContentBuilder = MappingBuilder.buildMapping(SampleDateMappingEntity.class, "mapping", "id", null);
xContentBuilder.close(); String mapping = getMappingBuilder().buildMapping(SampleDateMappingEntity.class);
ByteArrayOutputStream bos = (ByteArrayOutputStream) xContentBuilder.getOutputStream();
String result = bos.toString(); Assert.assertEquals(EXPECTED_MAPPING, mapping);
Assert.assertEquals(EXPECTED_MAPPING, result);
} }
} }

View File

@ -0,0 +1,106 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/package org.springframework.data.elasticsearch.entities;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.CompletionField;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.InnerField;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.annotations.MultiField;
import org.springframework.data.elasticsearch.core.completion.Completion;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
/**
* @author Peter-Josef Meisch
*/
public class FieldNameEntity {
@Document(indexName = "fieldname-index", type = "fieldname-type")
public static class IdEntity {
@Id @Field("id-property")
private String id;
}
@Document(indexName = "fieldname-index", type = "fieldname-type")
public static class TextEntity {
@Id @Field("id-property")
private String id;
@Field(name = "text-property", type = FieldType.Text)
private String textProperty;
}
@Document(indexName = "fieldname-index", type = "fieldname-type")
public static class MappingEntity {
@Id @Field("id-property")
private String id;
@Field("mapping-property") @Mapping(mappingPath = "/mappings/test-field-analyzed-mappings.json")
private byte[] mappingProperty;
}
@Document(indexName = "fieldname-index", type = "fieldname-type")
public static class GeoPointEntity {
@Id @Field("id-property")
private String id;
@Field("geopoint-property")
private GeoPoint geoPoint;
}
@Document(indexName = "fieldname-index", type = "fieldname-type")
public static class CircularEntity {
@Id @Field("id-property")
private String id;
@Field(name = "circular-property", type = FieldType.Object,
ignoreFields = { "circular-property" })
private CircularEntity circularProperty;
}
@Document(indexName = "fieldname-index", type = "fieldname-type")
public static class CompletionEntity {
@Id @Field("id-property")
private String id;
@Field("completion-property")
@CompletionField(maxInputLength = 100)
private Completion suggest;
}
@Document(indexName = "fieldname-index", type = "fieldname-type")
public static class MultiFieldEntity {
@Id @Field("id-property")
private String id;
@Field("multifield-property")
@MultiField(
mainField = @Field(type = FieldType.Text, analyzer = "whitespace"),
otherFields = {
@InnerField(suffix = "prefix", type = FieldType.Text, analyzer = "stop", searchAnalyzer = "standard")
}
)
private String description;
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.entities;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Parent;
/**
* MinimalChildEntity
*
* @author Peter-josef Meisch
*/
@Document(indexName = "test-index-minimal", type = "mapping")
public class MinimalChildEntity {
@Id
private String id;
@Parent(type = "parentType")
private String parentId;
}

View File

@ -0,0 +1,4 @@
{
"type": "string",
"analyzer": "standard_lowercase_asciifolding"
}