Разрешить документу соответствовать нескольким сегментам гистограммы даты

У меня есть индекс с сопоставлением, похожим на

{
    "id": {
        "type": "long"
    },
    "start": {
        "type": "date"
    },
    "end": {
        "type": "date"
    }
}

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

Например. если для одного документа "start" = 12.01.2018, "end" = 25.04.2019, мой интервал даты-гистограммы составляет недели, а диапазон от now-1y до настоящего момента. Теперь я хочу, чтобы этот документ попал в каждую корзину с недели с 01.12.2018 до недели с 25.04.2019. Таким образом, с одним только этим документом результатом должно быть 52 сегмента, где сегменты с апреля по декабрь имеют doc_count 0, а сегменты с декабря по апрель имеют doc_count 1.

Как я вижу, гистограмма даты дает мне возможность сопоставить мой документ ровно с одной корзиной в зависимости от одного поля, либо «начало», либо «конец».

Что я пробовал до сих пор:

  1. Динамически генерировать запрос с 52 фильтрами, который проверяет, попадает ли документ в это «ведро».
  2. Старайтесь использовать безболезненные скрипты в каждом запросе

Оба решения были крайне медленными. Я работаю примерно с 200 тыс. Документов, и такие запросы занимали около 10 секунд.

РЕДАКТИРОВАТЬ: вот пример запроса, который создается динамически. Как видно, создается один фильтр в неделю. Этот запрос занимает около 10 секунд, что слишком долго.

%{
  aggs: %{
    count_chart: %{
      aggs: %{
        last_seen_over_time: %{
          filters: %{
            filters: %{
              "2018-09-24T00:00:00Z" => %{
                bool: %{
                  must: [
                    %{range: %{start: %{lte: "2018-09-24T00:00:00Z"}}},
                    %{range: %{end: %{gte: "2018-09-17T00:00:00Z"}}}
                  ]
                }
              },
              "2018-12-24T00:00:00Z" => %{
                bool: %{
                  must: [
                    %{range: %{start: %{lte: "2018-12-24T00:00:00Z"}}},
                    %{range: %{end: %{gte: "2018-12-17T00:00:00Z"}}}
                  ]
                }
              },
              "2019-04-01T00:00:00Z" => %{
                bool: %{
                  must: [
                    %{range: %{start: %{lte: "2019-04-01T00:00:00Z"}}},
                    %{range: %{end: %{gte: "2019-03-25T00:00:00Z"}}}
                  ]
                }
              }, ...
          }
      }
    }
  },
  size: 0
}

И образец ответа:

%{
  "_shards" => %{"failed" => 0, "skipped" => 0, "successful" => 5, "total" => 5},
  "aggregations" => %{
    "count_chart" => %{
      "doc_count" => 944542,
      "last_seen_over_time" => %{
        "buckets" => %{
          "2018-09-24T00:00:00Z" => %{"doc_count" => 52212},
          "2018-12-24T00:00:00Z" => %{"doc_count" => 138509},
          "2019-04-01T00:00:00Z" => %{"doc_count" => 119634},
          ...
        }
      }
    }
  },
  "hits" => %{"hits" => [], "max_score" => 0.0, "total" => 14161812},
  "timed_out" => false,
  "took" => 2505
}

Надеюсь, этот вопрос понятен. Если нет, я объясню это более подробно.


person Jonathan R    schedule 25.04.2019    source источник
comment
Вы использовали Агрегация скрипта сегмента? если нет, не могли бы вы поделиться написанным вами запросом? также, как бы вы себя чувствовали, пытаясь разделить по дате начала и добавить с помощью очереди приоритетов?   -  person Tom Slabbaert    schedule 26.04.2019
comment
Спасибо за ваш ответ, Ive отредактировал вопрос и добавил образец запроса / ответа. Может быть, это все еще немного проясняет. Я не понимаю, как я могу использовать агрегацию сценариев корзины для своих целей?   -  person Jonathan R    schedule 27.04.2019


Ответы (1)


Как насчет выполнения двух запросов date_histogram и расчета разницы за неделю? Я предполагаю, что вам просто нужно общее количество из-за размера: 0 в вашем запросе.

    let start = await client.search({
        index: 'dates',
        size: 0,
        body: {
            "aggs" : {
                "start": {
                    "date_histogram": {
                        "field": "start",
                        "interval": "week"
                    },
                }
            }
        }
    });

    let end = await client.search({
        index: 'dates',
        size: 0,
        body: {
            "aggs" : {
                "end": {
                    "date_histogram": {
                        "field": "end",
                        "interval": "week"
                    },
                }
            }
        }
    });

   let buckets = {};
   let start_buckets = start.aggregations.start.buckets;
   let end_buckets = end.aggregations.start.buckets;
   let started = 0;
   let ended = 0;
   for (let i = 0; i < start_buckets.length; i++) {
       started += start_buckets[i].doc_count;
       buckets[start_buckets[i].key_as_string] = started - ended;
       ended += end_buckets[i].doc_count;
   }

Этот тест занял менее 2 секунд на моем локальном компьютере в масштабе, аналогичном вашему.

Вы можете запустить обе агрегации одновременно, чтобы сэкономить больше времени.

person Tom Slabbaert    schedule 28.04.2019
comment
Спасибо, это выглядит интересно, плохо разбираюсь в этом! - person Jonathan R; 28.04.2019
comment
Это сработало для меня. Как вы думаете, есть ли способ сделать это только с помощью elasticsearch? - person Jonathan R; 01.05.2019
comment
Мое использование резинки сильно отличается от этого, поэтому я не хочу давать ответ, однако из-за моего короткого времени игры я не смог этого сделать. Я мог бы поиграть еще немного в эти выходные, когда у меня будет немного времени. я буду держать тебя в курсе - person Tom Slabbaert; 01.05.2019