首页 > 解决方案 > LocalDateTime 与@Query 格式错误,秒数被删除

问题描述

我有一个类似于这个的映射:

        @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss")
        private LocalDateTime startTime;

        @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss")
        private LocalDateTime endTime;

和这样的存储库中的请求:

    @Query("{" +
            "    \"nested\": {" +
            "      \"path\" : \"panel.channels\"," +
            "      \"query\" : {" +
            "        \"bool\" : {" +
            "          \"must\" : [" +
            "              { \"range\" : { \"panel.channels.startTime\" : { \"lte\": \"?1\" } } }," +
            "              { \"range\" : { \"panel.channels.endTime\" :   { \"gte\": \"?0\"  } } } " +
            "          ]" +
            "        }" +
            "      }" +
            "    }" +
            "}")
    Flux<Document> search(LocalDateTime from, LocalDateTime to);

如果我提供的日期时间秒数为零,例如在一天的开始,例如 2021-03-15T00:00:00,则日期格式不正确,请求不返回结果,日志中出现此错误(在ES响应):

"caused_by":{"type":"illegal_argument_exception","reason":"failed to parse date field [2021-03-15T00:00] with format [yyyy-MM-dd'T'HH:mm:ss]"

当我检查请求时确实是错误的:

{
  "nested": {
    "path": "panel.channels",
    "query": {
      "bool": {
        "must": [
          {
            "range": {
              "panel.channels.startTime": {
                "lte": "2021-03-15T00:00"
              }
            }
          },
          {
            "range": {
              "panel.channels.endTime": {
                "gte": "2020-01-01T00:00"
              }
            }
          }
        ]
      }
    }
  }
}

但是,如果 LocalDateTime 中有几秒钟,例如2021-03-15T00:00:01,是有效的!

标签: spring-data-elasticsearch

解决方案


我做了一些调试和测试;那么这里发生了什么?

@Field您已使用属性上的注释定义了自定义日期格式。这样,在内部为这些属性中的每一个创建了一个自定义转换器,该转换器可以以所需的格式从 a 转换为 a 并返回LocalDateTime重要提示:此转换器附加到实体的属性,而不是通常可用的转换器 (您可能对不同的属性有不同的格式)。然后在 Spring Data Elasticsearch 存储或检索实体时使用此转换器。StringLocalDateTime

进行查询时,事情变得更加复杂。例如,当您创建CriteriaQuerySpring Data 时,Elasticsearch 知道查询中使用了哪些属性,并且可以使用相应的转换器将属性值正确转换为查询参数。@Query对于由存储库方法上的注释定义的查询,这是不可能的。这些查询仅用作字符串,参数部分 ( ?0, ?1) 被方法的转换参数值替换。此处转换的意思是通过可用的默认转换进行转换,而不是附加到属性的转换,因为我们不知道参数可能与实体的哪个属性相关。我们需要实现一个查找相应属性的查询解析器 - 在您的情况下 panel.channels.startTime- 得到转换器,我认为我们不会实现这样的解析器。

因此,在您的情况下,默认转换服务会搜索一个LocalDateTime实例的转换器,并且没有人注册。然后使用回退调用toString()要转换的对象,并且在零秒的情况下返回您看到的字符串 - 仅包含小时和分钟,这LocalDateTime.toString()就是返回 this 的实现。

我想到的第一个解决方法是为LocalDateTime->定义一个转换器String并在 Spring Data Elasticsearch 中注册它,但我发现@Query参数解析没有使用它 - 这绝对是一个错误 - 我将在明天修复。Spring Data Elasticsearch 4.1、4.2 和当前主分支的下一轮维护版本将于下周五发布,因此您可以很快获得该更改。我已经在本地尝试过,并且生成的查询已正确构建。

另一种可能的解决方案可能是在方法参数上引入额外的属性提示注释,就像

Flux<Document> search(
    @Property("panel.channels.endTime") LocalDateTime from, 
    @Property("panel.channels.startTime") LocalDateTime to);

但老实说,我不喜欢这样。

当我实施了修复并发布了修复版本时,我将使用有关如何将转换器添加到您的设置并使其运行的附加信息来编辑此答案。


推荐阅读