diff --git a/pom.xml b/pom.xml index 71345f9a1..339499606 100644 --- a/pom.xml +++ b/pom.xml @@ -227,6 +227,13 @@ + + org.skyscreamer + jsonassert + 1.5.0 + test + + org.apache.xbean diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchRestTemplate.java b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchRestTemplate.java index ac798af91..fd511f6d2 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchRestTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchRestTemplate.java @@ -17,7 +17,6 @@ import static org.elasticsearch.client.Requests.*; 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.StringUtils.*; @@ -223,18 +222,12 @@ public boolean putMapping(Class clazz) { logger.info("mappingPath in @Mapping has to be defined. Building mappings using @Field"); } } - ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz); - XContentBuilder xContentBuilder = null; try { - - ElasticsearchPersistentProperty property = persistentEntity.getRequiredIdProperty(); - - xContentBuilder = buildMapping(clazz, persistentEntity.getIndexType(), property.getFieldName(), - persistentEntity.getParentType()); + MappingBuilder mappingBuilder = new MappingBuilder(elasticsearchConverter); + return putMapping(clazz, mappingBuilder.buildMapping(clazz)); } catch (Exception e) { throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e); } - return putMapping(clazz, xContentBuilder); } @Override diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java index c543d84bd..245531b6c 100755 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java @@ -17,7 +17,6 @@ import static org.elasticsearch.client.Requests.*; import static org.elasticsearch.index.query.QueryBuilders.*; -import static org.springframework.data.elasticsearch.core.MappingBuilder.*; import static org.springframework.util.CollectionUtils.*; import java.io.BufferedReader; @@ -205,18 +204,12 @@ public boolean putMapping(Class clazz) { LOGGER.info("mappingPath in @Mapping has to be defined. Building mappings using @Field"); } } - ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz); - XContentBuilder xContentBuilder = null; try { - - ElasticsearchPersistentProperty property = persistentEntity.getRequiredIdProperty(); - - xContentBuilder = buildMapping(clazz, persistentEntity.getIndexType(), property.getFieldName(), - persistentEntity.getParentType()); + MappingBuilder mappingBuilder = new MappingBuilder(elasticsearchConverter); + return putMapping(clazz, mappingBuilder.buildMapping(clazz)); } catch (Exception e) { throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e); } - return putMapping(clazz, xContentBuilder); } @Override diff --git a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java index 2029ff996..94f82957c 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java @@ -20,31 +20,25 @@ import java.io.ByteArrayInputStream; import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; -import java.util.Map; +import java.util.Iterator; import org.elasticsearch.common.xcontent.XContentBuilder; 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.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; -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.annotations.*; 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.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.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; import com.fasterxml.jackson.databind.JsonNode; @@ -63,6 +57,7 @@ * @author Nordine Bittich * @author Robert Gruendler * @author Petr Kukral + * @author Peter-Josef Meisch */ class MappingBuilder { @@ -92,169 +87,199 @@ class MappingBuilder { 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_PRECISION = "geohash_precision"; - + private static final Logger logger = LoggerFactory.getLogger(ElasticsearchRestTemplate.class); 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 - addDynamicTemplatesMapping(mapping, clazz); + addDynamicTemplatesMapping(builder, entity); // Parent + String parentType = entity.getParentType(); if (hasText(parentType)) { - mapping.startObject(FIELD_PARENT).field(FIELD_TYPE, parentType).endObject(); + builder.startObject(FIELD_PARENT).field(FIELD_TYPE, parentType).endObject(); } // Properties - XContentBuilder xContentBuilder = mapping.startObject(FIELD_PROPERTIES); + builder.startObject(FIELD_PROPERTIES); + + mapEntity(builder, entity, true, "", false, FieldType.Auto, null); - mapEntity(xContentBuilder, clazz, true, idFieldName, "", false, FieldType.Auto, null); + builder.endObject() // FIELD_PROPERTIES + .endObject() // indexType + .endObject() // root object + .close(); - return xContentBuilder.endObject().endObject().endObject(); + return builder.getOutputStream().toString(); } - private static void mapEntity(XContentBuilder xContentBuilder, Class clazz, boolean isRootObject, String idFieldName, - String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType, Field fieldAnnotation) throws IOException { + private void mapEntity(XContentBuilder builder, @Nullable ElasticsearchPersistentEntity entity, boolean isRootObject, + 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 = FieldType.Object.toString().toLowerCase(); - if (nestedOrObjectField) { - type = fieldType.toString().toLowerCase(); - } - XContentBuilder t = xContentBuilder.startObject(nestedObjectFieldName).field(FIELD_TYPE, type); + String type = nestedOrObjectField ? fieldType.toString().toLowerCase() + : FieldType.Object.toString().toLowerCase(); + builder.startObject(nestedObjectFieldName).field(FIELD_TYPE, type); + + if (nestedOrObjectField && FieldType.Nested == fieldType && parentFieldAnnotation != null + && parentFieldAnnotation.includeInParent()) { - if (nestedOrObjectField && FieldType.Nested == fieldType && fieldAnnotation.includeInParent()) { - t.field("include_in_parent", fieldAnnotation.includeInParent()); + builder.field("include_in_parent", parentFieldAnnotation.includeInParent()); } - t.startObject(FIELD_PROPERTIES); + + builder.startObject(FIELD_PROPERTIES); } + if (entity != null) { + + entity.doWithProperties((PropertyHandler) property -> { + try { + if (property.isAnnotationPresent(Transient.class) || isInIgnoreFields(property, parentFieldAnnotation)) { + return; + } - for (java.lang.reflect.Field field : fields) { + if (property.isAnnotationPresent(Mapping.class)) { - if (field.isAnnotationPresent(Transient.class) || isInIgnoreFields(field, fieldAnnotation)) { - continue; - } + String mappingPath = property.getRequiredAnnotation(Mapping.class).mappingPath(); + if (!StringUtils.isEmpty(mappingPath)) { - 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; + ClassPathResource mappings = new ClassPathResource(mappingPath); + if (mappings.exists()) { + builder.rawField(property.getFieldName(), mappings.getInputStream(), XContentType.JSON); + return; + } + } } - } - } - boolean isGeoPointField = isGeoPointField(field); - boolean isCompletionField = isCompletionField(field); + boolean isGeoPointProperty = isGeoPointProperty(property); + boolean isCompletionProperty = isCompletionProperty(property); + boolean isNestedOrObjectProperty = isNestedOrObjectProperty(property); - 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; - } - } + Field fieldAnnotation = property.findAnnotation(Field.class); + if (!isGeoPointProperty && !isCompletionProperty && property.isEntity() && hasRelevantAnnotation(property)) { - MultiField multiField = field.getAnnotation(MultiField.class); + if (fieldAnnotation == null) { + return; + } - if (isGeoPointField) { - applyGeoPointFieldMapping(xContentBuilder, field); - } + Iterator> iterator = property.getPersistentEntityTypes().iterator(); + ElasticsearchPersistentEntity persistentEntity = iterator.hasNext() + ? elasticsearchConverter.getMappingContext().getPersistentEntity(iterator.next()) + : null; - if (isCompletionField) { - CompletionField completionField = field.getAnnotation(CompletionField.class); - applyCompletionFieldMapping(xContentBuilder, field, completionField); - } + mapEntity(builder, persistentEntity, false, property.getFieldName(), isNestedOrObjectProperty, + fieldAnnotation.type(), fieldAnnotation); - 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 (isNestedOrObjectProperty) { + return; + } + } - if (!isRootObject && isAnyPropertyAnnotatedAsField(fields) || nestedOrObjectField) { - xContentBuilder.endObject().endObject(); - } - } + MultiField multiField = property.findAnnotation(MultiField.class); + + if (isGeoPointProperty) { + applyGeoPointFieldMapping(builder, property); + return; + } - private static java.lang.reflect.Field[] retrieveFields(Class clazz) { - // Create list of fields. - List fields = new ArrayList<>(); + if (isCompletionProperty) { + CompletionField completionField = property.findAnnotation(CompletionField.class); + applyCompletionFieldMapping(builder, property, completionField); + } - // Keep backing up the inheritance hierarchy. - Class targetClass = clazz; - do { - fields.addAll(Arrays.asList(targetClass.getDeclaredFields())); - targetClass = targetClass.getSuperclass(); + 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); + } + }); } - while (targetClass != null && targetClass != Object.class); - return fields.toArray(new java.lang.reflect.Field[fields.size()]); + if (writeNestedProperties) { + builder.endObject().endObject(); + } } - private static boolean isAnnotated(java.lang.reflect.Field field) { - return field.getAnnotation(Field.class) != null || - field.getAnnotation(MultiField.class) != null || - field.getAnnotation(GeoPointField.class) != null || - field.getAnnotation(CompletionField.class) != null; + private boolean hasRelevantAnnotation(ElasticsearchPersistentProperty property) { + + return property.findAnnotation(Field.class) != null || property.findAnnotation(MultiField.class) != null + || property.findAnnotation(GeoPointField.class) != null + || property.findAnnotation(CompletionField.class) != null; } - private static void applyGeoPointFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field) throws IOException { - xContentBuilder.startObject(field.getName()); - xContentBuilder.field(FIELD_TYPE, TYPE_VALUE_GEO_POINT); - xContentBuilder.endObject(); + private void applyGeoPointFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property) + throws IOException { + + builder.startObject(property.getFieldName()).field(FIELD_TYPE, TYPE_VALUE_GEO_POINT).endObject(); } - 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); + private void applyCompletionFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property, + @Nullable CompletionField annotation) throws IOException { + + builder.startObject(property.getFieldName()); + builder.field(FIELD_TYPE, TYPE_VALUE_COMPLETION); + if (annotation != null) { - xContentBuilder.field(COMPLETION_MAX_INPUT_LENGTH, annotation.maxInputLength()); - xContentBuilder.field(COMPLETION_PRESERVE_POSITION_INCREMENTS, annotation.preservePositionIncrements()); - xContentBuilder.field(COMPLETION_PRESERVE_SEPARATORS, annotation.preserveSeparators()); + + builder.field(COMPLETION_MAX_INPUT_LENGTH, annotation.maxInputLength()); + builder.field(COMPLETION_PRESERVE_POSITION_INCREMENTS, annotation.preservePositionIncrements()); + builder.field(COMPLETION_PRESERVE_SEPARATORS, annotation.preserveSeparators()); if (!StringUtils.isEmpty(annotation.searchAnalyzer())) { - xContentBuilder.field(FIELD_SEARCH_ANALYZER, annotation.searchAnalyzer()); + builder.field(FIELD_SEARCH_ANALYZER, annotation.searchAnalyzer()); } if (!StringUtils.isEmpty(annotation.analyzer())) { - xContentBuilder.field(FIELD_INDEX_ANALYZER, annotation.analyzer()); + builder.field(FIELD_INDEX_ANALYZER, annotation.analyzer()); } + if (annotation.contexts().length > 0) { - xContentBuilder.startArray(COMPLETION_CONTEXTS); + + builder.startArray(COMPLETION_CONTEXTS); for (CompletionContext context : annotation.contexts()) { - xContentBuilder.startObject(); - xContentBuilder.field(FIELD_CONTEXT_NAME, context.name()); - xContentBuilder.field(FIELD_CONTEXT_TYPE, context.type().name().toLowerCase()); + + builder.startObject(); + builder.field(FIELD_CONTEXT_NAME, context.name()); + builder.field(FIELD_CONTEXT_TYPE, context.type().name().toLowerCase()); 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 { - xContentBuilder.startObject(field.getName()) - .field(FIELD_TYPE, TYPE_VALUE_KEYWORD) - .field(FIELD_INDEX, true); - xContentBuilder.endObject(); + + builder.startObject(property.getFieldName()).field(FIELD_TYPE, TYPE_VALUE_KEYWORD).field(FIELD_INDEX, true) + .endObject(); } /** @@ -262,8 +287,10 @@ private static void applyDefaultIdFieldMapping(XContentBuilder xContentBuilder, * * @throws IOException */ - private static void addSingleFieldMapping(XContentBuilder builder, java.lang.reflect.Field field, Field annotation, boolean nestedOrObjectField) throws IOException { - builder.startObject(field.getName()); + private void addSingleFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property, + Field annotation, boolean nestedOrObjectField) throws IOException { + + builder.startObject(property.getFieldName()); addFieldMappingParameters(builder, annotation, nestedOrObjectField); builder.endObject(); } @@ -273,14 +300,11 @@ private static void addSingleFieldMapping(XContentBuilder builder, java.lang.ref * * @throws IOException */ - private static void addMultiFieldMapping( - XContentBuilder builder, - java.lang.reflect.Field field, - MultiField annotation, - boolean nestedOrObjectField) throws IOException { + private void addMultiFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property, + MultiField annotation, boolean nestedOrObjectField) throws IOException { // main field - builder.startObject(field.getName()); + builder.startObject(property.getFieldName()); addFieldMappingParameters(builder, annotation.mainField(), nestedOrObjectField); // inner fields @@ -295,7 +319,8 @@ private static void addMultiFieldMapping( 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 store = false; boolean fielddata = false; @@ -371,15 +396,19 @@ private static void addFieldMappingParameters(XContentBuilder builder, Object an * * @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(); + private void addDynamicTemplatesMapping(XContentBuilder builder, ElasticsearchPersistentEntity entity) + throws IOException { + + if (entity.isAnnotationPresent(DynamicTemplates.class)) { + String mappingPath = entity.getRequiredAnnotation(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()){ + if (jsonNode != null && jsonNode.isArray()) { String json = objectMapper.writeValueAsString(jsonNode); builder.rawField(FIELD_DYNAMIC_TEMPLATES, new ByteArrayInputStream(json.getBytes()), XContentType.JSON); } @@ -388,63 +417,33 @@ private static void addDynamicTemplatesMapping(XContentBuilder builder, Class } } - protected static boolean isEntity(java.lang.reflect.Field field) { - TypeInformation typeInformation = ClassTypeInformation.from(field.getType()); - Class clazz = getFieldType(field); - boolean isComplexType = !SIMPLE_TYPE_HOLDER.isSimpleType(clazz); - return isComplexType && !Map.class.isAssignableFrom(typeInformation.getType()); - } - - protected static Class getFieldType(java.lang.reflect.Field field) { - - 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 boolean isAnyPropertyAnnotatedWithField(@Nullable ElasticsearchPersistentEntity entity) { - 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; + return entity != null && entity.getPersistentProperty(Field.class) != null; } - private static boolean isIdField(java.lang.reflect.Field field, String idFieldName) { - return idFieldName.equals(field.getName()); - } + private boolean isInIgnoreFields(ElasticsearchPersistentProperty property, @Nullable Field parentFieldAnnotation) { - private static boolean isInIgnoreFields(java.lang.reflect.Field field, Field parentFieldAnnotation) { if (null != parentFieldAnnotation) { + String[] ignoreFields = parentFieldAnnotation.ignoreFields(); - return Arrays.asList(ignoreFields).contains(field.getName()); + return Arrays.asList(ignoreFields).contains(property.getFieldName()); } return false; } - private static boolean isNestedOrObjectField(java.lang.reflect.Field field) { - Field fieldAnnotation = field.getAnnotation(Field.class); - return fieldAnnotation != null && (FieldType.Nested == fieldAnnotation.type() || FieldType.Object == fieldAnnotation.type()); + private boolean isNestedOrObjectProperty(ElasticsearchPersistentProperty property) { + + 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) { - return field.getType() == GeoPoint.class || field.getAnnotation(GeoPointField.class) != null; + private boolean isGeoPointProperty(ElasticsearchPersistentProperty property) { + return property.getActualType() == GeoPoint.class || property.isAnnotationPresent(GeoPointField.class); } - private static boolean isCompletionField(java.lang.reflect.Field field) { - return field.getType() == Completion.class; + private boolean isCompletionProperty(ElasticsearchPersistentProperty property) { + return property.getActualType() == Completion.class; } } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchRestTemplateTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchRestTemplateTests.java index 2c7e8933c..35dfe29a0 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchRestTemplateTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchRestTemplateTests.java @@ -50,8 +50,8 @@ public void shouldThrowExceptionIfDocumentDoesNotExistWhileDoingPartialUpdate() // when IndexRequest indexRequest = new IndexRequest(); indexRequest.source("{}", XContentType.JSON); - UpdateQuery updateQuery = new UpdateQueryBuilder().withId(randomNumeric(5)) - .withClass(SampleEntity.class).withIndexRequest(indexRequest).build(); + UpdateQuery updateQuery = new UpdateQueryBuilder().withId(randomNumeric(5)).withClass(SampleEntity.class) + .withIndexRequest(indexRequest).build(); elasticsearchTemplate.update(updateQuery); } } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java index 4179c363e..d9e8e3500 100755 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java @@ -90,8 +90,8 @@ * @author Peter Nowak * @author Ivan Greene * @author Dmitriy Yakovlev + * @author Peter-Josef Meisch */ - @Ignore public class ElasticsearchTemplateTests { @@ -128,6 +128,8 @@ public AggregatedPage mapResults(SearchResponse response, Class clazz, @Before public void before() { + deleteIndices(); + elasticsearchTemplate.createIndex(SampleEntity.class); elasticsearchTemplate.putMapping(SampleEntity.class); @@ -137,7 +139,10 @@ public void before() { @After public void after() { + deleteIndices(); + } + private void deleteIndices() { elasticsearchTemplate.deleteIndex(SampleEntity.class); elasticsearchTemplate.deleteIndex(SampleEntityUUIDKeyed.class); elasticsearchTemplate.deleteIndex(UseServerConfigurationEntity.class); diff --git a/src/test/java/org/springframework/data/elasticsearch/core/MappingBuilderTests.java b/src/test/java/org/springframework/data/elasticsearch/core/MappingBuilderTests.java index 00cea87ea..dab674240 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/MappingBuilderTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/MappingBuilderTests.java @@ -21,7 +21,6 @@ import static org.junit.Assert.*; import static org.springframework.data.elasticsearch.utils.IndexBuilder.*; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigDecimal; import java.util.Arrays; @@ -29,25 +28,15 @@ import java.util.List; import java.util.Map; -import org.elasticsearch.common.xcontent.XContentBuilder; +import org.json.JSONException; import org.junit.Test; import org.junit.runner.RunWith; +import org.skyscreamer.jsonassert.JSONAssert; 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.core.query.NativeSearchQueryBuilder; import org.springframework.data.elasticsearch.core.query.SearchQuery; -import org.springframework.data.elasticsearch.entities.Book; -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.data.elasticsearch.entities.*; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -59,19 +48,14 @@ * @author Nordine Bittich * @author Don Wellington * @author Sascha Woo + * @author Peter-Josef Meisch */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:elasticsearch-template-test.xml") -public class MappingBuilderTests { +public class MappingBuilderTests extends MappingContextBaseTests { @Autowired private ElasticsearchTemplate elasticsearchTemplate; - private String xContentBuilderToString(XContentBuilder builder) { - builder.close(); - ByteArrayOutputStream bos = (ByteArrayOutputStream) builder.getOutputStream(); - return bos.toString(); - } - @Test public void shouldNotFailOnCircularReference() { elasticsearchTemplate.deleteIndex(SimpleRecursiveEntity.class); @@ -81,24 +65,25 @@ public void shouldNotFailOnCircularReference() { } @Test - public void testInfiniteLoopAvoidance() throws IOException { + public void testInfiniteLoopAvoidance() throws IOException, JSONException { final String expected = "{\"mapping\":{\"properties\":{\"message\":{\"store\":true,\"" + "type\":\"text\",\"index\":false," + "\"analyzer\":\"standard\"}}}}"; - XContentBuilder xContentBuilder = MappingBuilder.buildMapping(SampleTransientEntity.class, "mapping", "id", null); - assertThat(xContentBuilderToString(xContentBuilder), is(expected)); + String mapping = getMappingBuilder().buildMapping(SampleTransientEntity.class); + + JSONAssert.assertEquals(expected, mapping, false); } @Test - public void shouldUseValueFromAnnotationType() throws IOException { + public void shouldUseValueFromAnnotationType() throws IOException, JSONException { // Given - final String expected = "{\"mapping\":{\"properties\":{\"price\":{\"store\":false,\"type\":\"double\"}}}}"; + final String expected = "{\"price\":{\"properties\":{\"price\":{\"store\":false,\"type\":\"double\"}}}}"; // When - XContentBuilder xContentBuilder = MappingBuilder.buildMapping(StockPrice.class, "mapping", "id", null); + String mapping = getMappingBuilder().buildMapping(StockPrice.class); // Then - assertThat(xContentBuilderToString(xContentBuilder), is(expected)); + JSONAssert.assertEquals(expected, mapping, false); } @Test // DATAES-530 @@ -127,23 +112,26 @@ public void shouldAddStockPriceDocumentToIndex() throws IOException { } @Test - public void shouldCreateMappingForSpecifiedParentType() throws IOException { + public void shouldCreateMappingForSpecifiedParentType() throws IOException, JSONException { 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 */ @Test - public void shouldBuildMappingWithSuperclass() throws IOException { + public void shouldBuildMappingWithSuperclass() throws IOException, JSONException { final String expected = "{\"mapping\":{\"properties\":{\"message\":{\"store\":true,\"" + "type\":\"text\",\"index\":false,\"analyzer\":\"standard\"}" + ",\"createdDate\":{\"store\":false," + "\"type\":\"date\",\"index\":false}}}}"; - XContentBuilder xContentBuilder = MappingBuilder.buildMapping(SampleInheritedEntity.class, "mapping", "id", null); - assertThat(xContentBuilderToString(xContentBuilder), is(expected)); + String mapping = getMappingBuilder().buildMapping(SampleInheritedEntity.class); + + JSONAssert.assertEquals(expected, mapping, false); } /* @@ -174,19 +162,18 @@ public void shouldAddSampleInheritedEntityDocumentToIndex() throws IOException { } @Test - public void shouldBuildMappingsForGeoPoint() throws IOException { + public void shouldBuildMappingsForGeoPoint() throws IOException, JSONException { // given + String expected = "{\"geo-test-index\": {\"properties\": {" + "\"pointA\":{\"type\":\"geo_point\"}," + + "\"pointB\":{\"type\":\"geo_point\"}," + "\"pointC\":{\"type\":\"geo_point\"}," + + "\"pointD\":{\"type\":\"geo_point\"}" + "}}}"; // when - XContentBuilder xContentBuilder = MappingBuilder.buildMapping(GeoEntity.class, "mapping", "id", null); + String mapping; + mapping = getMappingBuilder().buildMapping(GeoEntity.class); // then - final String result = xContentBuilderToString(xContentBuilder); - - 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\"")); + JSONAssert.assertEquals(expected, mapping, false); } /** @@ -276,4 +263,103 @@ public void shouldUseCopyTo() throws IOException { assertThat(fieldFirstName.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); + } } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/MappingContextBaseTests.java b/src/test/java/org/springframework/data/elasticsearch/core/MappingContextBaseTests.java new file mode 100644 index 000000000..dbac910d3 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/MappingContextBaseTests.java @@ -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 = Lazy.of(this::setupElasticsearchConverter); + + private ElasticsearchConverter setupElasticsearchConverter() { + return new MappingElasticsearchConverter(setupMappingContext()); + } + + private SimpleElasticsearchMappingContext setupMappingContext() { + + SimpleElasticsearchMappingContext mappingContext = new ElasticsearchConfigurationSupport() { + @Override + protected Collection getMappingBasePackages() { + return Collections.singletonList("org.springframework.data.elasticsearch.entities.mapping"); + } + }.elasticsearchMappingContext(); + + mappingContext.initialize(); + return mappingContext; + } + + final MappingBuilder getMappingBuilder() { + return new MappingBuilder(elasticsearchConverter.get()); + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/SimpleDynamicTemplatesMappingTests.java b/src/test/java/org/springframework/data/elasticsearch/core/SimpleDynamicTemplatesMappingTests.java index 586223859..71ddaa90d 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/SimpleDynamicTemplatesMappingTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/SimpleDynamicTemplatesMappingTests.java @@ -2,8 +2,6 @@ import java.io.IOException; -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -16,35 +14,32 @@ * Dynamic templates tests * * @author Petr Kukral + * @author Peter-Josef Meisch */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:elasticsearch-template-test.xml") -public class SimpleDynamicTemplatesMappingTests { +public class SimpleDynamicTemplatesMappingTests extends MappingContextBaseTests { @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, Strings.toString(xContentBuilder)); + String mapping = getMappingBuilder().buildMapping(SampleDynamicTemplatesEntity.class); + + 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, mapping); } @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, Strings.toString(xContentBuilder)); + String mapping = getMappingBuilder().buildMapping(SampleDynamicTemplatesEntityTwo.class); + 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, mapping); } } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/SimpleElasticsearchDateMappingTests.java b/src/test/java/org/springframework/data/elasticsearch/core/SimpleElasticsearchDateMappingTests.java index f090a54a2..bdce10abf 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/SimpleElasticsearchDateMappingTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/SimpleElasticsearchDateMappingTests.java @@ -15,11 +15,8 @@ */ package org.springframework.data.elasticsearch.core; -import java.beans.IntrospectionException; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.junit.Assert; import org.junit.Test; import org.springframework.data.elasticsearch.entities.SampleDateMappingEntity; @@ -28,20 +25,20 @@ * @author Jakub Vavrik * @author Mohsin Husen * @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," + - "\"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,\"" + - "type\":\"date\",\"format\":\"basic_date\"}}}}"; + 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\"}," + + "\"defaultFormatDate\":{\"store\":false,\"type\":\"date\"},\"basicFormatDate\":{\"store\":false,\"" + + "type\":\"date\",\"format\":\"basic_date\"}}}}"; @Test - public void testCorrectDateMappings() throws NoSuchFieldException, IntrospectionException, IOException { - XContentBuilder xContentBuilder = MappingBuilder.buildMapping(SampleDateMappingEntity.class, "mapping", "id", null); - xContentBuilder.close(); - ByteArrayOutputStream bos = (ByteArrayOutputStream) xContentBuilder.getOutputStream(); - String result = bos.toString(); - Assert.assertEquals(EXPECTED_MAPPING, result); + public void testCorrectDateMappings() throws IOException { + + String mapping = getMappingBuilder().buildMapping(SampleDateMappingEntity.class); + + Assert.assertEquals(EXPECTED_MAPPING, mapping); } } diff --git a/src/test/java/org/springframework/data/elasticsearch/entities/FieldNameEntity.java b/src/test/java/org/springframework/data/elasticsearch/entities/FieldNameEntity.java new file mode 100644 index 000000000..d5fc2e1cd --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/entities/FieldNameEntity.java @@ -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; + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/entities/MinimalChildEntity.java b/src/test/java/org/springframework/data/elasticsearch/entities/MinimalChildEntity.java new file mode 100644 index 000000000..7172dc10e --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/entities/MinimalChildEntity.java @@ -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; +} diff --git a/src/test/resources/mappings/test-field-analyzed-mappings.json b/src/test/resources/mappings/test-field-analyzed-mappings.json new file mode 100644 index 000000000..cce7b3e99 --- /dev/null +++ b/src/test/resources/mappings/test-field-analyzed-mappings.json @@ -0,0 +1,4 @@ +{ + "type": "string", + "analyzer": "standard_lowercase_asciifolding" +}