#spring #spring-data-elasticsearch
#spring #spring-data-elasticsearch
Вопрос:
У меня есть репозиторий для взаимодействия с индексом ES:
@Repository
public interface RegDocumentRepo extends ElasticsearchRepository<RegDocument, String> {
}
Класс RegDocument является POJO индекса reg-document:
@Document(indexName = "reg-document")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RegDocument {
@Id
String id;
@Field(type = FieldType.Nested, includeInParent = true)
private List<Map<String, Object>> attachments;
private String author;
@Field(type = FieldType.Nested, includeInParent = true)
private List<Map<String, Object>> classification;
private String content;
private String intent;
@Field(type = FieldType.Nested, includeInParent = true)
private List<Map<String, Object>> links;
private String name;
@Field(name = "publication_date")
private String publicationDate;
private Integer raiting;
private Long status;
private String title;
private String type;
private String version;
}
Чтобы скрыть свою бизнес-логику, я использую service:
@RequiredArgsConstructor
@Service
public class SearchServiceImpl {
@Autowired
RegDocumentRepo regDocumentRepo;
public RegDocument updateRating(String uuid, Integer rating) throws IOException {
final RegDocument regDocument = regDocumentRepo
.findById(uuid)
.orElseThrow(() -> new IOException(String.format("No document with %s id", uuid)));
Integer ratingFromDB = regDocument.getRaiting();
ratingFromDB = ratingFromDB == null ? rating : ratingFromDB rating;
regDocument.setRaiting(ratingFromDB);
final RegDocument save = regDocumentRepo.save(regDocument);
return save;
}
}
Итак, у меня был такой документ в моем индексе ES:
{
"_index" : "reg-document",
"_type" : "_doc",
"_id" : "9wEgQnQBKzq7IqBZMDaO",
"_score" : 1.0,
"_source" : {
"raiting" : null,
"attachments" : null,
"author" : null,
"type" : "answer",
"classification" : [
{
"code" : null,
"level" : null,
"name" : null,
"description" : null,
"id_parent" : null,
"topic_type" : null,
"uuid" : null
}
],
"intent" : null,
"version" : null,
"content" : "В 2019 году размер материнского капитала составляет 453026 рублей",
"name" : "Каков размер МСК в 2019 году?",
"publication_date" : "2020-08-26 06:49:10",
"rowkey" : null,
"links" : null,
"status" : 1
}
}
But after I update my ranking score, I have next structure:
{
"_index" : "reg-document",
"_type" : "_doc",
"_id" : "9wEgQnQBKzq7IqBZMDaO",
"_score" : 1.0,
"_source" : {
"raiting" : 4,
"type" : "answer",
"classification" : [
{
"code" : null,
"level" : null,
"name" : null,
"description" : null,
"id_parent" : null,
"topic_type" : null,
"uuid" : null
}
],
"content" : "В 2019 году размер материнского капитала составляет 453026 рублей",
"name" : "Каков размер МСК в 2019 году?",
"publication_date" : "2020-08-26 06:49:10",
"status" : 1
}
}
As you can see, Java service skip NULL values. But if the field is nested, null values were saved.
ElasticSearch version — 7.8.0
maven dependency for spring-data:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
So how can i SAVE null values, not skip them?
**
UDP
**
Я исследовал зависимость spring-data-elasticsearch-4.0.0 и выяснил, как сказал автор лучшего ответа, что MappingElasticsearchConverter.java
имеет следующие методы:
@Override
public void write(Object source, Document sink) {
Assert.notNull(source, "source to map must not be null");
if (source instanceof Map) {
// noinspection unchecked
sink.putAll((Map<String, Object>) source);
return;
}
Class<?> entityType = ClassUtils.getUserClass(source.getClass());
TypeInformation<?> type = ClassTypeInformation.from(entityType);
if (requiresTypeHint(type, source.getClass(), null)) {
typeMapper.writeType(source.getClass(), sink);
}
Optional<Class<?>> customTarget = conversions.getCustomWriteTarget(entityType, Map.class);
if (customTarget.isPresent()) {
sink.putAll(conversionService.convert(source, Map.class));
return;
}
ElasticsearchPersistentEntity<?> entity = type.getType().equals(entityType)
? mappingContext.getRequiredPersistentEntity(type)
: mappingContext.getRequiredPersistentEntity(entityType);
writeEntity(entity, source, sink, null);
}
Эти методы объясняют, почему вложенные данные были сохранены как нулевые и не были пропущены. Он просто помещается Map
внутрь.
Итак, следующий метод использует отражение таким образом. Так что, если это нулевое значение, просто пропустите его:
protected void writeProperties(ElasticsearchPersistentEntity<?> entity, PersistentPropertyAccessor<?> accessor,
MapValueAccessor sink) {
for (ElasticsearchPersistentProperty property : entity) {
if (!property.isWritable()) {
continue;
}
Object value = accessor.getProperty(property);
if (value == null) {
continue;
}
if (property.hasPropertyConverter()) {
ElasticsearchPersistentPropertyConverter propertyConverter = property.getPropertyConverter();
value = propertyConverter.write(value);
}
if (!isSimpleType(value)) {
writeProperty(property, value, sink);
} else {
Object writeSimpleValue = getWriteSimpleValue(value);
if (writeSimpleValue != null) {
sink.set(property, writeSimpleValue);
}
}
}
}
Официального решения нет. Итак, я создал билет Jira
Комментарии:
1. Вы хотите установить значение ранее ненулевого значения как null? Попробуйте добавить @JsonInclude (содержимое = Включить. ВСЕГДА) в ваш класс.
Ответ №1:
null
Значения внутренних объектов сохраняются, потому что это происходит, когда сохраняются Map
со null
значениями для ключей.
Свойства объекта с нулевым значением не сохраняются в Spring Data Elasticsearch не сохраняются, поскольку это позволило бы хранить информацию, которая не нужна для сохранения / извлечения данных.
Если вам нужно, чтобы null
значения были записаны, это означало бы, что для этого нам нужно добавить какой-либо флаг в @Field
аннотацию, можете ли вы добавить проблему в Jira (https://jira.spring.io/projects/DATAES/issues ) для этого?
Редактировать: реализовано в версиях 4.0.4.RELEASE и 4.1.0.RC1
Комментарии:
1. Дополнил мой вопрос фактами, которые вы предложили