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