【问题标题】:Filter Elasticsearch by day of week and time按星期几和时间过滤 Elasticsearch
【发布时间】:2020-11-17 20:54:56
【问题描述】:

我在 Elasticsearch 中有一个企业索引。索引中的每个文档代表一个业务,每个业务都有 business_hours。我正在尝试使用星期几和时间来过滤营业时间。例如,我们希望能够为 向我们展示所有周二晚上 6:00 PM 之后营业的商家做一个过滤器 我在想我们应该有一个具有以下映射的字段:

  {
      "mappings": {
        "properties": {
          
          "business_hours": {
                             "type": "date_range",
                            "format": "w'T'hh:mma"
          }
        }
      }
    }

然后,每个文档都会有一个 business_hours 数组。 因此,周一上午 9:00 - 下午 5:00 和周二上午 9:30 - 下午 5:00 营业的商店如下所示:

POST my-index/_doc
    {
      "name": "My Store",
      "business_hours": [
        {
        "gte": "1T09:00AM",
        "lte": "1T05:00PM"
        },
        {
        "gte": "2T09:30AM",
        "lte": "2T05:00PM"
        }
      ]
    }

我尝试搜索此文档并进行查询,但时间过滤器不起作用,它们看起来被忽略了...... Elasticsearch 是否支持按一周中的某一天进行过滤,还是必须是实际的日期时间?

这是我使用的查询。它应该过滤周三营业,但它返回了上面只有周一和周二营业时间的文档

GET my-index/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "business_hours": {
              "gte": "3T10:00AM",
              "lte": "3T05:00PM",
              "relation": "CONTAINS"
            }
          }
        }
      ]
    }
  }
}

【问题讨论】:

    标签: elasticsearch elasticsearch-dsl


    【解决方案1】:

    使用range 字段的想法很好。但是,我建议使用integer_range 字段,而不是使用绝对日期的date_range

    由于每天包含 1440 分钟,我的建议是将营业时间编码为自午夜以来的分钟数,并在该数字前加上当天的索引(星期一 = 1、星期二 = 2 等)。将给定的小时数转换为自午夜后的分钟数的公式非常简单:

    (60 * HH) + MM 
    
    Note: HH is in 24 hours format, not AM/PM, but that's a detail
    

    以你上面的例子,它会产生这个:

    POST my-index/_doc
    {
      "business_hours": [
        {
          "gte": 10540,       <--- Monday (1), 540 minutes after midnight
          "lte": 11020        <--- Monday (1), 1020 minutes after midnight
        },
        {
          "gte": 20570,       <--- Tuesday (2), 570 minutes after midnight
          "lte": 21020        <--- Tuesday (2), 1020 minutes after midnight
        }
      ]
    }
    

    这样range 查询就变得很简单,可以消除任何与日期相关的问题。例如,下面的查询通过搜索周一早上 6 点到下午 5 点之间营业的企业来检索上述文档

    GET my-index/_search
    {
      "query": {
        "bool": {
          "filter": [
            {
              "range": {
                "business_hours": {
                  "gte": "10600",         <--- Monday (1), 600 minutes after midnight
                  "lte": "11020",         <--- Monday (1), 1020 minutes after midnight
                  "relation": "CONTAINS"
                }
              }
            }
          ]
        }
      }
    }
    

    【讨论】:

    • 感谢这个解决方案 Val!只是想知道,使用附加的日期作为一个附加在开头的整数,而不是仅仅将字段计算为自周六午夜以来经过的分钟数,是否有优势?这样周一 (D) 午夜后的 600 分钟将是 (HH * 60) + (D * 1440) + MM = (10 * 60) + (1 * 1440) + 0 = 2040
    • 酷,很高兴它有帮助!就像我的一样,您的解决方案也完全可行。底线是确保将不同的时间段转换为不同的间隔,您可以在这些间隔上运行范围正常的数字查询。
    【解决方案2】:

    我能够重现您的映射、示例数据和查询的问题,当我在查询中使用 explain 时,它解释了为什么它会获取第 1 天和第 2 天的结果。

    带有解释的搜索查询输出

    "hits": [
                {
                    "_shard": "[64883176][0]",
                    "_node": "kL6FUU3RT5GGzu_mqRs8NA",
                    "_index": "64883176",
                    "_type": "_doc",
                    "_id": "1",
                    "_score": 0.0,
                    "_source": {
                        "name": "My Store",
                        "business_hours": [
                            {
                                "gte": "1T09:00AM",
                                "lte": "1T05:00PM"
                            },
                            {
                                "gte": "2T09:30AM",
                                "lte": "2T05:00PM"
                            }
                        ]
                    },
                    "_explanation": {
                        "value": 0.0,
                        "description": "ConstantScore(business_hours:<ranges:[36000000 : 61199999]>)^0.0",
                        "details": []
                    }
                }
            ]
    

    如果您仔细观察,查询会转换为纪元格式,如下所示

    ConstantScore(business_hours:<ranges:[36000000 : 61199999]>)^0.0",
    

    现在,当您使用 epoch converter 时,您会注意到它实际上是在完全不同的日期范围内进行范围查询,起始范围是 Friday, 10 December 1971 07:59:59

    根据日期字段的范围查询,Elasticsearch 添加了missing date component,这似乎是导致问题的原因。

    如果您提供正确的数据范围(即完整的日期,包括年、月等),显然它可以工作,但我同意,这会导致复杂性,我将看看我们如何使用给定的格式实现相同的目标。

    【讨论】:

    • 感谢您的解释!我想一个可能的解决方案是在 business_hours 中添加一个实际的日期,但它肯定感觉很老套。如果我们能在一周中的一天和几个小时内使范围工作,那就太好了
    • @SGolds,是的,这绝对是一个可能的解决方案,我在回答中也提到过,正如我提到的那样,它会导致复杂性,我努力寻找如何在一周中的某一天工作和几个小时,但即使花了几个小时也找不到解决方案
    • 感谢所有的努力!
    猜你喜欢
    • 2015-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-10
    • 2015-06-11
    • 2015-08-19
    相关资源
    最近更新 更多