mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-05-30 00:32:12 +00:00
Add runtime fields to index mapping.
Original Pull Request: #1820 Closes: #1816
This commit is contained in:
parent
25b323c00d
commit
0836411d45
@ -51,10 +51,24 @@ class Entity {
|
||||
|
||||
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.
|
||||
* `mappingPath` a classpath resource in JSON format; if this is not empty it 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.
|
||||
* `runtimeFieldsPath` a classpath resource in JSON format containing the definition of runtime fields which is written to the index mappings, for example:
|
||||
====
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"day_of_week": {
|
||||
"type": "keyword",
|
||||
"script": {
|
||||
"source": "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT))"
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[elasticsearch.misc.filter]]
|
||||
== Filter Builder
|
||||
|
@ -27,6 +27,7 @@ import org.springframework.data.annotation.Persistent;
|
||||
* Elasticsearch Mapping
|
||||
*
|
||||
* @author Mohsin Husen
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@Persistent
|
||||
@Inherited
|
||||
@ -42,6 +43,7 @@ public @interface Mapping {
|
||||
* @since 4.2
|
||||
*/
|
||||
boolean enabled() default true;
|
||||
|
||||
/**
|
||||
* whether date_detection is enabled
|
||||
*
|
||||
@ -58,10 +60,20 @@ public @interface Mapping {
|
||||
|
||||
/**
|
||||
* custom dynamic date formats
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
String[] dynamicDateFormats() default {};
|
||||
|
||||
/**
|
||||
* classpath to a JSON file containing the values for a runtime mapping definition. The file must contain the JSON
|
||||
* object that is written as the value of the runtime property. {@see <a href=
|
||||
* "https://www.elastic.co/guide/en/elasticsearch/reference/7.12/runtime-mapping-fields.html">elasticsearch doc</a>}
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
String runtimeFieldsPath() default "";
|
||||
|
||||
enum Detection {
|
||||
DEFAULT, TRUE, FALSE;
|
||||
}
|
||||
|
@ -219,8 +219,6 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
|
||||
if (hasText(mappings)) {
|
||||
return Document.parse(mappings);
|
||||
}
|
||||
} else {
|
||||
LOGGER.info("mappingPath in @Mapping has to be defined. Building mappings using @Field");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,6 +95,7 @@ public class MappingBuilder {
|
||||
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 static final String RUNTIME = "runtime";
|
||||
|
||||
private final ElasticsearchConverter elasticsearchConverter;
|
||||
|
||||
@ -168,6 +169,10 @@ public class MappingBuilder {
|
||||
if (mappingAnnotation.dynamicDateFormats().length > 0) {
|
||||
builder.field(DYNAMIC_DATE_FORMATS, mappingAnnotation.dynamicDateFormats());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(mappingAnnotation.runtimeFieldsPath())) {
|
||||
addRuntimeFields(builder, mappingAnnotation.runtimeFieldsPath());
|
||||
}
|
||||
}
|
||||
|
||||
boolean writeNestedProperties = !isRootObject && (isAnyPropertyAnnotatedWithField(entity) || nestedOrObjectField);
|
||||
@ -222,6 +227,15 @@ public class MappingBuilder {
|
||||
|
||||
}
|
||||
|
||||
private void addRuntimeFields(XContentBuilder builder, String runtimeFieldsPath) throws IOException {
|
||||
|
||||
ClassPathResource runtimeFields = new ClassPathResource(runtimeFieldsPath);
|
||||
|
||||
if (runtimeFields.exists()) {
|
||||
builder.rawField(RUNTIME, runtimeFields.getInputStream(), XContentType.JSON);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildPropertyMapping(XContentBuilder builder, boolean isRootObject,
|
||||
ElasticsearchPersistentProperty property) throws IOException {
|
||||
|
||||
|
@ -25,6 +25,7 @@ import static org.springframework.data.elasticsearch.utils.IndexBuilder.*;
|
||||
import java.lang.Integer;
|
||||
import java.lang.Object;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@ -316,6 +317,16 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
indexOps.delete();
|
||||
}
|
||||
|
||||
@Test // #1816
|
||||
@DisplayName("should write runtime fields")
|
||||
void shouldWriteRuntimeFields() {
|
||||
|
||||
IndexOperations indexOps = operations.indexOps(RuntimeFieldEntity.class);
|
||||
indexOps.create();
|
||||
indexOps.putMapping();
|
||||
indexOps.delete();
|
||||
}
|
||||
|
||||
// region entities
|
||||
@Document(indexName = "ignore-above-index")
|
||||
static class IgnoreAboveEntity {
|
||||
@ -1130,6 +1141,14 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
private static class DynamicDetectionMapping {
|
||||
@Id @Nullable private String id;
|
||||
}
|
||||
|
||||
@Document(indexName = "runtime-fields")
|
||||
@Mapping(runtimeFieldsPath = "/mappings/runtime-fields.json")
|
||||
private static class RuntimeFieldEntity {
|
||||
@Id @Nullable private String id;
|
||||
@Field(type = Date, format = DateFormat.epoch_millis, name = "@timestamp") @Nullable private Instant timestamp;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import java.lang.Double;
|
||||
import java.lang.Integer;
|
||||
import java.lang.Object;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
@ -855,7 +856,38 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
|
||||
assertEquals(expected, mapping, true);
|
||||
}
|
||||
|
||||
@Test // #1816
|
||||
@DisplayName("should write runtime fields")
|
||||
void shouldWriteRuntimeFields() throws JSONException {
|
||||
|
||||
String expected = "{\n" + //
|
||||
" \"runtime\": {\n" + //
|
||||
" \"day_of_week\": {\n" + //
|
||||
" \"type\": \"keyword\",\n" + //
|
||||
" \"script\": {\n" + //
|
||||
" \"source\": \"emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT))\"\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" },\n" + //
|
||||
" \"properties\": {\n" + //
|
||||
" \"_class\": {\n" + //
|
||||
" \"type\": \"keyword\",\n" + //
|
||||
" \"index\": false,\n" + //
|
||||
" \"doc_values\": false\n" + //
|
||||
" },\n" + //
|
||||
" \"@timestamp\": {\n" + //
|
||||
" \"type\": \"date\",\n" + //
|
||||
" \"format\": \"epoch_millis\"\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
"}\n"; //
|
||||
|
||||
String mapping = getMappingBuilder().buildPropertyMapping(RuntimeFieldEntity.class);
|
||||
|
||||
assertEquals(expected, mapping, true);
|
||||
}
|
||||
// region entities
|
||||
|
||||
@Document(indexName = "ignore-above-index")
|
||||
static class IgnoreAboveEntity {
|
||||
@Nullable @Id private String id;
|
||||
@ -1778,7 +1810,7 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
|
||||
}
|
||||
|
||||
@Document(indexName = "dynamic-dateformats-mapping")
|
||||
@Mapping(dynamicDateFormats = {"date1", "date2"})
|
||||
@Mapping(dynamicDateFormats = { "date1", "date2" })
|
||||
private static class DynamicDateFormatsMapping {
|
||||
@Id @Nullable private String id;
|
||||
}
|
||||
@ -1794,5 +1826,12 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
|
||||
private static class DynamicDetectionMappingFalse {
|
||||
@Id @Nullable private String id;
|
||||
}
|
||||
|
||||
@Document(indexName = "runtime-fields")
|
||||
@Mapping(runtimeFieldsPath = "/mappings/runtime-fields.json")
|
||||
private static class RuntimeFieldEntity {
|
||||
@Id @Nullable private String id;
|
||||
@Field(type = Date, format = DateFormat.epoch_millis, name = "@timestamp") @Nullable private Instant timestamp;
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
8
src/test/resources/mappings/runtime-fields.json
Normal file
8
src/test/resources/mappings/runtime-fields.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"day_of_week": {
|
||||
"type": "keyword",
|
||||
"script": {
|
||||
"source": "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT))"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user