Эластичная агрегация для определения процентного увеличения периода A по сравнению с периодом B

У меня есть данные о ежедневных продажах, проиндексированные в Elasticsearch. Я успешно запускаю ряд агрегаций для определения лидеров продаж в диапазоне дат и т. д.

Теперь я пытаюсь написать один запрос, чтобы сделать следующее:

  • Определить n лучших продавцов за диапазон дат (период A)
  • Возьмите результаты периода A и просуммируйте продажи этих продуктов за второй диапазон дат (период B).
  • Сравните продажи в период A с периодом B и определите те, в которых процентное увеличение превышает X%.

Моя попытка до сих пор:

{
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "date": {
              "gte": "2017-10-01",
              "lte": "2017-10-14"
            }
          }
        }
      ]
    }
  },
  "size": 0,
  "aggs": {
    "data_split": {
      "terms": {
        "size": 10,
        "field": "product_id"
      },
      "aggs": {
        "date_periods": {
          "date_range": {
            "field": "date",
            "format": "YYYY-MM-dd",
            "ranges": [
              {
                "from": "2017-10-01",
                "to": "2017-10-07"
              },
              {
                "from": "2017-10-08",
                "to": "2017-10-14"
              }
            ]
          },
          "aggs": {
            "product_id_split": {
              "terms": {
                "field": "product_id"
              },
              "aggs": {
                "unit_sum": {
                  "sum": {
                    "field": "units"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Хотя это выводит результаты для двух периодов, я не думаю, что это совсем то, что мне нужно, поскольку первоначальный фильтр работает от даты начала периода A до даты окончания периода B, и я думаю, что результаты суммируются для этого диапазона, а не только для периода A. Я также не получаю сравнения%, я, вероятно, сделал бы это на уровне своего приложения, но я понимаю, что это может быть обработано с помощью скриптового эластичного запроса?

Было бы особенно здорово, если бы вместо первых n результатов в период А я мог установить порог продаж, скажем, в 1000 продаж.

Любые указатели будут высоко оценены. Заранее спасибо!

В настоящее время работает Elastic 5.6.


person Raoot    schedule 23.11.2017    source источник


Ответы (1)


{
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "date": {
              "gte": "2017-10-01",
              "lte": "2017-10-14"
            }
          }
        }
      ]
    }
  },
  "size": 0,
  "aggs": {
    "data_split": {
      "terms": {
        "size": 10,
        "field": "product_id"
      },
      "aggs": {
        "date_period1": {
          "filter": {
            "range": {
              "date": {
                "gte": "2017-10-01",
                "lte": "2017-10-07"
              }
            }
          },
          "aggs": {
            "unit_sum": {
              "sum": {
                "field": "units"
              }
            }
          }
        },
        "date_period2": {
          "filter": {
            "range": {
              "date": {
                "gte": "2017-10-08",
                "lte": "2017-10-14"
              }
            }
          },
          "aggs": {
            "unit_sum": {
              "sum": {
                "field": "units"
              }
            }
          }
        },
        "percentage_increase": {
          "bucket_script": {
            "buckets_path": {
              "firstPeriod": "date_period1>unit_sum",
              "secondPeriod": "date_period2>unit_sum"
            },
            "script": "(params.secondPeriod-params.firstPeriod)*100/params.firstPeriod"
          }
        },
        "retain_buckets": {
          "bucket_selector": {
            "buckets_path": {
              "percentage": "percentage_increase"
            },
            "script": "params.percentage > 5"
          }
        }
      }
    }
  }
}

И полные тестовые данные в этом суть.

Результат этой агрегации дает вам следующее:

  "aggregations": {
    "data_split": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "A",
          "doc_count": 6,
          "date_period1": {
            "doc_count": 3,
            "unit_sum": {
              "value": 150
            }
          },
          "date_period2": {
            "doc_count": 3,
            "unit_sum": {
              "value": 160
            }
          },
          "percentage_increase": {
            "value": 6.666666666666667
          }
        },
        {
          "key": "C",
          "doc_count": 2,
          "date_period1": {
            "doc_count": 1,
            "unit_sum": {
              "value": 50
            }
          },
          "date_period2": {
            "doc_count": 1,
            "unit_sum": {
              "value": 70
            }
          },
          "percentage_increase": {
            "value": 40
          }
        }
      ]
    }
  }

Идея состоит в том, что вы используете два агрегирования типа filter для двух интервалов дат. И для каждого вы вычисляете сумму. Затем, используя третью агрегацию типа bucket_script, вы вычисляете процентное увеличение (обратите внимание, что это будет отрицательное число, например, при снижении продаж). Затем, используя еще одну агрегацию — типа bucket_selector — вы сохраняете product_id, где процент больше 5%.

person Andrei Stefan    schedule 08.12.2017
comment
Отлично выглядит, Андрей, сейчас попробую. - person Raoot; 11.12.2017