Elasticsearch日期字段上基于脚本的排序

时间:2015-10-04 11:44:36

标签: sorting date datetime elasticsearch

我刚刚开始使用Elasticsearch,并希望在映射为date,格式为hour_minute的字段上使用基于脚本的排序。每个文档中可以有多个字段实例。

在介绍表达式之前,作为第一步,我尝试进行简单的排序(使用Sense插件):

POST myIndex/_search
{
   "query": {
      "match_all": {}
   },
   "sort": {
      "_script": {
         "script": "doc[\"someTime\"].value",
         "lang": "groovy",
         "type": "date",
         "order": "asc"
      }
   }
}

我收到此错误(片段):

SearchPhaseExecutionException[Failed to execute phase [query], all shards failed;
shardFailures {[tjWL-zV5QXmGjNlXzLvrzw][myIndex][0]:
SearchParseException[[myIndex][0]: 
query[ConstantScore(*:*)],from[-1],size[-1]: Parse Failure [Failed to parse source…

如果我使用"type": "number"发布上述查询,则没有错误,但这当然不按日期排序。以下工作正常:

POST myIndex/_search
{
   "query": {
      "match_all": {}
   },
   "sort": {
      "someTime": {
         "order": "asc"
      }
   }
}

最终,我想使用基于脚本的排序,因为我将尝试使用日期和时间条件进行查询,过滤或排序,例如查询具有今天日期的文档,然后按最短时间对其进行排序在此之后,等等。

我们非常感谢任何建议。

1 个答案:

答案 0 :(得分:4)

使用脚本对文档进行排序并不是很有效,特别是如果您的文档库预计会随着时间的推移而增长。因此,我将提供解决方案,然后提出另一种选择。

为了使用脚本进行排序,您需要将日期转换为毫秒,以便可以对简单数字运行排序(排序类型只能是numberstring)。

POST myIndex/_search
{
   "query": {
      "match_all": {}
   },
   "sort": {
      "_script": {
         "script": "doc[\"someTime\"].date.getMillisOfDay()",
         "lang": "groovy",
         "type": "number",       <----- make sure this is number
         "order": "asc"
      }
   }
}

请注意,根据您想要的粒度,您还可以使用getSecondOfDay()getMinuteOfDay()。这样,如果您的查询和过滤器在正确的日期选择了文档,您的排序脚本将根据当天的毫秒数(或秒或分钟数)对文档进行排序。

第二个解决方案意味着还要将从那天开始以来的毫秒数(或几秒或几分钟)索引到另一个字段,并简单地使用它进行排序,这样您就不需要脚本了。最重要的是,在索引时可以知道的搜索时需要的任何信息都应该被索引而不是实时计算。

例如,如果您的someTime字段包含日期2015-10-05T05:34:12.276Z,那么您可以使用值millisOfDay20052276字段编制索引,这是

  • 5小时* 3600000 ms
  • +34分钟* 60000毫秒
  • +12秒* 1000毫秒
  • +276 ms

然后你可以使用

排序
POST myIndex/_search
{
   "query": {
      "range": {
          "someTime": {
              "gt": "now"
          }
      }
   },
   "sort": {
      "millisOfDay": {
         "order": "asc"
      }
   }
}

请注意,我添加了一个查询,仅选择someTime日期之后的文档,因此您将来会获得所有文档,但会按升序millisOfDay排序,这意味着您将首先从now获取最近的日期。

<强>更新

如果someTime的格式为HH:mm,那么您还可以存储其millisOfDay值,例如如果someTime = 17:30millisOfDay为(17h * 3600000 ms)+(30 min * 60000 ms)= 63000000

然后,您的查询需要使用script过滤器进行一些重写,如下所示:

{
  "query": {
    "filtered": {
      "filter": {
        "script": {
          "script": "doc.millisOfDay.value > new DateTime().millisOfDay"
        }
      }
    }
  },
  "sort": {
    "millisOfDay": {
      "order": "asc"
    }
  }
}