mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-05-31 09:12:11 +00:00
datatype detection support in mapping.
Original Pull Request #1810 Closes #638
This commit is contained in:
parent
df0d65eda2
commit
38b1763b34
@ -46,6 +46,16 @@ class Entity {
|
|||||||
<.> `sortModes`, `sortOrders` and `sortMissingValues` are optional, but if they are set, the number of entries must match the number of `sortFields` elements
|
<.> `sortModes`, `sortOrders` and `sortMissingValues` are optional, but if they are set, the number of entries must match the number of `sortFields` elements
|
||||||
====
|
====
|
||||||
|
|
||||||
|
[[elasticsearch.misc.mappings]]
|
||||||
|
== Index Mapping
|
||||||
|
|
||||||
|
When Spring Data Elasticsearch creates the index mapping with the `IndexOperations.createMapping()` methods, it uses the annotations described in <<elasticsearch.mapping.meta-model.annotations>>, especially the `@Field` annotation. In addition to that it is possible to add the `@Mapping` annotation to a class. This annotation has the following properties:
|
||||||
|
|
||||||
|
* `mappingPath` a classpath resource in JSON format which is used as the mapping, no other mapping processing is done.
|
||||||
|
* `enabled` when set to false, this flag is written to the mapping and no further processing is done.
|
||||||
|
* `dateDetection` and `numericDetection` set the corresponding properties in the mapping when not set to `DEFAULT`.
|
||||||
|
* `dynamicDateFormats` when this String array is not empty, it defines the date formats used for automatic date detection.
|
||||||
|
|
||||||
[[elasticsearch.misc.filter]]
|
[[elasticsearch.misc.filter]]
|
||||||
== Filter Builder
|
== Filter Builder
|
||||||
|
|
||||||
|
@ -9,19 +9,17 @@ import java.lang.annotation.Target;
|
|||||||
import org.springframework.data.annotation.Persistent;
|
import org.springframework.data.annotation.Persistent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Elasticsearch dynamic templates mapping.
|
* Elasticsearch dynamic templates mapping. This annotation is handy if you prefer apply dynamic templates on fields
|
||||||
* This annotation is handy if you prefer apply dynamic templates on fields with annotation e.g. {@link Field}
|
* with annotation e.g. {@link Field} with type = FieldType.Object etc. instead of static mapping on Document via
|
||||||
* with type = FieldType.Object etc. instead of static mapping on Document via {@link Mapping} annotation.
|
* {@link Mapping} annotation. DynamicTemplates annotation is omitted if {@link Mapping} annotation is used.
|
||||||
* DynamicTemplates annotation is omitted if {@link Mapping} annotation is used.
|
|
||||||
*
|
*
|
||||||
* @author Petr Kukral
|
* @author Petr Kukral
|
||||||
*/
|
*/
|
||||||
@Persistent
|
@Persistent
|
||||||
@Inherited
|
@Inherited
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target({ElementType.TYPE})
|
@Target({ ElementType.TYPE })
|
||||||
public @interface DynamicTemplates {
|
public @interface DynamicTemplates {
|
||||||
|
|
||||||
String mappingPath() default "";
|
String mappingPath() default "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,8 +38,31 @@ public @interface Mapping {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* whether mappings are enabled
|
* whether mappings are enabled
|
||||||
*
|
*
|
||||||
* @since 4.2
|
* @since 4.2
|
||||||
*/
|
*/
|
||||||
boolean enabled() default true;
|
boolean enabled() default true;
|
||||||
|
/**
|
||||||
|
* whether date_detection is enabled
|
||||||
|
*
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
Detection dateDetection() default Detection.DEFAULT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* whether numeric_detection is enabled
|
||||||
|
*
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
Detection numericDetection() default Detection.DEFAULT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* custom dynamic date formats
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
String[] dynamicDateFormats() default {};
|
||||||
|
|
||||||
|
enum Detection {
|
||||||
|
DEFAULT, TRUE, FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,9 @@ public class MappingBuilder {
|
|||||||
private static final String JOIN_TYPE_RELATIONS = "relations";
|
private static final String JOIN_TYPE_RELATIONS = "relations";
|
||||||
|
|
||||||
private static final String MAPPING_ENABLED = "enabled";
|
private static final String MAPPING_ENABLED = "enabled";
|
||||||
|
private static final String DATE_DETECTION = "date_detection";
|
||||||
|
private static final String NUMERIC_DETECTION = "numeric_detection";
|
||||||
|
private static final String DYNAMIC_DATE_FORMATS = "dynamic_date_formats";
|
||||||
|
|
||||||
private final ElasticsearchConverter elasticsearchConverter;
|
private final ElasticsearchConverter elasticsearchConverter;
|
||||||
|
|
||||||
@ -147,11 +150,24 @@ public class MappingBuilder {
|
|||||||
@Nullable Field parentFieldAnnotation, @Nullable DynamicMapping dynamicMapping) throws IOException {
|
@Nullable Field parentFieldAnnotation, @Nullable DynamicMapping dynamicMapping) throws IOException {
|
||||||
|
|
||||||
if (entity != null && entity.isAnnotationPresent(Mapping.class)) {
|
if (entity != null && entity.isAnnotationPresent(Mapping.class)) {
|
||||||
|
Mapping mappingAnnotation = entity.getRequiredAnnotation(Mapping.class);
|
||||||
|
|
||||||
if (!entity.getRequiredAnnotation(Mapping.class).enabled()) {
|
if (!mappingAnnotation.enabled()) {
|
||||||
builder.field(MAPPING_ENABLED, false);
|
builder.field(MAPPING_ENABLED, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mappingAnnotation.dateDetection() != Mapping.Detection.DEFAULT) {
|
||||||
|
builder.field(DATE_DETECTION, Boolean.parseBoolean(mappingAnnotation.dateDetection().name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mappingAnnotation.numericDetection() != Mapping.Detection.DEFAULT) {
|
||||||
|
builder.field(NUMERIC_DETECTION, Boolean.parseBoolean(mappingAnnotation.numericDetection().name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mappingAnnotation.dynamicDateFormats().length > 0) {
|
||||||
|
builder.field(DYNAMIC_DATE_FORMATS, mappingAnnotation.dynamicDateFormats());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean writeNestedProperties = !isRootObject && (isAnyPropertyAnnotatedWithField(entity) || nestedOrObjectField);
|
boolean writeNestedProperties = !isRootObject && (isAnyPropertyAnnotatedWithField(entity) || nestedOrObjectField);
|
||||||
|
@ -306,6 +306,17 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
|||||||
indexOps.delete();
|
indexOps.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // #638
|
||||||
|
@DisplayName("should write dynamic detection values")
|
||||||
|
void shouldWriteDynamicDetectionValues() {
|
||||||
|
|
||||||
|
IndexOperations indexOps = operations.indexOps(DynamicDetectionMapping.class);
|
||||||
|
indexOps.create();
|
||||||
|
indexOps.putMapping();
|
||||||
|
indexOps.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// region entities
|
||||||
@Document(indexName = "ignore-above-index")
|
@Document(indexName = "ignore-above-index")
|
||||||
static class IgnoreAboveEntity {
|
static class IgnoreAboveEntity {
|
||||||
@Nullable @Id private String id;
|
@Nullable @Id private String id;
|
||||||
@ -1113,4 +1124,12 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Document(indexName = "dynamic-detection-mapping-true")
|
||||||
|
@Mapping(dateDetection = Mapping.Detection.TRUE, numericDetection = Mapping.Detection.TRUE,
|
||||||
|
dynamicDateFormats = { "MM/dd/yyyy" })
|
||||||
|
private static class DynamicDetectionMapping {
|
||||||
|
@Id @Nullable private String id;
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -774,6 +774,87 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
|
|||||||
assertEquals(expected, mapping, true);
|
assertEquals(expected, mapping, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // #638
|
||||||
|
@DisplayName("should not write dynamic detection mapping entries in default setting")
|
||||||
|
void shouldNotWriteDynamicDetectionMappingEntriesInDefaultSetting() throws JSONException {
|
||||||
|
|
||||||
|
String expected = "{\n" + //
|
||||||
|
" \"properties\": {\n" + //
|
||||||
|
" \"_class\": {\n" + //
|
||||||
|
" \"type\": \"keyword\",\n" + //
|
||||||
|
" \"index\": false,\n" + //
|
||||||
|
" \"doc_values\": false\n" + //
|
||||||
|
" }\n" + //
|
||||||
|
" }\n" + //
|
||||||
|
"}"; //
|
||||||
|
|
||||||
|
String mapping = getMappingBuilder().buildPropertyMapping(DynamicDetectionMappingDefault.class);
|
||||||
|
|
||||||
|
assertEquals(expected, mapping, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // #638
|
||||||
|
@DisplayName("should write dynamic detection mapping entries when set to false")
|
||||||
|
void shouldWriteDynamicDetectionMappingEntriesWhenSetToFalse() throws JSONException {
|
||||||
|
|
||||||
|
String expected = "{\n" + //
|
||||||
|
" \"date_detection\": false," + //
|
||||||
|
" \"numeric_detection\": false," + //
|
||||||
|
" \"properties\": {\n" + //
|
||||||
|
" \"_class\": {\n" + //
|
||||||
|
" \"type\": \"keyword\",\n" + //
|
||||||
|
" \"index\": false,\n" + //
|
||||||
|
" \"doc_values\": false\n" + //
|
||||||
|
" }\n" + //
|
||||||
|
" }\n" + //
|
||||||
|
"}"; //
|
||||||
|
|
||||||
|
String mapping = getMappingBuilder().buildPropertyMapping(DynamicDetectionMappingFalse.class);
|
||||||
|
|
||||||
|
assertEquals(expected, mapping, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // #638
|
||||||
|
@DisplayName("should write dynamic detection mapping entries when set to true")
|
||||||
|
void shouldWriteDynamicDetectionMappingEntriesWhenSetToTrue() throws JSONException {
|
||||||
|
|
||||||
|
String expected = "{\n" + //
|
||||||
|
" \"date_detection\": true," + //
|
||||||
|
" \"numeric_detection\": true," + //
|
||||||
|
" \"properties\": {\n" + //
|
||||||
|
" \"_class\": {\n" + //
|
||||||
|
" \"type\": \"keyword\",\n" + //
|
||||||
|
" \"index\": false,\n" + //
|
||||||
|
" \"doc_values\": false\n" + //
|
||||||
|
" }\n" + //
|
||||||
|
" }\n" + //
|
||||||
|
"}"; //
|
||||||
|
|
||||||
|
String mapping = getMappingBuilder().buildPropertyMapping(DynamicDetectionMappingTrue.class);
|
||||||
|
|
||||||
|
assertEquals(expected, mapping, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // #638
|
||||||
|
@DisplayName("should write dynamic date formats")
|
||||||
|
void shouldWriteDynamicDateFormats() throws JSONException {
|
||||||
|
|
||||||
|
String expected = "{\n" + //
|
||||||
|
" \"dynamic_date_formats\": [\"date1\",\"date2\"]," + //
|
||||||
|
" \"properties\": {\n" + //
|
||||||
|
" \"_class\": {\n" + //
|
||||||
|
" \"type\": \"keyword\",\n" + //
|
||||||
|
" \"index\": false,\n" + //
|
||||||
|
" \"doc_values\": false\n" + //
|
||||||
|
" }\n" + //
|
||||||
|
" }\n" + //
|
||||||
|
"}"; //
|
||||||
|
|
||||||
|
String mapping = getMappingBuilder().buildPropertyMapping(DynamicDateFormatsMapping.class);
|
||||||
|
|
||||||
|
assertEquals(expected, mapping, true);
|
||||||
|
}
|
||||||
|
|
||||||
// region entities
|
// region entities
|
||||||
@Document(indexName = "ignore-above-index")
|
@Document(indexName = "ignore-above-index")
|
||||||
static class IgnoreAboveEntity {
|
static class IgnoreAboveEntity {
|
||||||
@ -1690,5 +1771,28 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
|
|||||||
@Field(type = Text) @Nullable private String title;
|
@Field(type = Text) @Nullable private String title;
|
||||||
@Field(type = Nested) @Nullable private List<Author> authors;
|
@Field(type = Nested) @Nullable private List<Author> authors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Document(indexName = "dynamic-field-mapping-default")
|
||||||
|
private static class DynamicDetectionMappingDefault {
|
||||||
|
@Id @Nullable private String id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Document(indexName = "dynamic-dateformats-mapping")
|
||||||
|
@Mapping(dynamicDateFormats = {"date1", "date2"})
|
||||||
|
private static class DynamicDateFormatsMapping {
|
||||||
|
@Id @Nullable private String id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Document(indexName = "dynamic-detection-mapping-true")
|
||||||
|
@Mapping(dateDetection = Mapping.Detection.TRUE, numericDetection = Mapping.Detection.TRUE)
|
||||||
|
private static class DynamicDetectionMappingTrue {
|
||||||
|
@Id @Nullable private String id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Document(indexName = "dynamic-detection-mapping-false")
|
||||||
|
@Mapping(dateDetection = Mapping.Detection.FALSE, numericDetection = Mapping.Detection.FALSE)
|
||||||
|
private static class DynamicDetectionMappingFalse {
|
||||||
|
@Id @Nullable private String id;
|
||||||
|
}
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user