Проблема производительности агрегации в Elasticsearch с данными о доступности отелей - PullRequest
1 голос
/ 18 октября 2019

Я создаю небольшое приложение для поиска номеров в отелях, например, booking.com, используя Elasticsearch 6.8.0.

По сути, у меня есть документ на день и номер, в котором указано, доступен ли он, и стоимостьна этот день. Мне нужно выполнить запрос с этими требованиями:

Ввод:

  • Дни желаемого пребывания.
  • Максимальная сумма денег, которую я готов потратить.
  • Страница результатов, которые я хочу видеть.
  • Количество результатов на странице.

Вывод:

  • Список самых дешевых предложений для каждой гостиницы, которые отвечают требованиям, заказанных в порядке ASC.

Схема документов:

{
  "mappings": {
    "_doc": {
      "properties": {
        "room_id": {
          "type": "keyword"
        },
        "available": {
          "type": "boolean"
        },
        "rate": {
          "type": "float"
        },
        "hotel_id": {
          "type": "keyword"
        },
        "day": {
          "type": "date",
          "format": "yyyyMMdd"
        }
      }
    }
  }
}

У меня есть индекс в месяц, и на данный момент я толькопоиск в том же месяце.

Я пришел с этим запросом:

GET /hotels_201910/_search?filter_path=aggregations.hotel.buckets.min_price.value,aggregations.hotel.buckets.key
{
  "size": 0,
  "query": {
    "bool": {
      "filter": [
        {
         "range": {
            "day": { "gte" : "20191001", "lte" : "20191010" }
          }
        },
        {
          "term": {
            "available": true
          }
        }
      ]
    }
  },
  "aggs": {
    "hotel": {
      "terms": {
        "field": "hotel_id",
        "min_doc_count": 1,
        "size" : 1000000
      },
      "aggs": {
        "room": {
          "terms": {
            "field": "room_id",
            "min_doc_count": 10,
            "size" : 1000000
          },
          "aggs": {
            "sum_price": {
              "sum": {
                "field": "rate"
              }
            },
            "max_price": {
              "bucket_selector": {
                "buckets_path": {
                  "price": "sum_price"
                },
                "script": "params.price <= 600"
              }
            }
          }
        },
        "min_price": {
          "min_bucket": {
            "buckets_path": "room>sum_price"
          }
        },
        "sort_by_min_price" : {
          "bucket_sort" :{
            "sort": [{"min_price" : { "order" : "asc" }}],
            "from" : 0,
            "size" : 20
          }
        }
      }
    }
  }
}

И это работает, но есть несколько проблем.

  • Это слишком медленно,При 100 тыс. Ежедневных комнат на моем компьютере требуется около 500 мсек, когда не выполняется ни один другой запрос. Так что в реальной системе это было бы очень плохо.
  • Мне нужно установить "size" на большое число в терминах агрегации, иначе не все отели и номера считаются.

Есть ли способ улучшить производительность этой агрегации? Я пытался разделить индекс на несколько сегментов, но это не помогло.

Я почти уверен, что подход неправильный, и поэтому он медленный. Любая рекомендация о том, как добиться более быстрого времени ответа на запрос в этом случае?

1 Ответ

1 голос
/ 18 октября 2019

Прежде чем перейти к ответу, я не понял, почему вы используете приведенное ниже условие / агрегацию

"min_price": {
          "min_bucket": {
            "buckets_path": "room>sum_price"
          }
        }

Не могли бы вы дать мне больше разъяснений о том, зачем вам это нужно.

Теперь ответьте на свой главный вопрос:

Почему вы хотите использовать термин room_id и hotel_id? Вы можете получить все комнаты вашего поиска, а затем сгруппировать их по hotel_id на стороне приложения.

Приведенная ниже логика, вы получите все документы, сгруппированные по room_id и с метриками суммы. Вы можете использовать тот же фильтр сценариев для условия> 600.

   {
      "size": 0,
      "query": {
        "bool": {
          "filter": [
            {
             "range": {
                "day": { "gte" : "20191001", "lte" : "20191010" }
              }
            },
            {
              "term": {
                "available": true
              }
            }
          ]
        }
      },
      "by_room_id": {
            "composite" : {
              "size": 100, 
                "sources" : [
                    { 
                      "room_id": { 
                        "terms" : { 
                          "field": "room_id" 
                        } 
                      } 
                    }
                ]
            },
            "aggregations": {
                "price_on_required_dates": {
                    "sum": { "field": "rate" }
                },
                "include_source": {
                    "top_hits": {
                "size": 1,
                "_source": true
              }
            },
            "price_bucket_sort": {
                "bucket_sort": {
                        "sort": [
                          {"price_on_required_dates": {"order": "desc"}}
                        ]
                    }
                }
            }
        }
     }

Кроме того, для повышения эффективности поиска, https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-search-speed.html

...