elasticsearch - 如何使用Spring Data Elasticsearch正确格式化日期

标签 elasticsearch date-format spring-data-elasticsearch

我正在将SpringBoot 2.2.5与Elasticsearch 6.8.6一起使用。我正在从Spring Data Jest迁移到通过ElasticsearchEntityMapper使用Spring Data Elasticsearch REST传输机制。

我有一个带有以下定义的Date字段:

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
private Date date;

我想要这样存储在Elasticsearch中的日期:
"date": "2020-04-02T14:49:05.672+0000"

当我启动应用程序时,将创建索引,但是当我尝试保存实体时,出现以下异常:
Caused by: org.elasticsearch.client.ResponseException: method [POST], host [http://localhost:9200], URI [/trends/estrend?timeout=1m], status line [HTTP/1.1 400 Bad Request]
{"error":{"root_cause":[{"type":"mapper_parsing_exception","reason":"failed to parse field [date] of type [date] in document with id 'rS5UP3EB9eKtCTMXW_Ky'"}],"type":"mapper_parsing_exception","reason":"failed to parse field [date] of type [date] in document with id 'rS5UP3EB9eKtCTMXW_Ky'","caused_by":{"type":"illegal_argument_exception","reason":"Invalid format: \"1585905425266\" is malformed at \"5266\""}},"status":400}

关于我做错了什么以及应该如何解决的任何指示?

下面的配置和实体定义:
@Configuration
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {

    @Value("${spring.data.elasticsearch.host}")
    private String elasticSearchHost;

    @Value("${spring.data.elasticsearch.port}")
    private String elasticSearchPort;

    @Bean
    public RestHighLevelClient elasticsearchClient() {
        final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
        .connectedTo(elasticSearchHost + ":" + elasticSearchPort)
        .usingSsl()
        .build();
        return RestClients.create(clientConfiguration).rest();
    }

    @Bean
    public EntityMapper entityMapper() {
        ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(), new DefaultConversionService());
        entityMapper.setConversions(elasticsearchCustomConversions());
        return entityMapper;
    }
}


package com.es.test;

import java.util.Date;
import java.util.UUID;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

@Document(indexName = "trends")
public class EsTrend {

    @Id
    private UUID id;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
    @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
    private Date date;

    private String entityOrRelationshipId;

    // getter and setters

}

更新:

如果禁用ElasticsearchEntityMapper bean,则不会出现异常,并且日期会以正确的格式写入Elasticsearch。我还需要为ElasticsearchEntityMapper配置什么吗?

最佳答案

首先,请不要使用基于Jackson的默认映射器。在Spring Data Elasticsearch的下一个主要版本(4.0)中将其删除。这样就没有可用的选择,并且在内部使用了ElasticsearchEntityMapper

关于您的问题:Spring Boot当前使用的3.2版中的ElasticsearchEntityMapper并不使用@Field属性中的日期相关信息来转换实体,它仅用于创建索引映射。这是缺少的功能或错误,已在下一个主要版本中修复,整个映射过程在那里进行了全面检查。

在当前情况下可以做什么:您需要添加自定义转换器。您可以在配置类中执行以下操作:

@Configuration
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {

    private static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");

    @Value("${spring.data.elasticsearch.host}")
    private String elasticSearchHost;

    @Value("${spring.data.elasticsearch.port}")
    private String elasticSearchPort;

    @Bean
    public RestHighLevelClient elasticsearchClient() {
        final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
            .connectedTo(elasticSearchHost + ":" + elasticSearchPort)
            .usingSsl()
            .build();
        return RestClients.create(clientConfiguration).rest();
    }

    @Bean
    public EntityMapper entityMapper() {
        ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(), new DefaultConversionService());
        entityMapper.setConversions(elasticsearchCustomConversions());
        return entityMapper;
    }

    @Override
    public ElasticsearchCustomConversions elasticsearchCustomConversions() {
        return new ElasticsearchCustomConversions(Arrays.asList(DateToStringConverter.INSTANCE, StringToDateConverter.INSTANCE));
    }

    @WritingConverter
    enum DateToStringConverter implements Converter<Date, String> {
        INSTANCE;
        @Override
        public String convert(Date date) {
            return formatter.format(date);
        }
    }

    @ReadingConverter
    enum StringToDateConverter implements Converter<String, Date> {
        INSTANCE;
        @Override
        public Date convert(String s) {
            try {
                return formatter.parse(s);
            } catch (ParseException e) {
                return null;
            }
        }
    }
}

但是,您仍然需要在@Field注释中使用dateformat,因为创建正确的索引映射是必需的。

并且您应该更改代码以使用Java 8引入的时间类,例如LocalDateLocalDateTime,Spring Data Elasticsearch支持开箱即用的时间类,而java.util.Date将需要自定义转换器。

编辑09.04.2020:添加了必要的@WritingConverter@ReadingConverter批注。

编辑19.04.2020: Spring Data Elasticsearch 4.0将同时支持带有java.util.Date批注的@Field类。

关于elasticsearch - 如何使用Spring Data Elasticsearch正确格式化日期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61008881/

相关文章:

java - 如何使用 spring boot 2.15 正确配置 elasticsearch 7.1.1?

elasticsearch - 在 C#.Net 的 DataTable 中获取 Elasticsearch 响应

elasticsearch - 如何通过Spark Streaming实时更新Elasticsearch文档?

elasticsearch - 在ElasticSearch存储库上查询 bool 字段

elasticsearch - 无法使用设置和源Java API创建索引

java - 批注作为另一个批注的快捷方式

elasticsearch - Elasticsearch-nGram过滤器保留/保留原始 token

java - 无法将字符串解析为正确的日期格式

mysql - 使用 dateformat 在 mysql 中按日期搜索

ruby-on-rails - Rails 没有在 en.yml 中获取自定义日期和时间格式