ElasticsearchRepository 跳过空值

ElasticsearchRepository skip null values

我有与 ES 索引交互的 Repo:

@Repository
public interface RegDocumentRepo extends ElasticsearchRepository<RegDocument, String> {
}

RegDocument class 是 reg-document 索引的 POJO:

@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;
}

为了隐藏我的业务逻辑,我使用服务:

@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
    }
  }

但是在我更新排名分数后,我有下一个结构:

{
    "_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
    }
  }

如您所见,Java 服务跳过 NULL 值。但如果该字段是嵌套的,则会保存空值。

ElasticSearch 版本 - 7.8.0

spring-数据的 Maven 依赖项:

<dependency>
   <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-elasticsearch</artifactId>
    <version>4.0.0.RELEASE</version>
</dependency>

那么我怎样才能保存空值而不是跳过它们呢?

**

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);
}

这个方法解释了为什么嵌套数据被保存为 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 ticket

存储内部对象的 null 值,因为当存储键的 Mapnull 值时会发生这种情况。

具有空值的实体属性不会被 Spring 数据 Elasticsearch 不会持久化,因为它会存储 saving/retrieving 数据不需要的信息。

如果您需要写入 null 值,这意味着我们需要为此向 @Field 注释添加一些标志,您可以在 Jira 中添加一个问题吗(https://jira.spring.io/projects/DATAES/issues)为了这个?

编辑: 在版本 4.0.4.RELEASE 和 4.1.0.RC1

中实现