【问题标题】:How to store date range data in elastic search (aws) and search for a range?如何在弹性搜索(aws)中存储日期范围数据并搜索范围?
【发布时间】:2016-10-15 21:57:49
【问题描述】:

我正在尝试在 elasticsearch 中存储酒店房间可用性。然后我需要 搜索从某个日期到另一个日期都可用的房间。我想出了 两种存储可用性数据的方法,它们如下:

这里可用性字典存储所有日期和值,每个日期键为真或假,表示其可用 那天与否。

{
  "_id": "khg2uo47tyhgjwebu7624787",
  "room_type": "garden view",
  "hotel_name": "Cool hotel",
  "hotel_id": "jytu64r982u0299023",
  "room_metadata1": 233,
  "room_color": "black",
  "availability": {
    "2016-07-01": true,
    "2016-07-02": true,
    "2016-07-03": false,
    "2016-07-04": true,
    "2016-07-05": true,
    "2016-07-06": null,
    "2016-07-07": true,
    "2016-07-08": true,
    ----
    ----
    for 365 days 
  }

}

这里的可用性数组只存储有空房的日期

{
  "_id": "khg2uo47tyhgjwebu7624787",
  "room_type": "garden view",
  "hotel_name": "Cool hotel",
  "hotel_id": "jytu64r982u0299023",
  "room_metadata1": 535,
  "room_color": "black",
  "availability": ["2016-07-01", "2016-07-02", "2016-07-04", "2016-07-05", "2016-07-07", "2016-07-08"] ---for 365 days
  }
}

我想搜索所有房间,从 from_dateto_date 都可以找到,应该查看 availability 字典或数组。我的日期范围可能长达 365 天

如何存储这些可用性数据,以便我可以轻松地执行上述搜索? 而且我找不到任何方法来搜索日期范围,所以有什么建议吗?

请注意,物品 在availability 中可能不会保持排序。而且我可能有超过 1 亿条记录可供搜索。

【问题讨论】:

    标签: elasticsearch amazon-elasticsearch nosql


    【解决方案1】:

    对此进行建模的一种方法是使用父/子文档。房间文件是父文件,可用性文件是他们的子文件。对于每个房间,每个房间可用的日期都会有一份可用性文件。然后,在查询时,我们可以查询在搜索间隔内的每个日期都有一个可用性子文档的父房间(甚至是不相交的房间)。

    请注意,您需要确保在预订房间后立即删除每个预订日期的相应子文档。

    让我们试试这个。首先创建索引:

    PUT /rooms
    {
      "mappings": {
        "room": {
          "properties": {
            "room_num": {
              "type": "integer"
            }
          }
        },
        "availability": {
          "_parent": {
            "type": "room"
          },
          "properties": {
            "date": {
              "type": "date",
              "format": "date"
            },
            "available": {
              "type": "boolean"
            }
          }
        }
      }
    }
    

    然后添加一些数据

    POST /rooms/_bulk
    {"_index": { "_type": "room", "_id": 233}}
    {"room_num": 233}
    {"_index": { "_type": "availability", "_id": "20160701", "_parent": 233}}
    {"date": "2016-07-01"}
    {"_index": { "_type": "availability", "_id": "20160702", "_parent": 233}}
    {"date": "2016-07-02"}
    {"_index": { "_type": "availability", "_id": "20160704", "_parent": 233}}
    {"date": "2016-07-04"}
    {"_index": { "_type": "availability", "_id": "20160705", "_parent": 233}}
    {"date": "2016-07-05"}
    {"_index": { "_type": "availability", "_id": "20160707", "_parent": 233}}
    {"date": "2016-07-07"}
    {"_index": { "_type": "availability", "_id": "20160708", "_parent": 233}}
    {"date": "2016-07-08"}
    

    最后,我们可以开始查询了。首先,假设我们想在2016-07-01 上找到一个可用的房间:

    POST /rooms/room/_search
    {
      "query": {
        "has_child": {
          "type": "availability",
          "query": {
            "term": {
              "date": "2016-07-01"
            }
          }
        }
      }
    }
    => result: room 233
    

    然后,让我们尝试搜索从2016-07-012016-07-03 的可用房间

    POST /rooms/room/_search
    {
      "query": {
        "bool": {
          "minimum_should_match": 3,
          "should": [
            {
              "has_child": {
                "type": "availability",
                "query": {
                  "term": {
                    "date": "2016-07-01"
                  }
                }
              }
            },
            {
              "has_child": {
                "type": "availability",
                "query": {
                  "term": {
                    "date": "2016-07-02"
                  }
                }
              }
            },
            {
              "has_child": {
                "type": "availability",
                "query": {
                  "term": {
                    "date": "2016-07-03"
                  }
                }
              }
            }
          ]
        }
      }
    }
    => Result: No rooms
    

    但是,搜索从 2016-07-012016-07-02 的可用房间会得到房间 233

    POST /rooms/room/_search
    {
      "query": {
        "bool": {
          "minimum_should_match": 2,
          "should": [
            {
              "has_child": {
                "type": "availability",
                "query": {
                  "term": {
                    "date": "2016-07-01"
                  }
                }
              }
            },
            {
              "has_child": {
                "type": "availability",
                "query": {
                  "term": {
                    "date": "2016-07-02"
                  }
                }
              }
            }
          ]
        }
      }
    }
    => Result: Room 233
    

    我们还可以搜索不相交的区间,比如从2016-07-012016-07-02 + 从2016-07-042016-07-05

    POST /rooms/room/_search
    {
      "query": {
        "bool": {
          "minimum_should_match": 4,
          "should": [
            {
              "has_child": {
                "type": "availability",
                "query": {
                  "term": {
                    "date": "2016-07-01"
                  }
                }
              }
            },
            {
              "has_child": {
                "type": "availability",
                "query": {
                  "term": {
                    "date": "2016-07-02"
                  }
                }
              }
            },
            {
              "has_child": {
                "type": "availability",
                "query": {
                  "term": {
                    "date": "2016-07-04"
                  }
                }
              }
            },
            {
              "has_child": {
                "type": "availability",
                "query": {
                  "term": {
                    "date": "2016-07-05"
                  }
                }
              }
            }
          ]
        }
      }
    }
    => Result: Room 233
    

    等等...关键是每个日期添加一个has_child 查询以检查可用性并将minimum_should_match 设置为您要检查的日期数。

    更新

    另一种选择是使用script filter,但是对于 1 亿个文档,我不确定它是否能很好地扩展。

    在这种情况下,您可以保留您的原始设计(最好是第二个,因为使用第一个,您将在映射中创建太多不必要的字段),查询将如下所示:

    POST /rooms/room/_search
    {
      "query": {
        "bool": {
          "filter": {
            "script": {
              "script": {
                "inline": "def dates = doc.availability.sort(false); from = Date.parse('yyyy-MM-dd', from); to = Date.parse('yyyy-MM-dd', to); def days = to - from; def fromIndex = doc.availability.values.indexOf(from.time); def toIndex = doc.availability.values.indexOf(to.time); return days == (toIndex - fromIndex)",
                "params": {
                  "from": "2016-07-01",
                  "to": "2016-07-04"
                }
              }
            }
          }
        }
      }
    }
    

    【讨论】:

    • 谢谢@Val。因此,根据您的解决方案,如果我必须检查从 2016 年 1 月 1 日到 2016 年 12 月 1 日的范围,这意味着在搜索查询中,每个日期我将有近 330 个has_child 日期。你认为这是个好主意吗?
    • 您认为您经常会有人预订 330 天的房间吗? ;-)
    • 是的,有一些酒店,他们提供长住。例如租用一年的“通勤者”
    • 我还读到父子关系有限制elastic.co/guide/en/elasticsearch/guide/current/…父文档,它的所有子文档必须生活在同一个分片上。问题1)那么如果我有很多分片,那么它会扩展吗?问题 2)也是我最初的问题,如果我的搜索范围为 3 个月(这对我的应用程序来说很常见),那么它将是 90 has_child。那么,您认为它仍然是一个优雅的解决方案吗?
    • 1) 出于技术和实际原因,父文档和子文档存在于同一个分片上,这不是问题。 2)如果这是您的应用程序的常见要求,我希望在您的原始问题中看到此要求;-) 对于您的初始设计(第一个或第二个),我知道的唯一选择是使用脚本,但是如果你有 1 亿个文档,我认为这不会是真正的性能。
    【解决方案2】:

    我是新手,刚刚学习 ES。这种设置/映射的缺点是什么?

    ciao..remco

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-13
    • 2014-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多