【问题标题】:Storing Document with generic JsonNode in ElasticSearch using Spring Data使用 Spring Data 在 ElasticSearch 中使用通用 JsonNode 存储文档
【发布时间】:2021-03-18 07:09:39
【问题描述】:

我想使用带有以下实体的 Spring Data 将未指定的 json 有效负载数据存储在 Elasticsearch 索引中

    @Document(indexName = "message")
    public class Message {
    
        @Id
        private String id;
        private JsonNode payload;

        //getters and setters here
    }

有效负载各不相同,需要以通用方式存储,也可以轻松再次加载,这就是我想在这里使用 JsonNode 的原因。

写入了带有“id”的文档,但“payload”字段为空。

当我在 Kibana 中查找写入索引的文档时,它看起来像这样:

_class:
    com.tryout.Message
payload:
id:
    30243006-0844-4438-a7f0-db93518b340f
_id:
    30243006-0844-4438-a7f0-db93518b340f
_type:
    _doc
_index:
    message
_score:
    0 

在索引映射中“有效负载”也没有创建,它看起来像这样:

{
  "mappings": {
    "_doc": {
      "properties": {
        "_class": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "id": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        }
      }
    }
  }
}

任何想法如何存储我的通用有效负载?

(我使用的是 Elastic v7.9.2 & Spring Boot + spring-boot-data-jpa v2.3.5 和 spring-data-elasticsearch v4.1.1)

【问题讨论】:

  • 你有用于有效载荷的 getter/setter 吗?
  • 是的,getter/setter 在那里。我只是把它简化为这里的必需品
  • 有效载荷字段的映射是什么?你想搜索通用字符串中的字段还是只存储?
  • 我想在字段上进行搜索。我也已经尝试添加 @Field(type = Object) 注释,它强制对象映射但仍然不会存储 json
  • 我刚刚看了JsonNode(来自杰克逊?)。此类没有属性,因此 Spring Data Elasticsearch 可以编写的 JsonNode 中没有任何内容。你需要一个自定义的 Converter 能够从 JsonNode 转换为 Map<String, Object> 并返回。

标签: json spring elasticsearch spring-data spring-data-elasticsearch


【解决方案1】:

我在将 spring-data-elastic-search 版本升级到 4 后遇到了这个问题,不幸的是我没有找到明确的答案。阅读spring data elasticsearch documentation,我发现了这个:

从 4.0 版开始,仅使用 Meta Object Mapping,不再使用基于 Jackson 的映射器,而是使用 MappingElasticsearchConverter。

经过几天的搜索,我终于以这种方式解决了我的问题,希望它可以帮助其他人。

p.s : JsonNode 可以包含 jsonArray 或 JsonObject,所以这两种数据类型应该在读/写时处理。

class JsonElasticSearchConverter extends MappingElasticsearchConverter {

    private CustomConversions conversions = new ElasticsearchCustomConversions(Collections.emptyList());

    CustomElasticSearchConverter(MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) {
        super(mappingContext);
        setConversions(conversions);
    }

    CustomElasticSearchConverter(MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext, GenericConversionService conversionService) {
        super(mappingContext, conversionService);
        setConversions(conversions);
    }

    @Override
    protected <R> R readValue(@Nullable Object source, ElasticsearchPersistentProperty property,
                              TypeInformation<R> targetType) {

        if (source == null) {
            return null;
        }

        if (targetType.getType() == JsonNode.class) {
            return (R) mapStoredValueToJsonNode(source);
        }

        return super.readValue(source, property, targetType);
    }

    @Override
    protected Object getWriteComplexValue(ElasticsearchPersistentProperty property, TypeInformation<?> typeHint, Object value) {
        if (typeHint.getType() == JsonNode.class) {
            return getWriteJsonNodeValue(value);
        }
        return super.getWriteComplexValue(property, typeHint, value);
    }

    private JsonNode mapStoredValueToJsonNode(Object source) {
        JsonNode jsonNode = null;
        ObjectMapper mapper = new ObjectMapper();
        if (source instanceof ArrayList) {
            ArrayNode array = mapper.valueToTree(source);
            jsonNode = mapper.valueToTree(array);
        }
        if (source instanceof HashMap) {
            jsonNode = mapper.convertValue(source, JsonNode.class);
        }
        return jsonNode;
    }

    private Object getWriteJsonNodeValue(Object value) {
        JsonNode jsonNode = null;
        ObjectMapper mapper = new ObjectMapper();
        if (value instanceof ObjectNode)
            try {
                jsonNode = mapper.readTree(value.toString());
            } catch (IOException shouldNotHappened) {
                //log.warn("This error should not happened" + shouldNotHappened.getMessage());
            }
        else if (value instanceof ArrayNode) {
            ArrayNode array = mapper.valueToTree(value);
            jsonNode = mapper.valueToTree(array);
        }
        return jsonNode;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-25
    • 2015-04-16
    • 2021-06-10
    • 1970-01-01
    相关资源
    最近更新 更多