You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Java API Client与Spring Data Elasticsearch设文档属性为null

问题:Spring Data Elasticsearch Java API Client Upsert时保留Map的Null值

场景与需求

我有一个包含Todo类型Map的Schedule文档,结构如下:

{
  "_index": "schedule-index",
  "_id": "sched-id",
  "_source": {
    "id": "sched-id",
    "todos": {
      "todo-id-1": {
        "id": "todo-id-1",
        "name": "laundry"
      },
      "todo-id-2": {
        "id": "todo-id-2",
        "name": "dishes"
      }
    }
  }
}

需要将其中一个Todo值设为null并保留对应的key,期望结果如下:

{
  "_index": "schedule-index",
  "_id": "sched-id",
  "_source": {
    "id": "sched-id",
    "todos": {
      "todo-id-1": null, // <-- 设置为NULL
      "todo-id-2": {
        "id": "todo-id-2",
        "name": "dishes"
      }
    }
  }
}

旧实现(RestHighLevelClient)正常工作

使用RestHighLevelClient时可实现该功能,代码如下:

// 使用HighLevelRestClient
public BulkResponse upsert(List<Schedule> schedules) {
  // 'schedules' 包含1个id为sched-id的schedule,其中todo-id-1的值为null
  BulkRequest bulkRequest = new BulkRequest();

  schedules.forEach(schedule-> {
    UpdateRequest updateRequest = new UpdateRequest("schedule-index", schedule.getId())
      .docAsUpsert(true)
      .doc(asJson(schedule), XContentType.JSON);
    bulkRequest.add(updateRequest);
  });

  BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
  return response;
}

生成的请求如下:

{
  "update": {
    "_index": "schedule-index",
    "_id": "sched-id"
  }
}
{
  "doc_as_upsert": true,
  "doc": {
    "id": "sched-id",
    "todos": {
      "todo-id-1": null    // <----- 设置为NULL
    }
  }
}

新实现(Java API Client)出现问题

切换为Spring Data Elasticsearch中的Java API Client后,生成的请求会忽略null值,map变为空:

// 使用Java API Client
public BulkResponse upsert(List<Schedule> schedules) {
  // 'schedules' 包含1个id为sched-id的schedule,其中todo-id-1的值为null
  BulkRequest.Builder br = new BulkRequest.Builder();

  schedules.forEach(schedule-> {
    br.operations(op -> op 
      .update(ur -> ur
        .index("schedule-index")
        .id(schedule.getId())
        .action(a -> a
          .docAsUpsert(true)
          .doc(schedule)
        )));
  });

  return client.bulk(br.build());
}

生成的请求如下:

{
  "update": {
    "_index": "schedule-index",
    "_id": "sched-id"
  }
}
{
  "doc_as_upsert": true,
  "doc": {
    "id": "sched-id",
    "todos": {      // <----- TODO MAP为空
    }
  }
}

补充说明:此问题类似根属性被忽略的情况,但这里是Map内部的null值被过滤。


解决方案

要让Java API Client在upsert时保留Map中的null值,核心是修改JSON序列化策略,因为默认策略会自动过滤null字段。以下两种方式可实现需求:

方式1:全局配置序列化规则

在配置Elasticsearch Java Client时,设置JsonMapper的序列化规则,强制包含null值:

@Bean
public ElasticsearchClient elasticsearchClient(RestClient restClient) {
    JsonpMapper jsonpMapper = new JacksonJsonpMapper(
        Jackson2ObjectMapperBuilder.json()
            .serializationInclusion(JsonInclude.Include.ALWAYS) // 始终包含null字段
            .build()
    );
    return new ElasticsearchClient(
        new RestClientTransport(restClient, jsonpMapper)
    );
}

该方式全局生效,所有使用此客户端的序列化操作都会保留null值。

方式2:针对单个请求单独处理

如果不想全局修改,可在构建Bulk请求时手动序列化实体,生成包含null值的JSON:

public BulkResponse upsert(List<Schedule> schedules) throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS); // 临时设置包含null

    BulkRequest.Builder br = new BulkRequest.Builder();

    schedules.forEach(schedule-> {
        // 手动序列化实体为包含null的JSON字符串
        String scheduleJson = objectMapper.writeValueAsString(schedule);
        JsonData doc = JsonData.fromJson(scheduleJson);

        br.operations(op -> op 
            .update(ur -> ur
                .index("schedule-index")
                .id(schedule.getId())
                .action(a -> a
                    .docAsUpsert(true)
                    .doc(doc) // 使用手动序列化的JsonData
                )));
    });

    return client.bulk(br.build());
}

该方式仅对当前请求生效,不会影响其他业务的序列化行为。

原理说明

Java API Client默认使用的Jackson序列化策略是JsonInclude.Include.NON_NULL,会自动过滤null值字段。通过修改为Include.ALWAYS,可强制输出所有字段(包括值为null的Map条目),这样Elasticsearch就能接收到包含null值的Map,从而实现保留key并设置值为null的需求。


内容的提问来源于stack exchange,提问作者Seanimus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 07:59:53