From f0117e7b6a9a2df9c296f91c9c9f0107809f9a67 Mon Sep 17 00:00:00 2001 From: Peter-Josef Meisch Date: Thu, 29 Aug 2019 07:27:49 +0200 Subject: [PATCH] DATAES-621 - Add missing fieldtypes and parameters to mapping. Original PR: #311 --- .../data/elasticsearch/annotations/Field.java | 66 ++++- .../elasticsearch/annotations/FieldType.java | 44 +-- .../annotations/IndexOptions.java | 24 ++ .../annotations/IndexPrefixes.java | 29 ++ .../elasticsearch/annotations/InnerField.java | 63 ++++ .../elasticsearch/annotations/Similarity.java | 36 +++ .../elasticsearch/annotations/TermVector.java | 24 ++ .../core/ElasticsearchRestTemplate.java | 1 + .../core/ElasticsearchTemplate.java | 1 + .../data/elasticsearch/core/ResourceUtil.java | 4 +- .../{ => indexmapping}/MappingBuilder.java | 124 ++------ .../core/indexmapping/MappingParameters.java | 272 ++++++++++++++++++ .../core/indexmapping/package-info.java | 5 + .../MappingBuilderTests.java | 163 +++++++++-- .../MappingContextBaseTests.java | 2 +- .../indexmapping/MappingParametersTest.java | 68 +++++ .../SimpleDynamicTemplatesMappingTests.java | 2 +- .../SimpleElasticsearchDateMappingTests.java | 7 +- 18 files changed, 787 insertions(+), 148 deletions(-) create mode 100644 src/main/java/org/springframework/data/elasticsearch/annotations/IndexOptions.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/annotations/IndexPrefixes.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/annotations/Similarity.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/annotations/TermVector.java rename src/main/java/org/springframework/data/elasticsearch/core/{ => indexmapping}/MappingBuilder.java (76%) create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/indexmapping/MappingParameters.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/indexmapping/package-info.java rename src/test/java/org/springframework/data/elasticsearch/core/{ => indexmapping}/MappingBuilderTests.java (77%) rename src/test/java/org/springframework/data/elasticsearch/core/{ => indexmapping}/MappingContextBaseTests.java (96%) create mode 100644 src/test/java/org/springframework/data/elasticsearch/core/indexmapping/MappingParametersTest.java rename src/test/java/org/springframework/data/elasticsearch/core/{ => indexmapping}/SimpleDynamicTemplatesMappingTests.java (98%) rename src/test/java/org/springframework/data/elasticsearch/core/{ => indexmapping}/SimpleElasticsearchDateMappingTests.java (90%) diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/Field.java b/src/main/java/org/springframework/data/elasticsearch/annotations/Field.java index c48d4716c..aecea592a 100644 --- a/src/main/java/org/springframework/data/elasticsearch/annotations/Field.java +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/Field.java @@ -50,8 +50,8 @@ public @interface Field { /** * The name to be used to store the field inside the document. - *

√5 - * If not set, the name of the annotated property is used. + *

+ * √5 If not set, the name of the annotated property is used. * * @since 3.2 */ @@ -86,4 +86,66 @@ public @interface Field { * @since 4.0 */ int ignoreAbove() default -1; + + /** + * @since 4.0 + */ + boolean coerce() default true; + + /** + * @since 4.0 + */ + boolean docValues() default true; + + /** + * @since 4.0 + */ + boolean ignoreMalformed() default false; + + /** + * @since 4.0 + */ + IndexOptions indexOptions() default IndexOptions.none; + + /** + * @since 4.0 + */ + boolean indexPhrases() default false; + + /** + * implemented as array to enable the empty default value + * + * @since 4.0 + */ + IndexPrefixes[] indexPrefixes() default {}; + + /** + * @since 4.0 + */ + boolean norms() default true; + + /** + * @since 4.0 + */ + String nullValue() default ""; + + /** + * @since 4.0 + */ + int positionIncrementGap() default -1; + + /** + * @since 4.0 + */ + Similarity similarity() default Similarity.Default; + + /** + * @since 4.0 + */ + TermVector termVector() default TermVector.none; + + /** + * @since 4.0 + */ + double scalingFactor() default 1; } diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/FieldType.java b/src/main/java/org/springframework/data/elasticsearch/annotations/FieldType.java index 9913cb46d..867973dc5 100644 --- a/src/main/java/org/springframework/data/elasticsearch/annotations/FieldType.java +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/FieldType.java @@ -20,22 +20,34 @@ package org.springframework.data.elasticsearch.annotations; * @author Mohsin Husen * @author Artur Konczak * @author Zeng Zetang + * @author Peter-Josef Meisch */ public enum FieldType { - Text, - Byte, - Short, - Integer, - Long, - Date, - Half_Float, - Float, - Double, - Boolean, - Object, - Auto, - Nested, - Ip, - Attachment, - Keyword + Auto, + Text, + Keyword, + Long, + Integer, + Short, + Byte, + Double, + Float, + Half_Float, + Scaled_Float, + Date, + Date_Nanos, + Boolean, + Binary, + Integer_Range, + Float_Range, + Long_Range, + Double_Range, + Date_Range, + Ip_Range, + Object, + Nested, + Ip, + TokenCount, + Percolator, + Flattened } diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/IndexOptions.java b/src/main/java/org/springframework/data/elasticsearch/annotations/IndexOptions.java new file mode 100644 index 000000000..85141cb06 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/IndexOptions.java @@ -0,0 +1,24 @@ +/* + * 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.annotations; + +/** + * @author Peter-Josef Meisch + * @since 4.0 + */ +public enum IndexOptions { + none, docs, freqs, positions, offsets +} diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/IndexPrefixes.java b/src/main/java/org/springframework/data/elasticsearch/annotations/IndexPrefixes.java new file mode 100644 index 000000000..30f7d13b5 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/IndexPrefixes.java @@ -0,0 +1,29 @@ +/* + * 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.annotations; + +/** + * @author Peter-Josef Meisch + * @since 4.0 + */ +public @interface IndexPrefixes { + static final int MIN_DEFAULT = 2; + static final int MAX_DEFAULT = 2; + + int minChars() default MIN_DEFAULT; + + int maxChars() default MAX_DEFAULT; +} diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/InnerField.java b/src/main/java/org/springframework/data/elasticsearch/annotations/InnerField.java index 93fc75b1a..54a11dd84 100644 --- a/src/main/java/org/springframework/data/elasticsearch/annotations/InnerField.java +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/InnerField.java @@ -25,6 +25,7 @@ import java.lang.annotation.Target; * @author Mohsin Husen * @author Sascha Woo * @author Xiao Yu + * @author Peter-Josef Meisch */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @@ -54,4 +55,66 @@ public @interface InnerField { * @since 4.0 */ int ignoreAbove() default -1; + + /** + * @since 4.0 + */ + boolean coerce() default true; + + /** + * @since 4.0 + */ + boolean docValues() default true; + + /** + * @since 4.0 + */ + boolean ignoreMalformed() default false; + + /** + * @since 4.0 + */ + IndexOptions indexOptions() default IndexOptions.none; + + /** + * @since 4.0 + */ + boolean indexPhrases() default false; + + /** + * implemented as array to enable the empty default value + * + * @since 4.0 + */ + IndexPrefixes[] indexPrefixes() default {}; + + /** + * @since 4.0 + */ + boolean norms() default true; + + /** + * @since 4.0 + */ + String nullValue() default ""; + + /** + * @since 4.0 + */ + int positionIncrementGap() default -1; + + /** + * @since 4.0 + */ + Similarity similarity() default Similarity.Default; + + /** + * @since 4.0 + */ + TermVector termVector() default TermVector.none; + + /** + * @since 4.0 + */ + double scalingFactor() default 1; } diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/Similarity.java b/src/main/java/org/springframework/data/elasticsearch/annotations/Similarity.java new file mode 100644 index 000000000..bb4580efa --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/Similarity.java @@ -0,0 +1,36 @@ +/* + * 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.annotations; + +/** + * @author Peter-Josef Meisch + * @since 4.0 + */ +public enum Similarity { + Default("default"), BM25("BM25"), classic("classic"), Boolean("boolean"); + + // need to use a custom name because 'boolean' can't be used as enum name + private final String toStringName; + + Similarity(String name) { + this.toStringName = name; + } + + @Override + public String toString() { + return toStringName; + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/TermVector.java b/src/main/java/org/springframework/data/elasticsearch/annotations/TermVector.java new file mode 100644 index 000000000..615760a4d --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/TermVector.java @@ -0,0 +1,24 @@ +/* + * 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.annotations; + +/** + * @author Peter-Josef Meisch + * @since 4.0 + */ +public enum TermVector { + none, no, yes, with_positions, with_offsets, woth_positions_offsets, with_positions_payloads, with_positions_offets_payloads +} 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 fa0fc3cec..2f428e4e2 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchRestTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchRestTemplate.java @@ -101,6 +101,7 @@ import org.springframework.data.elasticsearch.core.client.support.AliasData; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter; import org.springframework.data.elasticsearch.core.facet.FacetRequest; +import org.springframework.data.elasticsearch.core.indexmapping.MappingBuilder; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty; import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; 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 bb2eceb65..1657477ea 100755 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java @@ -88,6 +88,7 @@ import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter; import org.springframework.data.elasticsearch.core.facet.FacetRequest; +import org.springframework.data.elasticsearch.core.indexmapping.MappingBuilder; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty; import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ResourceUtil.java b/src/main/java/org/springframework/data/elasticsearch/core/ResourceUtil.java index aa0b8db28..627ab09a0 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/ResourceUtil.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ResourceUtil.java @@ -20,7 +20,6 @@ import java.nio.charset.Charset; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import org.springframework.core.io.ClassPathResource; import org.springframework.util.StreamUtils; @@ -28,9 +27,10 @@ import org.springframework.util.StreamUtils; * Utility to read {@link org.springframework.core.io.Resource}s. * * @author Mark Paluch + * @author Peter-Josef Meisch * @since 3.2 */ -abstract class ResourceUtil { +public abstract class ResourceUtil { private static final Logger LOGGER = LoggerFactory.getLogger(ResourceUtil.class); diff --git a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/indexmapping/MappingBuilder.java similarity index 76% rename from src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java rename to src/main/java/org/springframework/data/elasticsearch/core/indexmapping/MappingBuilder.java index 036f09a7a..4a82e8e85 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/indexmapping/MappingBuilder.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.elasticsearch.core; +package org.springframework.data.elasticsearch.core.indexmapping; import static org.elasticsearch.common.xcontent.XContentFactory.*; +import static org.springframework.data.elasticsearch.core.indexmapping.MappingParameters.*; import static org.springframework.util.StringUtils.*; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.Iterator; @@ -31,7 +33,6 @@ 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; @@ -39,6 +40,8 @@ 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.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.core.ResourceUtil; import org.springframework.data.elasticsearch.core.completion.Completion; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.geo.GeoPoint; @@ -47,7 +50,6 @@ import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersiste import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.util.TypeInformation; import org.springframework.lang.Nullable; -import org.springframework.util.Assert; import org.springframework.util.StringUtils; import com.fasterxml.jackson.databind.JsonNode; @@ -69,24 +71,15 @@ import com.fasterxml.jackson.databind.ObjectMapper; * @author Peter-Josef Meisch * @author Xiao Yu */ -class MappingBuilder { +public class MappingBuilder { - private static final String FIELD_DATA = "fielddata"; - private static final String FIELD_STORE = "store"; - private static final String FIELD_TYPE = "type"; private static final String FIELD_INDEX = "index"; - private static final String FIELD_FORMAT = "format"; - private static final String FIELD_SEARCH_ANALYZER = "search_analyzer"; - private static final String FIELD_INDEX_ANALYZER = "analyzer"; - private static final String FIELD_NORMALIZER = "normalizer"; private static final String FIELD_PROPERTIES = "properties"; private static final String FIELD_PARENT = "_parent"; - private static final String FIELD_COPY_TO = "copy_to"; private static final String FIELD_CONTEXT_NAME = "name"; private static final String FIELD_CONTEXT_TYPE = "type"; private static final String FIELD_CONTEXT_PRECISION = "precision"; private static final String FIELD_DYNAMIC_TEMPLATES = "dynamic_templates"; - private static final String FIELD_IGNORE_ABOVE = "ignore_above"; private static final String COMPLETION_PRESERVE_SEPARATORS = "preserve_separators"; private static final String COMPLETION_PRESERVE_POSITION_INCREMENTS = "preserve_position_increments"; @@ -101,7 +94,7 @@ class MappingBuilder { private final ElasticsearchConverter elasticsearchConverter; - MappingBuilder(ElasticsearchConverter elasticsearchConverter) { + public MappingBuilder(ElasticsearchConverter elasticsearchConverter) { this.elasticsearchConverter = elasticsearchConverter; } @@ -111,7 +104,7 @@ class MappingBuilder { * @return JSON string * @throws IOException */ - String buildPropertyMapping(Class clazz) throws IOException { + public String buildPropertyMapping(Class clazz) throws IOException { ElasticsearchPersistentEntity entity = elasticsearchConverter.getMappingContext() .getRequiredPersistentEntity(clazz); @@ -124,7 +117,7 @@ class MappingBuilder { // Parent String parentType = entity.getParentType(); if (hasText(parentType)) { - builder.startObject(FIELD_PARENT).field(FIELD_TYPE, parentType).endObject(); + builder.startObject(FIELD_PARENT).field(FIELD_PARAM_TYPE, parentType).endObject(); } // Properties @@ -149,7 +142,7 @@ class MappingBuilder { String type = nestedOrObjectField ? fieldType.toString().toLowerCase() : FieldType.Object.toString().toLowerCase(); - builder.startObject(nestedObjectFieldName).field(FIELD_TYPE, type); + builder.startObject(nestedObjectFieldName).field(FIELD_PARAM_TYPE, type); if (nestedOrObjectField && FieldType.Nested == fieldType && parentFieldAnnotation != null && parentFieldAnnotation.includeInParent()) { @@ -250,14 +243,14 @@ class MappingBuilder { private void applyGeoPointFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property) throws IOException { - builder.startObject(property.getFieldName()).field(FIELD_TYPE, TYPE_VALUE_GEO_POINT).endObject(); + builder.startObject(property.getFieldName()).field(FIELD_PARAM_TYPE, TYPE_VALUE_GEO_POINT).endObject(); } private void applyCompletionFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property, @Nullable CompletionField annotation) throws IOException { builder.startObject(property.getFieldName()); - builder.field(FIELD_TYPE, TYPE_VALUE_COMPLETION); + builder.field(FIELD_PARAM_TYPE, TYPE_VALUE_COMPLETION); if (annotation != null) { @@ -265,10 +258,10 @@ class MappingBuilder { builder.field(COMPLETION_PRESERVE_POSITION_INCREMENTS, annotation.preservePositionIncrements()); builder.field(COMPLETION_PRESERVE_SEPARATORS, annotation.preserveSeparators()); if (!StringUtils.isEmpty(annotation.searchAnalyzer())) { - builder.field(FIELD_SEARCH_ANALYZER, annotation.searchAnalyzer()); + builder.field(FIELD_PARAM_SEARCH_ANALYZER, annotation.searchAnalyzer()); } if (!StringUtils.isEmpty(annotation.analyzer())) { - builder.field(FIELD_INDEX_ANALYZER, annotation.analyzer()); + builder.field(FIELD_PARAM_INDEX_ANALYZER, annotation.analyzer()); } if (annotation.contexts().length > 0) { @@ -294,7 +287,7 @@ class MappingBuilder { private void applyDefaultIdFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property) throws IOException { - builder.startObject(property.getFieldName()).field(FIELD_TYPE, TYPE_VALUE_KEYWORD).field(FIELD_INDEX, true) + builder.startObject(property.getFieldName()).field(FIELD_PARAM_TYPE, TYPE_VALUE_KEYWORD).field(FIELD_INDEX, true) .endObject(); } @@ -335,92 +328,15 @@ class MappingBuilder { builder.endObject(); } - private void addFieldMappingParameters(XContentBuilder builder, Object annotation, boolean nestedOrObjectField) + private void addFieldMappingParameters(XContentBuilder builder, Annotation annotation, boolean nestedOrObjectField) throws IOException { - boolean index = true; - boolean store = false; - boolean fielddata = false; - FieldType type = null; - DateFormat dateFormat = null; - String datePattern = null; - String analyzer = null; - String searchAnalyzer = null; - String normalizer = null; - String[] copyTo = null; - Integer ignoreAbove = null; + MappingParameters mappingParameters = MappingParameters.from(annotation); - if (annotation instanceof Field) { - // @Field - Field fieldAnnotation = (Field) annotation; - index = fieldAnnotation.index(); - store = fieldAnnotation.store(); - fielddata = fieldAnnotation.fielddata(); - type = fieldAnnotation.type(); - dateFormat = fieldAnnotation.format(); - datePattern = fieldAnnotation.pattern(); - analyzer = fieldAnnotation.analyzer(); - searchAnalyzer = fieldAnnotation.searchAnalyzer(); - normalizer = fieldAnnotation.normalizer(); - copyTo = fieldAnnotation.copyTo(); - ignoreAbove = fieldAnnotation.ignoreAbove() >= 0 ? fieldAnnotation.ignoreAbove() : null; - } else if (annotation instanceof InnerField) { - // @InnerField - InnerField fieldAnnotation = (InnerField) annotation; - index = fieldAnnotation.index(); - store = fieldAnnotation.store(); - fielddata = fieldAnnotation.fielddata(); - type = fieldAnnotation.type(); - dateFormat = fieldAnnotation.format(); - datePattern = fieldAnnotation.pattern(); - analyzer = fieldAnnotation.analyzer(); - searchAnalyzer = fieldAnnotation.searchAnalyzer(); - normalizer = fieldAnnotation.normalizer(); - ignoreAbove = fieldAnnotation.ignoreAbove() >= 0 ? fieldAnnotation.ignoreAbove() : null; - } else { - throw new IllegalArgumentException("annotation must be an instance of @Field or @InnerField"); - } - - if (!nestedOrObjectField) { - builder.field(FIELD_STORE, store); - } - - if (fielddata) { - builder.field(FIELD_DATA, fielddata); - } - - if (type != FieldType.Auto) { - builder.field(FIELD_TYPE, type.name().toLowerCase()); - - if (type == FieldType.Date && dateFormat != DateFormat.none) { - builder.field(FIELD_FORMAT, dateFormat == DateFormat.custom ? datePattern : dateFormat.toString()); - } - } - - if (!index) { - builder.field(FIELD_INDEX, index); - } - - if (!StringUtils.isEmpty(analyzer)) { - builder.field(FIELD_INDEX_ANALYZER, analyzer); - } - - if (!StringUtils.isEmpty(searchAnalyzer)) { - builder.field(FIELD_SEARCH_ANALYZER, searchAnalyzer); - } - - if (!StringUtils.isEmpty(normalizer)) { - builder.field(FIELD_NORMALIZER, normalizer); - } - - if (copyTo != null && copyTo.length > 0) { - builder.field(FIELD_COPY_TO, copyTo); - } - - if (ignoreAbove != null) { - Assert.isTrue(ignoreAbove >= 0, "ignore_above must be a positive value"); - builder.field(FIELD_IGNORE_ABOVE, ignoreAbove); + if (!nestedOrObjectField && mappingParameters.isStore()) { + builder.field(FIELD_PARAM_STORE, mappingParameters.isStore()); } + mappingParameters.writeTypeAndParametersTo(builder); } /** diff --git a/src/main/java/org/springframework/data/elasticsearch/core/indexmapping/MappingParameters.java b/src/main/java/org/springframework/data/elasticsearch/core/indexmapping/MappingParameters.java new file mode 100644 index 000000000..04c330986 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/indexmapping/MappingParameters.java @@ -0,0 +1,272 @@ +/* + * 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.indexmapping; + +import java.io.IOException; +import java.lang.annotation.Annotation; + +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.springframework.data.elasticsearch.annotations.DateFormat; +import org.springframework.data.elasticsearch.annotations.Field; +import org.springframework.data.elasticsearch.annotations.FieldType; +import org.springframework.data.elasticsearch.annotations.IndexOptions; +import org.springframework.data.elasticsearch.annotations.IndexPrefixes; +import org.springframework.data.elasticsearch.annotations.InnerField; +import org.springframework.data.elasticsearch.annotations.Similarity; +import org.springframework.data.elasticsearch.annotations.TermVector; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * A class to hold the mapping parameters that might be set on + * {@link org.springframework.data.elasticsearch.annotations.Field } or + * {@link org.springframework.data.elasticsearch.annotations.InnerField} annotation. + * + * @author Peter-Josef Meisch + * @since 4.0 + */ +public final class MappingParameters { + + static final String FIELD_PARAM_COERCE = "coerce"; + static final String FIELD_PARAM_COPY_TO = "copy_to"; + static final String FIELD_PARAM_DOC_VALUES = "doc_values"; + static final String FIELD_PARAM_DATA = "fielddata"; + static final String FIELD_PARAM_FORMAT = "format"; + static final String FIELD_PARAM_IGNORE_ABOVE = "ignore_above"; + static final String FIELD_PARAM_IGNORE_MALFORMED = "ignore_malformed"; + static final String FIELD_PARAM_INDEX = "index"; + static final String FIELD_PARAM_INDEX_OPTIONS = "index_options"; + static final String FIELD_PARAM_INDEX_PHRASES = "index_phrases"; + static final String FIELD_PARAM_INDEX_PREFIXES = "index_prefixes"; + static final String FIELD_PARAM_INDEX_PREFIXES_MIN_CHARS = "min_chars"; + static final String FIELD_PARAM_INDEX_PREFIXES_MAX_CHARS = "max_chars"; + static final String FIELD_PARAM_INDEX_ANALYZER = "analyzer"; + static final String FIELD_PARAM_NORMALIZER = "normalizer"; + static final String FIELD_PARAM_NORMS = "norms"; + static final String FIELD_PARAM_NULL_VALUE = "null_value"; + static final String FIELD_PARAMETER_NAME_POSITION_INCREMENT_GAP = "position_increment_gap"; + static final String FIELD_PARAM_SCALING_FACTOR = "scaling_factor"; + static final String FIELD_PARAM_SEARCH_ANALYZER = "search_analyzer"; + static final String FIELD_PARAM_STORE = "store"; + static final String FIELD_PARAM_SIMILARITY = "similarity"; + static final String FIELD_PARAM_TERM_VECTOR = "term_vector"; + static final String FIELD_PARAM_TYPE = "type"; + + private boolean index = true; + private boolean store = false; + private boolean fielddata = false; + private FieldType type = null; + private DateFormat dateFormat = null; + private String datePattern = null; + private String analyzer = null; + private String searchAnalyzer = null; + private String normalizer = null; + private String[] copyTo = null; + private Integer ignoreAbove = null; + private boolean coerce = true; + private boolean docValues = true; + private boolean ignoreMalformed = false; + private IndexOptions indexOptions = null; + boolean indexPhrases = false; + private IndexPrefixes indexPrefixes = null; + private boolean norms = true; + private String nullValue = null; + private Integer positionIncrementGap = null; + private Similarity similarity = Similarity.Default; + private TermVector termVector = TermVector.none; + private double scalingFactor = 1.0; + + /** + * extracts the mapping parameters from the relevant annotations. + * + * @param annotation must not be {@literal null}. + * @return empty Optional if the annotation does not have a conformant type. + */ + public static MappingParameters from(Annotation annotation) { + + Assert.notNull(annotation, "annotation must not be null!"); + + if (annotation instanceof Field) { + return new MappingParameters((Field) annotation); + } else if (annotation instanceof InnerField) { + return new MappingParameters((InnerField) annotation); + } else { + throw new IllegalArgumentException("annotation must be an instance of @Field or @InnerField"); + } + } + + private MappingParameters(Field field) { + index = field.index(); + store = field.store(); + fielddata = field.fielddata(); + type = field.type(); + dateFormat = field.format(); + datePattern = field.pattern(); + analyzer = field.analyzer(); + searchAnalyzer = field.searchAnalyzer(); + normalizer = field.normalizer(); + copyTo = field.copyTo(); + ignoreAbove = field.ignoreAbove() >= 0 ? field.ignoreAbove() : null; + coerce = field.coerce(); + docValues = field.docValues(); + Assert.isTrue(!((type == FieldType.Text || type == FieldType.Nested) && !docValues), + "docValues false is not allowed for field type text"); + ignoreMalformed = field.ignoreMalformed(); + indexOptions = field.indexOptions(); + indexPhrases = field.indexPhrases(); + if (field.indexPrefixes().length > 0) { + indexPrefixes = field.indexPrefixes()[0]; + } + norms = field.norms(); + nullValue = field.nullValue(); + positionIncrementGap = field.positionIncrementGap(); + similarity = field.similarity(); + termVector = field.termVector(); + scalingFactor = field.scalingFactor(); + } + + private MappingParameters(InnerField field) { + index = field.index(); + store = field.store(); + fielddata = field.fielddata(); + type = field.type(); + dateFormat = field.format(); + datePattern = field.pattern(); + analyzer = field.analyzer(); + searchAnalyzer = field.searchAnalyzer(); + normalizer = field.normalizer(); + ignoreAbove = field.ignoreAbove() >= 0 ? field.ignoreAbove() : null; + coerce = field.coerce(); + docValues = field.docValues(); + Assert.isTrue(!((type == FieldType.Text || type == FieldType.Nested) && !docValues), + "docValues false is not allowed for field type text"); + ignoreMalformed = field.ignoreMalformed(); + indexOptions = field.indexOptions(); + indexPhrases = field.indexPhrases(); + if (field.indexPrefixes().length > 0) { + indexPrefixes = field.indexPrefixes()[0]; + } + norms = field.norms(); + nullValue = field.nullValue(); + positionIncrementGap = field.positionIncrementGap(); + similarity = field.similarity(); + termVector = field.termVector(); + scalingFactor = field.scalingFactor(); + } + + public boolean isStore() { + return store; + } + + /** + * writes the different fields to the builder. + * + * @param builder must not be {@literal null}. + */ + public void writeTypeAndParametersTo(XContentBuilder builder) throws IOException { + Assert.notNull(builder, "builder must ot be null"); + + if (fielddata) { + builder.field(FIELD_PARAM_DATA, fielddata); + } + + if (type != FieldType.Auto) { + builder.field(FIELD_PARAM_TYPE, type.name().toLowerCase()); + if (type == FieldType.Date && dateFormat != DateFormat.none) { + builder.field(FIELD_PARAM_FORMAT, dateFormat == DateFormat.custom ? datePattern : dateFormat.toString()); + } + } + + if (!index) { + builder.field(FIELD_PARAM_INDEX, index); + } + + if (!StringUtils.isEmpty(analyzer)) { + builder.field(FIELD_PARAM_INDEX_ANALYZER, analyzer); + } + + if (!StringUtils.isEmpty(searchAnalyzer)) { + builder.field(FIELD_PARAM_SEARCH_ANALYZER, searchAnalyzer); + } + + if (!StringUtils.isEmpty(normalizer)) { + builder.field(FIELD_PARAM_NORMALIZER, normalizer); + } + + if (copyTo != null && copyTo.length > 0) { + builder.field(FIELD_PARAM_COPY_TO, copyTo); + } + + if (ignoreAbove != null) { + Assert.isTrue(ignoreAbove >= 0, "ignore_above must be a positive value"); + builder.field(FIELD_PARAM_IGNORE_ABOVE, ignoreAbove); + } + + if (!coerce) { + builder.field(FIELD_PARAM_COERCE, coerce); + } + + if (!docValues) { + builder.field(FIELD_PARAM_DOC_VALUES, docValues); + } + + if (ignoreMalformed) { + builder.field(FIELD_PARAM_IGNORE_MALFORMED, ignoreMalformed); + } + + if (indexOptions != IndexOptions.none) { + builder.field(FIELD_PARAM_INDEX_OPTIONS, indexOptions); + } + + if (indexPhrases) { + builder.field(FIELD_PARAM_INDEX_PHRASES, indexPhrases); + } + + if (indexPrefixes != null) { + builder.startObject(FIELD_PARAM_INDEX_PREFIXES); + if (indexPrefixes.minChars() != IndexPrefixes.MIN_DEFAULT) { + builder.field(FIELD_PARAM_INDEX_PREFIXES_MIN_CHARS, indexPrefixes.minChars()); + } + if (indexPrefixes.maxChars() != IndexPrefixes.MAX_DEFAULT) { + builder.field(FIELD_PARAM_INDEX_PREFIXES_MAX_CHARS, indexPrefixes.maxChars()); + } + builder.endObject(); + } + + if (!norms) { + builder.field(FIELD_PARAM_NORMS, norms); + } + + if (!StringUtils.isEmpty(nullValue)) { + builder.field(FIELD_PARAM_NULL_VALUE, nullValue); + } + + if (positionIncrementGap != null && positionIncrementGap >= 0) { + builder.field(FIELD_PARAMETER_NAME_POSITION_INCREMENT_GAP, positionIncrementGap); + } + + if (similarity != Similarity.Default) { + builder.field(FIELD_PARAM_SIMILARITY, similarity); + } + + if (termVector != TermVector.none) { + builder.field(FIELD_PARAM_TERM_VECTOR, termVector); + } + + if (type == FieldType.Scaled_Float) { + builder.field(FIELD_PARAM_SCALING_FACTOR, scalingFactor); + } + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/indexmapping/package-info.java b/src/main/java/org/springframework/data/elasticsearch/core/indexmapping/package-info.java new file mode 100644 index 000000000..38d3619c0 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/indexmapping/package-info.java @@ -0,0 +1,5 @@ +/** + * infrastructure to define the Elasticsearch mapping for an index. + */ +@org.springframework.lang.NonNullApi +package org.springframework.data.elasticsearch.core.indexmapping; diff --git a/src/test/java/org/springframework/data/elasticsearch/core/MappingBuilderTests.java b/src/test/java/org/springframework/data/elasticsearch/core/indexmapping/MappingBuilderTests.java similarity index 77% rename from src/test/java/org/springframework/data/elasticsearch/core/MappingBuilderTests.java rename to src/test/java/org/springframework/data/elasticsearch/core/indexmapping/MappingBuilderTests.java index d27103f24..4f334d8b8 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/MappingBuilderTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/indexmapping/MappingBuilderTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.data.elasticsearch.core; +package org.springframework.data.elasticsearch.core.indexmapping; import static org.assertj.core.api.Assertions.*; import static org.elasticsearch.index.query.QueryBuilders.*; @@ -30,6 +30,7 @@ import lombok.Setter; import java.io.IOException; import java.lang.Boolean; +import java.lang.Double; import java.lang.Integer; import java.math.BigDecimal; import java.util.Collection; @@ -49,16 +50,8 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Transient; -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.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.Parent; -import org.springframework.data.elasticsearch.annotations.Setting; +import org.springframework.data.elasticsearch.annotations.*; +import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; import org.springframework.data.elasticsearch.core.completion.Completion; import org.springframework.data.elasticsearch.core.geo.GeoPoint; import org.springframework.data.elasticsearch.core.query.IndexQuery; @@ -125,7 +118,7 @@ public class MappingBuilderTests extends MappingContextBaseTests { public void shouldUseValueFromAnnotationType() throws IOException, JSONException { // Given - String expected = "{\"price\":{\"properties\":{\"price\":{\"store\":false,\"type\":\"double\"}}}}"; + String expected = "{\"price\":{\"properties\":{\"price\":{\"type\":\"double\"}}}}"; // When String mapping = getMappingBuilder().buildPropertyMapping(StockPrice.class); @@ -174,7 +167,7 @@ public class MappingBuilderTests extends MappingContextBaseTests { public void shouldBuildMappingWithSuperclass() throws IOException, JSONException { String expected = "{\"mapping\":{\"properties\":{\"message\":{\"store\":true,\"" - + "type\":\"text\",\"index\":false,\"analyzer\":\"standard\"}" + ",\"createdDate\":{\"store\":false," + + "type\":\"text\",\"index\":false,\"analyzer\":\"standard\"}" + ",\"createdDate\":{" + "\"type\":\"date\",\"index\":false}}}}"; String mapping = getMappingBuilder().buildPropertyMapping(SampleInheritedEntity.class); @@ -329,7 +322,7 @@ public class MappingBuilderTests extends MappingContextBaseTests { // given String expected = "{\"fieldname-type\":{\"properties\":{" + "\"id-property\":{\"type\":\"keyword\",\"index\":true}," - + "\"text-property\":{\"store\":false,\"type\":\"text\"}" + "}}}"; + + "\"text-property\":{\"type\":\"text\"}" + "}}}"; // when String mapping = getMappingBuilder().buildPropertyMapping(FieldNameEntity.TextEntity.class); @@ -371,7 +364,7 @@ public class MappingBuilderTests extends MappingContextBaseTests { // given String expected = "{\"fieldname-type\":{\"properties\":{" + "\"id-property\":{\"type\":\"keyword\",\"index\":true}," - + "\"circular-property\":{\"type\":\"object\",\"properties\":{\"id-property\":{\"store\":false}}}" + "}}}"; + + "\"circular-property\":{\"type\":\"object\",\"properties\":{\"id-property\":{}}}" + "}}}"; // when String mapping = getMappingBuilder().buildPropertyMapping(FieldNameEntity.CircularEntity.class); @@ -385,7 +378,7 @@ public class MappingBuilderTests extends MappingContextBaseTests { // given 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}" + + "\"completion-property\":{\"type\":\"completion\",\"max_input_length\":100,\"preserve_position_increments\":true,\"preserve_separators\":true,\"search_analyzer\":\"simple\",\"analyzer\":\"simple\"},\"completion-property\":{}" + "}}}"; // when @@ -400,7 +393,7 @@ public class MappingBuilderTests extends MappingContextBaseTests { // given 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\"}}}" + + "\"multifield-property\":{\"type\":\"text\",\"analyzer\":\"whitespace\",\"fields\":{\"prefix\":{\"type\":\"text\",\"analyzer\":\"stop\",\"search_analyzer\":\"standard\"}}}" + "}}}"; // when @@ -414,7 +407,7 @@ public class MappingBuilderTests extends MappingContextBaseTests { public void shouldUseIgnoreAbove() throws IOException, JSONException { // given - String expected = "{\"ignore-above-type\":{\"properties\":{\"message\":{\"store\":false,\"type\":\"keyword\",\"ignore_above\":10}}}}"; + String expected = "{\"ignore-above-type\":{\"properties\":{\"message\":{\"type\":\"keyword\",\"ignore_above\":10}}}}"; // when String mapping = getMappingBuilder().buildPropertyMapping(IgnoreAboveEntity.class); @@ -423,6 +416,104 @@ public class MappingBuilderTests extends MappingContextBaseTests { assertEquals(expected, mapping, false); } + @Test + public void shouldSetFieldMappingProperties() throws JSONException, IOException { + String expected = "{\n" + // + " \"fmp\": {\n" + // + " \"properties\": {\n" + // + " \"storeTrue\": {\n" + // + " \"store\": true\n" + // + " },\n" + // + " \"storeFalse\": {},\n" + // + " \"indexTrue\": {},\n" + // + " \"indexFalse\": {\n" + // + " \"index\": false\n" + // + " },\n" + // + " \"coerceTrue\": {},\n" + // + " \"coerceFalse\": {\n" + // + " \"coerce\": false\n" + // + " },\n" + // + " \"fielddataTrue\": {\n" + // + " \"fielddata\": true\n" + // + " },\n" + // + " \"fielddataFalse\": {},\n" + // + " \"type\": {\n" + // + " \"type\": \"integer\"\n" + // + " },\n" + // + " \"ignoreAbove\": {\n" + // + " \"ignore_above\": 42\n" + // + " },\n" + // + " \"copyTo\": {\n" + // + " \"copy_to\": [\"foo\", \"bar\"]\n" + // + " },\n" + // + " \"date\": {\n" + // + " \"type\": \"date\",\n" + // + " \"format\": \"YYYYMMDD\"\n" + // + " },\n" + // + " \"analyzers\": {\n" + // + " \"analyzer\": \"ana\",\n" + // + " \"search_analyzer\": \"sana\",\n" + // + " \"normalizer\": \"norma\"\n" + // + " },\n" + // + " \"docValuesTrue\": {\n" + // + " \"type\": \"keyword\"\n" + // + " },\n" + // + " \"docValuesFalse\": {\n" + // + " \"type\": \"keyword\",\n" + // + " \"doc_values\": false\n" + // + " },\n" + // + " \"ignoreMalformedFalse\": {},\n" + // + " \"ignoreMalformedTrue\": {\n" + // + " \"ignore_malformed\": true\n" + // + " },\n" + // + " \"indexPhrasesTrue\": {\n" + // + " \"index_phrases\": true\n" + // + " },\n" + // + " \"indexPhrasesFalse\": {},\n" + // + " \"indexOptionsNone\": {},\n" + // + " \"indexOptionsPositions\": {\n" + // + " \"index_options\": \"positions\"\n" + // + " },\n" + // + " \"defaultIndexPrefixes\": {\n" + // + " \"index_prefixes\":{}" + // + " },\n" + // + " \"customIndexPrefixes\": {\n" + // + " \"index_prefixes\":{\"min_chars\":1,\"max_chars\":10}" + // + " },\n" + // + " \"normsTrue\": {},\n" + // + " \"normsFalse\": {\n" + // + " \"norms\": false\n" + // + " },\n" + // + " \"nullValueNotSet\": {},\n" + // + " \"nullValueSet\": {\n" + // + " \"null_value\": \"NULLNULL\"\n" + // + " },\n" + // + " \"positionIncrementGap\": {\n" + // + " \"position_increment_gap\": 42\n" + // + " },\n" + // + " \"similarityDefault\": {},\n" + // + " \"similarityBoolean\": {\n" + // + " \"similarity\": \"boolean\"\n" + // + " },\n" + // + " \"termVectorDefault\": {},\n" + // + " \"termVectorWithOffsets\": {\n" + // + " \"term_vector\": \"with_offsets\"\n" + // + " },\n" + // + " \"scaledFloat\": {\n" + // + " \"type\": \"scaled_float\",\n" + // + " \"scaling_factor\": 100.0\n" + // + " }\n" + // + " }\n" + // + " }\n" + // + "}\n"; // + + // when + String mapping = getMappingBuilder().buildPropertyMapping(FieldMappingParameters.class); + + // then + assertEquals(expected, mapping, true); + } + /** * @author Xiao Yu */ @@ -821,4 +912,40 @@ public class MappingBuilderTests extends MappingContextBaseTests { @Field(type = FieldType.Nested, ignoreFields = { "groups" }) private Set users = new HashSet<>(); } + @Document(indexName = "test-index-field-mapping-parameters", type = "fmp") + static class FieldMappingParameters { + @Field private String indexTrue; + @Field(index = false) private String indexFalse; + @Field(store = true) private String storeTrue; + @Field private String storeFalse; + @Field private String coerceTrue; + @Field(coerce = false) private String coerceFalse; + @Field(fielddata = true) private String fielddataTrue; + @Field private String fielddataFalse; + @Field(copyTo = { "foo", "bar" }) private String copyTo; + @Field(ignoreAbove = 42) private String ignoreAbove; + @Field(type = FieldType.Integer) private String type; + @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "YYYYMMDD") private String date; + @Field(analyzer = "ana", searchAnalyzer = "sana", normalizer = "norma") private String analyzers; + @Field(type = Keyword, docValues = true) private String docValuesTrue; + @Field(type = Keyword, docValues = false) private String docValuesFalse; + @Field(ignoreMalformed = true) private String ignoreMalformedTrue; + @Field(ignoreMalformed = false) private String ignoreMalformedFalse; + @Field(indexOptions = IndexOptions.none) private String indexOptionsNone; + @Field(indexOptions = IndexOptions.positions) private String indexOptionsPositions; + @Field(indexPhrases = true) private String indexPhrasesTrue; + @Field(indexPhrases = false) private String indexPhrasesFalse; + @Field(indexPrefixes = @IndexPrefixes) private String defaultIndexPrefixes; + @Field(indexPrefixes = @IndexPrefixes(minChars = 1, maxChars = 10)) private String customIndexPrefixes; + @Field private String normsTrue; + @Field(norms = false) private String normsFalse; + @Field private String nullValueNotSet; + @Field(nullValue = "NULLNULL") private String nullValueSet; + @Field(positionIncrementGap = 42) private String positionIncrementGap; + @Field private String similarityDefault; + @Field(similarity = Similarity.Boolean) private String similarityBoolean; + @Field private String termVectorDefault; + @Field(termVector = TermVector.with_offsets) private String termVectorWithOffsets; + @Field(type = FieldType.Scaled_Float, scalingFactor = 100.0) Double scaledFloat; + } } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/MappingContextBaseTests.java b/src/test/java/org/springframework/data/elasticsearch/core/indexmapping/MappingContextBaseTests.java similarity index 96% rename from src/test/java/org/springframework/data/elasticsearch/core/MappingContextBaseTests.java rename to src/test/java/org/springframework/data/elasticsearch/core/indexmapping/MappingContextBaseTests.java index 98f7513b3..7276b0370 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/MappingContextBaseTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/indexmapping/MappingContextBaseTests.java @@ -13,7 +13,7 @@ * limitations under the License. */ -package org.springframework.data.elasticsearch.core; +package org.springframework.data.elasticsearch.core.indexmapping; import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; diff --git a/src/test/java/org/springframework/data/elasticsearch/core/indexmapping/MappingParametersTest.java b/src/test/java/org/springframework/data/elasticsearch/core/indexmapping/MappingParametersTest.java new file mode 100644 index 000000000..06daa5d69 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/indexmapping/MappingParametersTest.java @@ -0,0 +1,68 @@ +package org.springframework.data.elasticsearch.core.indexmapping; + +import static org.assertj.core.api.Assertions.*; + +import java.lang.annotation.Annotation; + +import org.junit.Test; +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.Score; +import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; + +/** + * @author Peter-Josef Meisch + */ +public class MappingParametersTest extends MappingContextBaseTests { + + private ElasticsearchPersistentEntity entity = elasticsearchConverter.get().getMappingContext() + .getRequiredPersistentEntity(AnnotatedClass.class); + + @Test // DATAES-621 + public void shouldCreateParametersForFieldAnnotation() { + Annotation annotation = entity.getRequiredPersistentProperty("field").findAnnotation(Field.class); + + MappingParameters mappingParameters = MappingParameters.from(annotation); + + assertThat(mappingParameters).isNotNull(); + } + + @Test // DATAES-621 + public void shouldCreateParametersForInnerFieldAnnotation() { + Annotation annotation = entity.getRequiredPersistentProperty("innerField").findAnnotation(InnerField.class); + + MappingParameters mappingParameters = MappingParameters.from(annotation); + + assertThat(mappingParameters).isNotNull(); + } + + @Test // DATAES-621 + public void shouldNotCreateParametersForUnknownAnnotation() { + Annotation annotation = entity.getRequiredPersistentProperty("score").findAnnotation(Score.class); + + assertThatThrownBy(() -> MappingParameters.from(annotation)).isInstanceOf(IllegalArgumentException.class); + } + + @Test // DATAES-621 + public void shouldNotAllowDocValueFalseOnFieldTypeText() { + Annotation annotation = entity.getRequiredPersistentProperty("docValuesText").findAnnotation(Field.class); + + assertThatThrownBy(() -> MappingParameters.from(annotation)).isInstanceOf(IllegalArgumentException.class); + } + + @Test // DATAES-621 + public void shouldNotAllowDocValuesFalseOnFieldTypeNested() { + Annotation annotation = entity.getRequiredPersistentProperty("docValuesNested").findAnnotation(Field.class); + + assertThatThrownBy(() -> MappingParameters.from(annotation)).isInstanceOf(IllegalArgumentException.class); + } + + static class AnnotatedClass { + @Field private String field; + @InnerField(suffix = "test", type = FieldType.Text) private String innerField; + @Score private float score; + @Field(type = FieldType.Text, docValues = false) private String docValuesText; + @Field(type = FieldType.Nested, docValues = false) private String docValuesNested; + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/SimpleDynamicTemplatesMappingTests.java b/src/test/java/org/springframework/data/elasticsearch/core/indexmapping/SimpleDynamicTemplatesMappingTests.java similarity index 98% rename from src/test/java/org/springframework/data/elasticsearch/core/SimpleDynamicTemplatesMappingTests.java rename to src/test/java/org/springframework/data/elasticsearch/core/indexmapping/SimpleDynamicTemplatesMappingTests.java index 2e0d2efea..0b5da9aa3 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/SimpleDynamicTemplatesMappingTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/indexmapping/SimpleDynamicTemplatesMappingTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.elasticsearch.core; +package org.springframework.data.elasticsearch.core.indexmapping; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/org/springframework/data/elasticsearch/core/SimpleElasticsearchDateMappingTests.java b/src/test/java/org/springframework/data/elasticsearch/core/indexmapping/SimpleElasticsearchDateMappingTests.java similarity index 90% rename from src/test/java/org/springframework/data/elasticsearch/core/SimpleElasticsearchDateMappingTests.java rename to src/test/java/org/springframework/data/elasticsearch/core/indexmapping/SimpleElasticsearchDateMappingTests.java index 2e2e164a8..a8701dca4 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/SimpleElasticsearchDateMappingTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/indexmapping/SimpleElasticsearchDateMappingTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.elasticsearch.core; +package org.springframework.data.elasticsearch.core.indexmapping; import static org.assertj.core.api.Assertions.*; import static org.springframework.data.elasticsearch.annotations.FieldType.*; @@ -24,7 +24,6 @@ import java.io.IOException; import java.util.Date; import org.junit.Test; - import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.DateFormat; import org.springframework.data.elasticsearch.annotations.Document; @@ -40,8 +39,8 @@ import org.springframework.data.elasticsearch.annotations.FieldType; 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\":\"text\",\"index\":false,\"analyzer\":\"standard\"},\"customFormatDate\":{\"type\":\"date\",\"format\":\"dd.MM.yyyy hh:mm\"}," + + "\"defaultFormatDate\":{\"type\":\"date\"},\"basicFormatDate\":{\"" + "type\":\"date\",\"format\":\"basic_date\"}}}}"; @Test // DATAES-568