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
值,因为当存储键的 Map
和 null
值时会发生这种情况。
具有空值的实体属性不会被 Spring 数据 Elasticsearch 不会持久化,因为它会存储 saving/retrieving 数据不需要的信息。
如果您需要写入 null
值,这意味着我们需要为此向 @Field
注释添加一些标志,您可以在 Jira 中添加一个问题吗(https://jira.spring.io/projects/DATAES/issues)为了这个?
编辑: 在版本 4.0.4.RELEASE 和 4.1.0.RC1
中实现
我有与 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
值,因为当存储键的 Map
和 null
值时会发生这种情况。
具有空值的实体属性不会被 Spring 数据 Elasticsearch 不会持久化,因为它会存储 saving/retrieving 数据不需要的信息。
如果您需要写入 null
值,这意味着我们需要为此向 @Field
注释添加一些标志,您可以在 Jira 中添加一个问题吗(https://jira.spring.io/projects/DATAES/issues)为了这个?
编辑: 在版本 4.0.4.RELEASE 和 4.1.0.RC1
中实现