Сегменты гистограммы Elasticsearch, усредненные по дате

У меня есть куча документов, проиндексированных в ElasticSearch, и мне нужно получить следующие данные:

Для каждого месяца получите среднее количество документов за рабочий день месяца (или, если это невозможно, по умолчанию используйте 20 дней).

Я уже сгруппировал свои данные в сегменты месяцев, используя агрегирование date histogram. Я попытался вложить stats сегмент, но при этом агрегировании используются данные, извлеченные из поля документа, а не из родительского сегмента.

Вот мой запрос:

{
    "query": {
        "match_all": {}
    },
    "aggs": {
        "docs_per_month": {
            "date_histogram": {
                "field": "created_date",
                "interval": "month",
                "min_doc_count": 0
            }
            "aggs": {
                '???': '???'
            }
        }
    }
}

изменить

Чтобы прояснить мой вопрос, мне нужно:

  • Получите общее количество документов, созданных за месяц (что уже сделано благодаря агрегированию date_histogram)
  • Получите количество рабочих дней в месяце
  • Разделите первое на второе.


person Thibault J    schedule 11.06.2015    source источник
comment
Определенно нужно обновить мой профиль…   -  person Thibault J    schedule 11.06.2015


Ответы (4)


В основном вам нужно что-то вроде этого (что не работает, поскольку это недоступная функция):

{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "docs_per_month": {
      "date_histogram": {
        "field": "date",
        "interval": "month",
        "min_doc_count": 0
      },
      "aggs": {
        "average": {
          "avg": {
            "script": "doc_count / 20"
          }
        }
      }
    }
  }
}

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

Но это будет возможно в ветви Elasticsearch 2.x, и на данный момент она активно развивается: https://github.com/elastic/elasticsearch/issues/8110 Эта новая функция добавит второй уровень управления результатами (сегментами) агрегации, и это не только ваш вариант использования, но и многие другие. .

Если вы не хотите попробовать какие-нибудь идеи или выполнить свои собственные расчеты в ваше приложение, вам нужно дождаться появления этой функции.

person Andrei Stefan    schedule 15.06.2015

Для всех, кто все еще заинтересован, теперь вы можете использовать агрегирование avg_bucket. Это все еще немного сложно, потому что вы не можете просто запустить avg_bucket с результатом date_historgram агрегации, но со вторичной value_count агрегацией с некоторым уникальным значением, и он отлично работает :)

{
  "size": 0,
  "aggs": {
    "orders_per_day": {
      "date_histogram": {
        "field": "orderedDate",
        "interval": "day"
      },
      "aggs": {
        "amount": {
          "value_count": {
            "field": "dateCreated"
          }
        }
      }
    },
    "avg_daily_order": {
      "avg_bucket": {
        "buckets_path": "orders_per_day>amount"
      }
    }
  }
}
person dularion    schedule 07.08.2018
comment
Этот ответ должен быть принятым. Он учитывает то, что можно сделать в отношении того, что они добавили в последних версиях. Теперь это более точный ответ, чем другие. - person SantiStSupery; 27.11.2018

Существует довольно запутанное и не очень эффективное решение с использованием следующего _ 1_ агрегирование.

{
  "size": 0,
  "query": {
    "match_all": {}
  },
  "aggs": {
    "docs_per_month": {
      "date_histogram": {
        "field": "created_date",
        "interval": "month",
        "min_doc_count": 0
      },
      "aggs": {
        "avg_doc_per_biz_day": {
          "scripted_metric": {
            "init_script": "_agg.bizdays = []; _agg.allbizdays = [:]; start = new DateTime(1970, 1, 1, 0, 0); now = new DateTime(); while (start < now) { def end = start.plusMonths(1); _agg.allbizdays[start.year + '_' + start.monthOfYear] = (start.toDate()..<end.toDate()).sum {(it.day != 6 && it.day != 0) ? 1 : 0 }; start = end; }",
            "map_script": "_agg.bizdays << _agg.allbizdays[doc. created_date.date.year+'_'+doc. created_date.date.monthOfYear]",
            "combine_script": "_agg.allbizdays = null; doc_count = 0; for (d in _agg.bizdays){ doc_count++ }; return doc_count / _agg.bizdays[0]",
            "reduce_script": "res = 0; for (a in _aggs) { res += a }; return res"
          }
        }
      }
    }
  }
}

Давайте подробно рассмотрим каждый сценарий ниже.

В init_script я создаю карту количества рабочих дней для каждого месяца с 1970 года и сохраняю ее на _agg.allbizdays карте.

_agg.bizdays = [];
_agg.allbizdays = [:]; 
start = new DateTime(1970, 1, 1, 0, 0);
now = new DateTime();
while (start < now) { 
    def end = start.plusMonths(1);     
    _agg.allbizdays[start.year + '_' + start.monthOfYear] = (start.toDate()..<end.toDate()).sum {(it.day != 6 && it.day != 0) ? 1 : 0 }; 
    start = end; 
}

В map_script я просто получаю количество дней недели для месяца каждого документа;

_agg.bizdays << _agg.allbizdays[doc.created_date.date.year + '_' + doc. created_date.date.monthOfYear];

В combine_script я суммирую среднее количество документов для каждого шарда.

_agg.allbizdays = null;
doc_count = 0; 
for (d in _agg.bizdays){ doc_count++ }; 
return doc_count / _agg.bizdays[0];

И, наконец, в reduce_script я суммирую среднее количество документов для каждого узла:

res = 0; 
for (a in _aggs) { res += a }; 
return res

Опять же, я думаю, что это довольно запутанно, и, как правильно сказал Андрей, вероятно, лучше дождаться версии 2.0, чтобы она заработала так, как должна, но пока что у вас есть это решение, если оно вам нужно.

person Val    schedule 15.06.2015

Вы хотите исключить документы с отметкой времени в субботу и воскресенье, чтобы вы могли исключить эти документы из своего запроса с помощью скрипта.

{
  "query": {
    "filtered": {
      "filter": {
        "script": {
          "script": "doc['@timestamp'].date.dayOfWeek != 7 && doc['@timestamp'].date.dayOfWeek != 6"
        }
      }
    }
  },
  "aggs": {
    "docs_per_month": {
      "date_histogram": {
        "field": "created_date",
        "interval": "month",
        "min_doc_count": 0
      },
      "aggs": {
        "docs_per_day": {
          "date_histogram": {
            "field": "created_date",
            "interval": "day",
            "min_doc_count": 0
          }
        },
        "aggs": {
          "docs_count": {
            "avg": {
              "field": ""
            }
          }
        }
      }
    }
  }
}

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

Кстати, вам нужно убедиться, что динамические сценарии включены, добавив это в свою elasticsearch.yml конфигурацию

script.disable_dynamic: false

Или добавьте отличный скрипт в / config / scripts и используйте отфильтрованный запрос со скриптом в фильтре.

person Julien C.    schedule 11.06.2015
comment
Спасибо за ответ. Однако я не хочу считать только документы, созданные в рабочие дни, мне нужно посчитать все документы за месяц (что я уже сделал), а затем разделить на количество рабочих дней. Я не знаю, как мне посчитать это число (рабочие дни в месяце). - person Thibault J; 12.06.2015
comment
Я отредактирую свой вопрос, потому что понимаю, что он может вводить в заблуждение. - person Thibault J; 12.06.2015