一尘不染

通过Elasticsearch / Tire通过时间表查找开店

elasticsearch

我有Shop每个都有关系的模型,Timetable其中可能包含类似的内容:

shop_id: 1, day: 5, open_hour: 7,  open_minutes: 0,  close_hour: 13, close_minute: 30
shop_id: 1, day: 5, open_hour: 14, open_minutes: 30, close_hour: 18, close_minute: 00

当然Timetable可以有更优雅的格式,但是接下来的问题是:如何通过elasticsearch(tire)查找开着的Shop?

所有的想法都会被感动!谢谢!


找到的解决方案:

  1. 为每天(星期日,星期一,..)创建单独的索引

  2. 每天从Timetable以下位置构建完整的分钟列表:

    ((open_hour * 60 + open_minute)..(close_hour * 60 + close_minute)).to_a
    
  3. 添加过滤器进行搜索:

    filter :term, current_day_name => (current_hour * 60 + current_minutes)
    

这个解决方案也可以,但是看起来很麻烦,因为如果Shop每天工作8个小时,我创建的数组的大小为:(将8 * 60 = 480其转换为索引字段的字符串),所以这就是为什么这个问题仍然存在,也许有人会找到更好的解决方案


@Andrei Stefan的轮胎零件答案:

indexes :open_hours, type: :nested do
  indexes :open, type: 'integer'
  indexes :close, type: 'integer'
end

open_hours_query = Tire::Search::Query.new do
  filtered do
    query { all }
    filter :range, "open_hours.open"   => { lte: current_time }
    filter :range, "open_hours.close"  => { gte: current_time }
  end
end

filter :nested, { path: 'open_hours', query: open_hours_query.to_hash }

阅读 306

收藏
2020-06-22

共1个答案

一尘不染

我会考虑这样做如下:

  1. 营业时间和营业时间是Elasticsearch中嵌套对象数组的整数值:

例如:商店在第1天的07:00开店,在13:30关门,然后在14:30开店,在18:00关门,将在ES中转换为:

"shop_name": "Shop 1",
"open_hours": [
  { "open": 420, "close": 810 },
  { "open": 870, "close": 1080 }
]
  1. 一周中的每一天(1-> 7)代表一个值(将添加到分钟数中):

    Day 1 = addition 0
    Day 2 = addition 2000
    Day 3 = addition 4000

    Day 7 = addition 10000

因此,每天增加2000,因为每天最多包含1440分钟(24小时* 60分钟),并且为了能够将一天与单个数字区分开,这些数字不必相交。

因此,上面示例中商店在07:00开张的示例将在第4天转换为:

    "shop_name": "Shop 1",
    "open_hours": [
      { "open": 6420, "close": 6810 },
      { "open": 6870, "close": 7080 }
    ]
  1. 查询这些文档时,您要搜索的那一天需要遵循与上述相同的规则。例如,如果要查看是否在第4天的13:45打开了“商店1”,则可以搜索(6000 + 13 * 60 + 45 = 6825)分钟。

  2. Elasticsearch中上述所有内容的映射如下:

    {
      "mappings": {
        "shop" : {
          "properties": {
            "shop_name" : { "type" : "string" },
            "open_hours" : {
              "type" : "nested",
              "properties": {
                "open" : { "type" : "integer" },
                "close": { "type" : "integer" }
              }
            }
          }
        }
      }
    }
  1. 测试数据:
    POST /shops/shop/_bulk
    {"index":{}}
    {"shop_name":"Shop 1","open_hours":[{"open":420,"close":810},{"open":870,"close":1080}]}
    {"index":{}}
    {"shop_name":"Shop 2","open_hours":[{"open":0,"close":500},{"open":1000,"close":1440}]}
    {"index":{}}
    {"shop_name":"Shop 3","open_hours":[{"open":0,"close":10},{"open":70,"close":450},{"open":900,"close":1050}]}
    {"index":{}}
    {"shop_name":"Shop 4","open_hours":[{"open":2000,"close":2480}]}
    {"index":{}}
    {"shop_name":"Shop 5","open_hours":[{"open":2220,"close":2480},{"open":2580,"close":3000},{"open":3100,"close":3440}]}
    {"index":{}}
    {"shop_name":"Shop 6","open_hours":[{"open":6000,"close":6010},{"open":6700,"close":6900}]}
  1. 查询在第二天的第2400分钟(06:40)在第二天开张的商店:
    {
      "query": {
        "bool": {
          "must": [
            {
              "nested": {
                "path": "open_hours",
                "query": {
                  "bool": {
                    "must": [
                      {
                        "filtered": {
                          "filter": {
                            "range": {
                              "open_hours.open": {
                                "lte": 2400
                              }}}}},
                      {
                        "filtered": {
                          "filter": {
                            "range": {
                              "open_hours.close": {
                                "gte": 2400
                              }}}}}
                    ]
                  }}}}
                ]
    }}}

将输出Shop 4和Shop 5:

             "shop_name": "Shop 4",
               "open_hours": [
                  {
                     "open": 2000,
                     "close": 2480
                  }
               ]

             "shop_name": "Shop 5",
               "open_hours": [
                  {
                     "open": 2220,
                     "close": 2480
                  },
                  {
                     "open": 2580,
                     "close": 3000
                  },
                  {
                     "open": 3100,
                     "close": 3440
                  }
               ]

稍后编辑: 自从我添加此回复以来,Elasticsearch有了很长的路要走,此后发生了很多变化,因此可以用a
或什至简单的替换filtered过滤器(在bool
must我使用的上下文中)。另外,6.x中不再存在,因此,如果您需要以某种方式使用分析器或()按商店名称进行搜索,则可以使用:bool
filter
must``string``text``keyword``"shop_name": { "type" : "text" },

    {
      "query": {
        "bool": {
          "must": [
            {
              "nested": {
                "path": "open_hours",
                "query": {
                  "bool": {
                    "filter": [
                      {
                        "range": {
                          "open_hours.open": {
                            "lte": 2400
                          }
                        }
                      },
                      {
                        "range": {
                          "open_hours.close": {
                            "gte": 2400
                          }
                        }
                      }
                    ]
                  }
                }
              }
            }
          ]
        }
      }
    }
2020-06-22