Add enabled mapping parameter.

Original PullRequest #1652 
Closes #1370
This commit is contained in:
Peter-Josef Meisch 2021-01-17 23:54:35 +01:00 committed by GitHub
parent b7a23ed7f1
commit aba14c5e11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 167 additions and 104 deletions

View File

@ -15,7 +15,12 @@
*/ */
package org.springframework.data.elasticsearch.annotations; package org.springframework.data.elasticsearch.annotations;
import java.lang.annotation.*; import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.annotation.Persistent; import org.springframework.data.annotation.Persistent;
/** /**
@ -26,9 +31,15 @@ import org.springframework.data.annotation.Persistent;
@Persistent @Persistent
@Inherited @Inherited
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD}) @Target({ ElementType.TYPE, ElementType.FIELD })
public @interface Mapping { public @interface Mapping {
String mappingPath() default ""; String mappingPath() default "";
/**
* whether mappings are enabled
*
* @since 4.2
*/
boolean enabled() default true;
} }

View File

@ -31,7 +31,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.data.annotation.Transient; import org.springframework.data.annotation.Transient;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.annotations.*; import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.ResourceUtil; import org.springframework.data.elasticsearch.core.ResourceUtil;
@ -66,6 +65,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
*/ */
public class MappingBuilder { public class MappingBuilder {
private static final Logger logger = LoggerFactory.getLogger(ElasticsearchRestTemplate.class);
private static final String FIELD_INDEX = "index"; private static final String FIELD_INDEX = "index";
private static final String FIELD_PROPERTIES = "properties"; private static final String FIELD_PROPERTIES = "properties";
@Deprecated private static final String FIELD_PARENT = "_parent"; @Deprecated private static final String FIELD_PARENT = "_parent";
@ -88,7 +89,7 @@ public class MappingBuilder {
private static final String JOIN_TYPE_RELATIONS = "relations"; private static final String JOIN_TYPE_RELATIONS = "relations";
private static final Logger logger = LoggerFactory.getLogger(ElasticsearchRestTemplate.class); private static final String MAPPING_ENABLED = "enabled";
private final ElasticsearchConverter elasticsearchConverter; private final ElasticsearchConverter elasticsearchConverter;
@ -100,9 +101,9 @@ public class MappingBuilder {
* builds the Elasticsearch mapping for the given clazz. * builds the Elasticsearch mapping for the given clazz.
* *
* @return JSON string * @return JSON string
* @throws ElasticsearchException on errors while building the mapping * @throws MappingException on errors while building the mapping
*/ */
public String buildPropertyMapping(Class<?> clazz) throws ElasticsearchException { public String buildPropertyMapping(Class<?> clazz) throws MappingException {
try { try {
ElasticsearchPersistentEntity<?> entity = elasticsearchConverter.getMappingContext() ElasticsearchPersistentEntity<?> entity = elasticsearchConverter.getMappingContext()
@ -125,8 +126,8 @@ public class MappingBuilder {
.close(); .close();
return builder.getOutputStream().toString(); return builder.getOutputStream().toString();
} catch (MappingException | IOException e) { } catch (IOException e) {
throw new ElasticsearchException("could not build mapping", e); throw new MappingException("could not build mapping", e);
} }
} }
@ -134,6 +135,14 @@ public class MappingBuilder {
boolean isRootObject, String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType, boolean isRootObject, String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType,
@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.getRequiredAnnotation(Mapping.class).enabled()) {
builder.field(MAPPING_ENABLED, false);
return;
}
}
boolean writeNestedProperties = !isRootObject && (isAnyPropertyAnnotatedWithField(entity) || nestedOrObjectField); boolean writeNestedProperties = !isRootObject && (isAnyPropertyAnnotatedWithField(entity) || nestedOrObjectField);
if (writeNestedProperties) { if (writeNestedProperties) {
@ -189,14 +198,22 @@ public class MappingBuilder {
if (property.isAnnotationPresent(Mapping.class)) { if (property.isAnnotationPresent(Mapping.class)) {
String mappingPath = property.getRequiredAnnotation(Mapping.class).mappingPath(); Mapping mapping = property.getRequiredAnnotation(Mapping.class);
if (!StringUtils.isEmpty(mappingPath)) {
ClassPathResource mappings = new ClassPathResource(mappingPath); if (mapping.enabled()) {
if (mappings.exists()) { String mappingPath = mapping.mappingPath();
builder.rawField(property.getFieldName(), mappings.getInputStream(), XContentType.JSON);
return; if (StringUtils.hasText(mappingPath)) {
ClassPathResource mappings = new ClassPathResource(mappingPath);
if (mappings.exists()) {
builder.rawField(property.getFieldName(), mappings.getInputStream(), XContentType.JSON);
return;
}
} }
} else {
applyDisabledPropertyMapping(builder, property);
return;
} }
} }
@ -317,9 +334,29 @@ public class MappingBuilder {
private void applyDefaultIdFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property) private void applyDefaultIdFieldMapping(XContentBuilder builder, ElasticsearchPersistentProperty property)
throws IOException { throws IOException {
builder.startObject(property.getFieldName()) //
.field(FIELD_PARAM_TYPE, TYPE_VALUE_KEYWORD) //
.field(FIELD_INDEX, true) //
.endObject(); //
}
builder.startObject(property.getFieldName()).field(FIELD_PARAM_TYPE, TYPE_VALUE_KEYWORD).field(FIELD_INDEX, true) private void applyDisabledPropertyMapping(XContentBuilder builder, ElasticsearchPersistentProperty property)
.endObject(); throws IOException {
try {
Field field = property.getRequiredAnnotation(Field.class);
if (field.type() != FieldType.Object) {
throw new IllegalArgumentException("Field type must be 'object");
}
builder.startObject(property.getFieldName()) //
.field(FIELD_PARAM_TYPE, field.type().name().toLowerCase()) //
.field(MAPPING_ENABLED, false) //
.endObject(); //
} catch (Exception e) {
throw new MappingException("Could not write enabled: false mapping for " + property.getFieldName(), e);
}
} }
/** /**

View File

@ -30,6 +30,7 @@ import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import java.lang.Integer; import java.lang.Integer;
import java.lang.Object;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -268,9 +269,26 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
indexOps.putMapping(); indexOps.putMapping();
} }
/** @Test // #1370
* @author Xiao Yu @DisplayName("should write mapping for disabled entity")
*/ void shouldWriteMappingForDisabledEntity() {
IndexOperations indexOps = operations.indexOps(DisabledMappingEntity.class);
indexOps.create();
indexOps.putMapping();
indexOps.delete();
}
@Test // #1370
@DisplayName("should write mapping for disabled property")
void shouldWriteMappingForDisabledProperty() {
IndexOperations indexOps = operations.indexOps(DisabledMappingProperty.class);
indexOps.create();
indexOps.putMapping();
indexOps.delete();
}
@Setter @Setter
@Getter @Getter
@NoArgsConstructor @NoArgsConstructor
@ -284,9 +302,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
@Field(type = FieldType.Keyword, ignoreAbove = 10) private String message; @Field(type = FieldType.Keyword, ignoreAbove = 10) private String message;
} }
/**
* @author Peter-Josef Meisch
*/
static class FieldNameEntity { static class FieldNameEntity {
@Document(indexName = "fieldname-index") @Document(indexName = "fieldname-index")
@ -351,11 +366,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
} }
} }
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Nordine Bittich
*/
@Setter @Setter
@Getter @Getter
@NoArgsConstructor @NoArgsConstructor
@ -373,10 +383,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
searchAnalyzer = "standard") }) private String description; searchAnalyzer = "standard") }) private String description;
} }
/**
* @author Stuart Stevenson
* @author Mohsin Husen
*/
@Data @Data
@Document(indexName = "test-index-simple-recursive-mapping-builder", replicas = 0, refreshInterval = "-1") @Document(indexName = "test-index-simple-recursive-mapping-builder", replicas = 0, refreshInterval = "-1")
static class SimpleRecursiveEntity { static class SimpleRecursiveEntity {
@ -386,9 +392,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
ignoreFields = { "circularObject" }) private SimpleRecursiveEntity circularObject; ignoreFields = { "circularObject" }) private SimpleRecursiveEntity circularObject;
} }
/**
* @author Sascha Woo
*/
@Setter @Setter
@Getter @Getter
@NoArgsConstructor @NoArgsConstructor
@ -406,9 +409,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
@Field(type = FieldType.Keyword) private String name; @Field(type = FieldType.Keyword) private String name;
} }
/**
* @author Sascha Woo
*/
@Setter @Setter
@Getter @Getter
@NoArgsConstructor @NoArgsConstructor
@ -426,10 +426,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
type = FieldType.Keyword, normalizer = "lower_case_normalizer") }) private String description; type = FieldType.Keyword, normalizer = "lower_case_normalizer") }) private String description;
} }
/**
* @author Rizwan Idrees
* @author Mohsin Husen
*/
static class Author { static class Author {
@Nullable private String id; @Nullable private String id;
@ -454,9 +450,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
} }
} }
/**
* @author Kevin Leturc
*/
@Document(indexName = "test-index-sample-inherited-mapping-builder", replicas = 0, refreshInterval = "-1") @Document(indexName = "test-index-sample-inherited-mapping-builder", replicas = 0, refreshInterval = "-1")
static class SampleInheritedEntity extends AbstractInheritedEntity { static class SampleInheritedEntity extends AbstractInheritedEntity {
@ -472,9 +465,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
} }
} }
/**
* @author Kevin Leturc
*/
static class SampleInheritedEntityBuilder { static class SampleInheritedEntityBuilder {
private final SampleInheritedEntity result; private final SampleInheritedEntity result;
@ -506,10 +496,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
} }
} }
/**
* @author Artur Konczak
* @author Mohsin Husen
*/
@Setter @Setter
@Getter @Getter
@NoArgsConstructor @NoArgsConstructor
@ -525,9 +511,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
@Field(type = FieldType.Double) private BigDecimal price; @Field(type = FieldType.Double) private BigDecimal price;
} }
/**
* @author Kevin Letur
*/
static class AbstractInheritedEntity { static class AbstractInheritedEntity {
@Nullable @Id private String id; @Nullable @Id private String id;
@ -580,9 +563,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
orientation = GeoShapeField.Orientation.clockwise) private String shape2; orientation = GeoShapeField.Orientation.clockwise) private String shape2;
} }
/**
* Created by akonczak on 21/08/2016.
*/
@Document(indexName = "test-index-user-mapping-builder") @Document(indexName = "test-index-user-mapping-builder")
static class User { static class User {
@Nullable @Id private String id; @Nullable @Id private String id;
@ -590,9 +570,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
@Field(type = FieldType.Nested, ignoreFields = { "users" }) private Set<Group> groups = new HashSet<>(); @Field(type = FieldType.Nested, ignoreFields = { "users" }) private Set<Group> groups = new HashSet<>();
} }
/**
* Created by akonczak on 21/08/2016.
*/
@Document(indexName = "test-index-group-mapping-builder") @Document(indexName = "test-index-group-mapping-builder")
static class Group { static class Group {
@ -662,4 +639,20 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
@Nullable @Field(type = Wildcard) private String wildcardWithoutParams; @Nullable @Field(type = Wildcard) private String wildcardWithoutParams;
@Nullable @Field(type = Wildcard, nullValue = "WILD", ignoreAbove = 42) private String wildcardWithParams; @Nullable @Field(type = Wildcard, nullValue = "WILD", ignoreAbove = 42) private String wildcardWithParams;
} }
@Data
@Document(indexName = "disabled-entity-mapping")
@Mapping(enabled = false)
static class DisabledMappingEntity {
@Id private String id;
@Field(type = Text) private String text;
}
@Data
@Document(indexName = "disabled-property-mapping")
static class DisabledMappingProperty {
@Id private String id;
@Field(type = Text) private String text;
@Mapping(enabled = false) @Field(type = Object) private Object object;
}
} }

View File

@ -54,6 +54,7 @@ import org.springframework.data.geo.Box;
import org.springframework.data.geo.Circle; import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Point; import org.springframework.data.geo.Point;
import org.springframework.data.geo.Polygon; import org.springframework.data.geo.Polygon;
import org.springframework.data.mapping.MappingException;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
/** /**
@ -504,9 +505,49 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
assertEquals(expected, mapping, false); assertEquals(expected, mapping, false);
} }
/** @Test // #1370
* @author Xiao Yu @DisplayName("should not write mapping when enabled is false on entity")
*/ void shouldNotWriteMappingWhenEnabledIsFalseOnEntity() throws JSONException {
String expected = "{\n" + //
" \"enabled\": false" + //
"}";
String mapping = getMappingBuilder().buildPropertyMapping(DisabledMappingEntity.class);
assertEquals(expected, mapping, false);
}
@Test // #1370
@DisplayName("should write disabled property mapping")
void shouldWriteDisabledPropertyMapping() throws JSONException {
String expected = "{\n" + //
" \"properties\":{\n" + //
" \"text\": {\n" + //
" \"type\": \"text\"\n" + //
" },\n" + //
" \"object\": {\n" + //
" \"type\": \"object\",\n" + //
" \"enabled\": false\n" + //
" }\n" + //
" }\n" + //
"}\n"; //
String mapping = getMappingBuilder().buildPropertyMapping(DisabledMappingProperty.class);
assertEquals(expected, mapping, false);
}
@Test // #1370
@DisplayName("should only allow disabled properties on type object")
void shouldOnlyAllowDisabledPropertiesOnTypeObject() {
assertThatThrownBy(() ->
getMappingBuilder().buildPropertyMapping(InvalidDisabledMappingProperty.class)
).isInstanceOf(MappingException.class);
}
@Setter @Setter
@Getter @Getter
@NoArgsConstructor @NoArgsConstructor
@ -520,9 +561,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
@Field(type = FieldType.Keyword, ignoreAbove = 10) private String message; @Field(type = FieldType.Keyword, ignoreAbove = 10) private String message;
} }
/**
* @author Peter-Josef Meisch
*/
static class FieldNameEntity { static class FieldNameEntity {
@Document(indexName = "fieldname-index") @Document(indexName = "fieldname-index")
@ -587,11 +625,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
} }
} }
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Nordine Bittich
*/
@Setter @Setter
@Getter @Getter
@NoArgsConstructor @NoArgsConstructor
@ -609,10 +642,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
searchAnalyzer = "standard") }) private String description; searchAnalyzer = "standard") }) private String description;
} }
/**
* @author Stuart Stevenson
* @author Mohsin Husen
*/
@Data @Data
@Document(indexName = "test-index-simple-recursive-mapping-builder", replicas = 0, refreshInterval = "-1") @Document(indexName = "test-index-simple-recursive-mapping-builder", replicas = 0, refreshInterval = "-1")
static class SimpleRecursiveEntity { static class SimpleRecursiveEntity {
@ -622,9 +651,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
ignoreFields = { "circularObject" }) private SimpleRecursiveEntity circularObject; ignoreFields = { "circularObject" }) private SimpleRecursiveEntity circularObject;
} }
/**
* @author Sascha Woo
*/
@Setter @Setter
@Getter @Getter
@NoArgsConstructor @NoArgsConstructor
@ -642,9 +668,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
@Field(type = FieldType.Keyword) private String name; @Field(type = FieldType.Keyword) private String name;
} }
/**
* @author Sascha Woo
*/
@Setter @Setter
@Getter @Getter
@NoArgsConstructor @NoArgsConstructor
@ -662,10 +685,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
type = FieldType.Keyword, normalizer = "lower_case_normalizer") }) private String description; type = FieldType.Keyword, normalizer = "lower_case_normalizer") }) private String description;
} }
/**
* @author Rizwan Idrees
* @author Mohsin Husen
*/
static class Author { static class Author {
@Nullable private String id; @Nullable private String id;
@ -690,9 +709,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
} }
} }
/**
* @author Kevin Leturc
*/
@Document(indexName = "test-index-sample-inherited-mapping-builder", replicas = 0, refreshInterval = "-1") @Document(indexName = "test-index-sample-inherited-mapping-builder", replicas = 0, refreshInterval = "-1")
static class SampleInheritedEntity extends AbstractInheritedEntity { static class SampleInheritedEntity extends AbstractInheritedEntity {
@ -708,10 +724,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
} }
} }
/**
* @author Artur Konczak
* @author Mohsin Husen
*/
@Setter @Setter
@Getter @Getter
@NoArgsConstructor @NoArgsConstructor
@ -727,9 +739,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
@Field(type = FieldType.Double) private BigDecimal price; @Field(type = FieldType.Double) private BigDecimal price;
} }
/**
* @author Kevin Letur
*/
static class AbstractInheritedEntity { static class AbstractInheritedEntity {
@Nullable @Id private String id; @Nullable @Id private String id;
@ -755,9 +764,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
} }
} }
/**
* @author Jakub Vavrik
*/
@Document(indexName = "test-index-recursive-mapping-mapping-builder", replicas = 0, refreshInterval = "-1") @Document(indexName = "test-index-recursive-mapping-mapping-builder", replicas = 0, refreshInterval = "-1")
static class SampleTransientEntity { static class SampleTransientEntity {
@ -836,9 +842,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
orientation = GeoShapeField.Orientation.clockwise) private String shape2; orientation = GeoShapeField.Orientation.clockwise) private String shape2;
} }
/**
* Created by akonczak on 21/08/2016.
*/
@Document(indexName = "test-index-user-mapping-builder") @Document(indexName = "test-index-user-mapping-builder")
static class User { static class User {
@Nullable @Id private String id; @Nullable @Id private String id;
@ -846,9 +849,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
@Field(type = FieldType.Nested, ignoreFields = { "users" }) private Set<Group> groups = new HashSet<>(); @Field(type = FieldType.Nested, ignoreFields = { "users" }) private Set<Group> groups = new HashSet<>();
} }
/**
* Created by akonczak on 21/08/2016.
*/
@Document(indexName = "test-index-group-mapping-builder") @Document(indexName = "test-index-group-mapping-builder")
static class Group { static class Group {
@ -961,4 +961,26 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
@Field(type = FieldType.Rank_Feature, positiveScoreImpact = false) private Integer urlLength; @Field(type = FieldType.Rank_Feature, positiveScoreImpact = false) private Integer urlLength;
@Field(type = FieldType.Rank_Features) private Map<String, Integer> topics; @Field(type = FieldType.Rank_Features) private Map<String, Integer> topics;
} }
@Data
@Mapping(enabled = false)
static class DisabledMappingEntity {
@Id private String id;
@Field(type = Text) private String text;
}
@Data
static class InvalidDisabledMappingProperty {
@Id private String id;
@Mapping(enabled = false)
@Field(type = Text) private String text;
}
@Data
static class DisabledMappingProperty {
@Id private String id;
@Field(type = Text) private String text;
@Mapping(enabled = false)
@Field(type = Object) private Object object;
}
} }