ElasticsearchRepository пропускает нулевые значения

#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. Дополнил мой вопрос фактами, которые вы предложили