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;
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;
/**
@ -26,9 +31,15 @@ import org.springframework.data.annotation.Persistent;
@Persistent
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
@Target({ ElementType.TYPE, ElementType.FIELD })
public @interface Mapping {
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.springframework.core.io.ClassPathResource;
import org.springframework.data.annotation.Transient;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.ResourceUtil;
@ -66,6 +65,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
*/
public class MappingBuilder {
private static final Logger logger = LoggerFactory.getLogger(ElasticsearchRestTemplate.class);
private static final String FIELD_INDEX = "index";
private static final String FIELD_PROPERTIES = "properties";
@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 Logger logger = LoggerFactory.getLogger(ElasticsearchRestTemplate.class);
private static final String MAPPING_ENABLED = "enabled";
private final ElasticsearchConverter elasticsearchConverter;
@ -100,9 +101,9 @@ public class MappingBuilder {
* builds the Elasticsearch mapping for the given clazz.
*
* @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 {
ElasticsearchPersistentEntity<?> entity = elasticsearchConverter.getMappingContext()
@ -125,8 +126,8 @@ public class MappingBuilder {
.close();
return builder.getOutputStream().toString();
} catch (MappingException | IOException e) {
throw new ElasticsearchException("could not build mapping", e);
} catch (IOException e) {
throw new MappingException("could not build mapping", e);
}
}
@ -134,6 +135,14 @@ public class MappingBuilder {
boolean isRootObject, String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType,
@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);
if (writeNestedProperties) {
@ -189,14 +198,22 @@ public class MappingBuilder {
if (property.isAnnotationPresent(Mapping.class)) {
String mappingPath = property.getRequiredAnnotation(Mapping.class).mappingPath();
if (!StringUtils.isEmpty(mappingPath)) {
Mapping mapping = property.getRequiredAnnotation(Mapping.class);
ClassPathResource mappings = new ClassPathResource(mappingPath);
if (mappings.exists()) {
builder.rawField(property.getFieldName(), mappings.getInputStream(), XContentType.JSON);
return;
if (mapping.enabled()) {
String mappingPath = mapping.mappingPath();
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)
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)
.endObject();
private void applyDisabledPropertyMapping(XContentBuilder builder, ElasticsearchPersistentProperty property)
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 java.lang.Integer;
import java.lang.Object;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
@ -268,9 +269,26 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
indexOps.putMapping();
}
/**
* @author Xiao Yu
*/
@Test // #1370
@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
@Getter
@NoArgsConstructor
@ -284,9 +302,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
@Field(type = FieldType.Keyword, ignoreAbove = 10) private String message;
}
/**
* @author Peter-Josef Meisch
*/
static class FieldNameEntity {
@Document(indexName = "fieldname-index")
@ -351,11 +366,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
}
}
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Nordine Bittich
*/
@Setter
@Getter
@NoArgsConstructor
@ -373,10 +383,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
searchAnalyzer = "standard") }) private String description;
}
/**
* @author Stuart Stevenson
* @author Mohsin Husen
*/
@Data
@Document(indexName = "test-index-simple-recursive-mapping-builder", replicas = 0, refreshInterval = "-1")
static class SimpleRecursiveEntity {
@ -386,9 +392,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
ignoreFields = { "circularObject" }) private SimpleRecursiveEntity circularObject;
}
/**
* @author Sascha Woo
*/
@Setter
@Getter
@NoArgsConstructor
@ -406,9 +409,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
@Field(type = FieldType.Keyword) private String name;
}
/**
* @author Sascha Woo
*/
@Setter
@Getter
@NoArgsConstructor
@ -426,10 +426,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
type = FieldType.Keyword, normalizer = "lower_case_normalizer") }) private String description;
}
/**
* @author Rizwan Idrees
* @author Mohsin Husen
*/
static class Author {
@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")
static class SampleInheritedEntity extends AbstractInheritedEntity {
@ -472,9 +465,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
}
}
/**
* @author Kevin Leturc
*/
static class SampleInheritedEntityBuilder {
private final SampleInheritedEntity result;
@ -506,10 +496,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
}
}
/**
* @author Artur Konczak
* @author Mohsin Husen
*/
@Setter
@Getter
@NoArgsConstructor
@ -525,9 +511,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
@Field(type = FieldType.Double) private BigDecimal price;
}
/**
* @author Kevin Letur
*/
static class AbstractInheritedEntity {
@Nullable @Id private String id;
@ -580,9 +563,6 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
orientation = GeoShapeField.Orientation.clockwise) private String shape2;
}
/**
* Created by akonczak on 21/08/2016.
*/
@Document(indexName = "test-index-user-mapping-builder")
static class User {
@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<>();
}
/**
* Created by akonczak on 21/08/2016.
*/
@Document(indexName = "test-index-group-mapping-builder")
static class Group {
@ -662,4 +639,20 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
@Nullable @Field(type = Wildcard) private String wildcardWithoutParams;
@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.Point;
import org.springframework.data.geo.Polygon;
import org.springframework.data.mapping.MappingException;
import org.springframework.lang.Nullable;
/**
@ -504,9 +505,49 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
assertEquals(expected, mapping, false);
}
/**
* @author Xiao Yu
*/
@Test // #1370
@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
@Getter
@NoArgsConstructor
@ -520,9 +561,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
@Field(type = FieldType.Keyword, ignoreAbove = 10) private String message;
}
/**
* @author Peter-Josef Meisch
*/
static class FieldNameEntity {
@Document(indexName = "fieldname-index")
@ -587,11 +625,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
}
}
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Nordine Bittich
*/
@Setter
@Getter
@NoArgsConstructor
@ -609,10 +642,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
searchAnalyzer = "standard") }) private String description;
}
/**
* @author Stuart Stevenson
* @author Mohsin Husen
*/
@Data
@Document(indexName = "test-index-simple-recursive-mapping-builder", replicas = 0, refreshInterval = "-1")
static class SimpleRecursiveEntity {
@ -622,9 +651,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
ignoreFields = { "circularObject" }) private SimpleRecursiveEntity circularObject;
}
/**
* @author Sascha Woo
*/
@Setter
@Getter
@NoArgsConstructor
@ -642,9 +668,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
@Field(type = FieldType.Keyword) private String name;
}
/**
* @author Sascha Woo
*/
@Setter
@Getter
@NoArgsConstructor
@ -662,10 +685,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
type = FieldType.Keyword, normalizer = "lower_case_normalizer") }) private String description;
}
/**
* @author Rizwan Idrees
* @author Mohsin Husen
*/
static class Author {
@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")
static class SampleInheritedEntity extends AbstractInheritedEntity {
@ -708,10 +724,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
}
}
/**
* @author Artur Konczak
* @author Mohsin Husen
*/
@Setter
@Getter
@NoArgsConstructor
@ -727,9 +739,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
@Field(type = FieldType.Double) private BigDecimal price;
}
/**
* @author Kevin Letur
*/
static class AbstractInheritedEntity {
@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")
static class SampleTransientEntity {
@ -836,9 +842,6 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
orientation = GeoShapeField.Orientation.clockwise) private String shape2;
}
/**
* Created by akonczak on 21/08/2016.
*/
@Document(indexName = "test-index-user-mapping-builder")
static class User {
@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<>();
}
/**
* Created by akonczak on 21/08/2016.
*/
@Document(indexName = "test-index-group-mapping-builder")
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_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;
}
}