Add option to specify if empty property should not be sent to Elasticsearch.

Original Pull Request #2482
Closes #2290
This commit is contained in:
Peter-Josef Meisch 2023-02-25 17:50:12 +01:00 committed by GitHub
parent b4c3e25a60
commit e7c9bf20f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 151 additions and 1 deletions

View File

@ -210,4 +210,12 @@ public @interface Field {
* @since 4.3 * @since 4.3
*/ */
boolean excludeFromSource() default false; boolean excludeFromSource() default false;
/**
* when this field is a {{@link String}}, a {{@link java.util.Collection}} or a {{@link java.util.Map}} that is empty
* this property controlls whether the empty value is sent to Elasticsearch.
*
* @since 5.1
*/
boolean storeEmptyValue() default true;
} }

View File

@ -960,6 +960,10 @@ public class MappingElasticsearchConverter
continue; continue;
} }
if (!property.storeEmptyValue() && hasEmptyValue(value)) {
continue;
}
if (property.hasPropertyValueConverter()) { if (property.hasPropertyValueConverter()) {
value = propertyConverterWrite(property, value); value = propertyConverterWrite(property, value);
sink.set(property, value); sink.set(property, value);
@ -988,6 +992,16 @@ public class MappingElasticsearchConverter
} }
} }
private static boolean hasEmptyValue(Object value) {
if (value instanceof String s && s.isEmpty() || value instanceof Collection<?> c && c.isEmpty()
|| value instanceof Map<?, ?> m && m.isEmpty()) {
return true;
}
return false;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void writeProperty(ElasticsearchPersistentProperty property, Object value, MapValueAccessor sink) { protected void writeProperty(ElasticsearchPersistentProperty property, Object value, MapValueAccessor sink) {

View File

@ -74,6 +74,13 @@ public interface ElasticsearchPersistentProperty extends PersistentProperty<Elas
*/ */
boolean storeNullValue(); boolean storeNullValue();
/**
* @return true if empty values ({{@link String}}, or {{@link java.util.Collection}} or {{@link java.util.Map}})
* should be store in Elasticsearch.
* @since 5.1
*/
boolean storeEmptyValue();
/** /**
* @return {@literal true} if this is a GeoPoint property * @return {@literal true} if this is a GeoPoint property
* @since 4.1 * @since 4.1

View File

@ -82,6 +82,7 @@ public class SimpleElasticsearchPersistentProperty extends
private final @Nullable String annotatedFieldName; private final @Nullable String annotatedFieldName;
@Nullable private PropertyValueConverter propertyValueConverter; @Nullable private PropertyValueConverter propertyValueConverter;
private final boolean storeNullValue; private final boolean storeNullValue;
private final boolean storeEmptyValue;
public SimpleElasticsearchPersistentProperty(Property property, public SimpleElasticsearchPersistentProperty(Property property,
PersistentEntity<?, ElasticsearchPersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) { PersistentEntity<?, ElasticsearchPersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {
@ -106,6 +107,7 @@ public class SimpleElasticsearchPersistentProperty extends
initPropertyValueConverter(); initPropertyValueConverter();
storeNullValue = isField && getRequiredAnnotation(Field.class).storeNullValue(); storeNullValue = isField && getRequiredAnnotation(Field.class).storeNullValue();
storeEmptyValue = isField ? getRequiredAnnotation(Field.class).storeEmptyValue() : true;
} }
@Override @Override
@ -134,6 +136,11 @@ public class SimpleElasticsearchPersistentProperty extends
return storeNullValue; return storeNullValue;
} }
@Override
public boolean storeEmptyValue() {
return storeEmptyValue;
}
protected boolean hasExplicitFieldName() { protected boolean hasExplicitFieldName() {
return StringUtils.hasText(getAnnotatedFieldName()); return StringUtils.hasText(getAnnotatedFieldName());
} }

View File

@ -1893,6 +1893,35 @@ public class MappingElasticsearchConverterUnitTests {
assertEquals(expected, json, true); assertEquals(expected, json, true);
} }
@Test // #2290
@DisplayName("should respect field setting for empty properties")
void shouldRespectFieldSettingForEmptyProperties() throws JSONException {
@Language("JSON")
var expected = """
{
"_class": "org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverterUnitTests$EntityWithPropertiesThatMightBeEmpty",
"id": "42",
"stringToWriteWhenEmpty": "",
"listToWriteWhenEmpty": [],
"mapToWriteWhenEmpty": {}
}
""";
var entity = new EntityWithPropertiesThatMightBeEmpty();
entity.setId("42");
entity.setStringToWriteWhenEmpty("");
entity.setStringToNotWriteWhenEmpty("");
entity.setListToWriteWhenEmpty(emptyList());
entity.setListToNotWriteWhenEmpty(emptyList());
entity.setMapToWriteWhenEmpty(emptyMap());
entity.setMapToNotWriteWhenEmpty(emptyMap());
Document document = Document.create();
mappingElasticsearchConverter.write(entity, document);
String json = document.toJson();
assertEquals(expected, json, true);
}
// region entities // region entities
public static class Sample { public static class Sample {
@Nullable public @ReadOnlyProperty String readOnly; @Nullable public @ReadOnlyProperty String readOnly;
@ -2974,6 +3003,91 @@ public class MappingElasticsearchConverterUnitTests {
this.text = text; this.text = text;
} }
} }
static class EntityWithPropertiesThatMightBeEmpty {
@Nullable private String id;
@Field(type = FieldType.Text)
@Nullable private String stringToWriteWhenEmpty;
@Field(type = FieldType.Text, storeEmptyValue = false)
@Nullable private String stringToNotWriteWhenEmpty;
@Field(type = FieldType.Nested)
@Nullable private List<String> listToWriteWhenEmpty;
@Field(type = FieldType.Nested, storeEmptyValue = false)
@Nullable private List<String> listToNotWriteWhenEmpty;
@Field(type = FieldType.Nested)
@Nullable private Map<String, String> mapToWriteWhenEmpty;
@Field(type = FieldType.Nested, storeEmptyValue = false)
@Nullable private Map<String, String> mapToNotWriteWhenEmpty;
@Nullable
public String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
@Nullable
public String getStringToWriteWhenEmpty() {
return stringToWriteWhenEmpty;
}
public void setStringToWriteWhenEmpty(@Nullable String stringToWriteWhenEmpty) {
this.stringToWriteWhenEmpty = stringToWriteWhenEmpty;
}
@Nullable
public String getStringToNotWriteWhenEmpty() {
return stringToNotWriteWhenEmpty;
}
public void setStringToNotWriteWhenEmpty(@Nullable String stringToNotWriteWhenEmpty) {
this.stringToNotWriteWhenEmpty = stringToNotWriteWhenEmpty;
}
@Nullable
public List<String> getListToWriteWhenEmpty() {
return listToWriteWhenEmpty;
}
public void setListToWriteWhenEmpty(@Nullable List<String> listToWriteWhenEmpty) {
this.listToWriteWhenEmpty = listToWriteWhenEmpty;
}
@Nullable
public List<String> getListToNotWriteWhenEmpty() {
return listToNotWriteWhenEmpty;
}
public void setListToNotWriteWhenEmpty(@Nullable List<String> listToNotWriteWhenEmpty) {
this.listToNotWriteWhenEmpty = listToNotWriteWhenEmpty;
}
@Nullable
public Map<String, String> getMapToWriteWhenEmpty() {
return mapToWriteWhenEmpty;
}
public void setMapToWriteWhenEmpty(@Nullable Map<String, String> mapToWriteWhenEmpty) {
this.mapToWriteWhenEmpty = mapToWriteWhenEmpty;
}
@Nullable
public Map<String, String> getMapToNotWriteWhenEmpty() {
return mapToNotWriteWhenEmpty;
}
public void setMapToNotWriteWhenEmpty(@Nullable Map<String, String> mapToNotWriteWhenEmpty) {
this.mapToNotWriteWhenEmpty = mapToNotWriteWhenEmpty;
}
}
// endregion // endregion
private static String reverse(Object o) { private static String reverse(Object o) {