elasticsearch - фильтровать по процентилю

Скажем, хочу ли я фильтровать документы по какому-либо полю в пределах от 10-го до 20-го процентиля. Мне интересно, возможно ли это с помощью какого-то простого запроса, например {"fieldName":{"percentile": [0.1, 0.2]}}.

Скажем, у меня есть эти документы:

[{"a":1,"b":101},{"a":2,"b":102},{"a":3,"b":103}, ..., {"a":100,"b":200}]

Мне нужно отфильтровать первые 10 из них по a (в порядке возрастания), это будет a от 1 до 10. Затем мне нужно отсортировать эти результаты по b в порядке убывания, а затем взять результат с разбивкой на страницы (например, страница № 2 , по 10 элементов на каждой странице).

Одно из возможных решений:

  1. получить общее количество документов.

  2. отсортировать документы по a, взять соответствующие _id с ограничением 0.1 * total_count

  3. напишите окончательный запрос, что-то вроде id in (...) order by b

Но недостатки тоже довольно очевидны:

  1. кажется неэффективным, если мы говорим о задержке в доли секунды

  2. второй запрос может не работать, если мы вернули слишком много _id в первом запросе (по умолчанию ES разрешает только 1000. Я, конечно, могу изменить конфигурацию, но всегда есть предел).


person Dean Winchester    schedule 04.05.2018    source источник
comment
Не могли бы вы объяснить процентиль того, что вы хотите получить от этого запроса? Не могли бы вы предоставить несколько примеров документов и ожидаемый ответ?   -  person Nikolay Vasiliev    schedule 04.05.2018
comment
@NikolayVasiliev Отредактировано почтой, надеюсь, стало понятнее.   -  person Dean Winchester    schedule 14.05.2018


Ответы (1)


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

Я бы предложил сделать percentiles агрегация в качестве первого запроса и range запрос вторым.

В моем выборочном индексе у меня всего 14 документов, поэтому для пояснения я попытаюсь найти те документы, которые составляют от 30% до 60% поля a и отсортирую их по полю b в обратном порядке (чтобы убедиться, что сортировка сработала) .

Вот документы, которые я вставил:

{"a":1,"b":101}
{"a":5,"b":105}
{"a":10,"b":110}
{"a":2,"b":102}
{"a":6,"b":106}
{"a":7,"b":107}
{"a":9,"b":109}
{"a":4,"b":104}
{"a":8,"b":108}
{"a":12,"b":256}
{"a":13,"b":230}
{"a":14,"b":215}
{"a":3,"b":103}
{"a":11,"b":205}

Давайте выясним, каковы границы поля a между 30% и 60% процентилями:

POST my_percent/doc/_search
{
    "size": 0,
    "aggs" : {
        "percentiles" : {
            "percentiles" : {
                "field" : "a",
                "percents": [ 30, 60, 90 ]
            }
        }
    }
}

С моим образцом индекса это выглядит так:

{
...
  "hits": {
    "total": 14,
    "max_score": 0,
    "hits": []
  },
  "aggregations": {
    "percentiles": {
      "values": {
        "30.0": 4.9,
        "60.0": 8.8,
        "90.0": 12.700000000000001
      }
    }
  }
}

Теперь мы можем использовать границы для выполнения запроса range:

POST my_percent/doc/_search
{
    "query": {
      "range": {
            "a" : {
                "gte" : 4.9,
                "lte" : 8.8
            }
        }
    },
    "sort": {
      "b": "desc"
    }
}

И результат:

{
  "took": 5,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 4,
    "max_score": null,
    "hits": [
      {
        "_index": "my_percent",
        "_type": "doc",
        "_id": "vkFvYGMB_zM1P5OLcYkS",
        "_score": null,
        "_source": {
          "a": 8,
          "b": 108
        },
        "sort": [
          108
        ]
      },
      {
        "_index": "my_percent",
        "_type": "doc",
        "_id": "vUFvYGMB_zM1P5OLWYkM",
        "_score": null,
        "_source": {
          "a": 7,
          "b": 107
        },
        "sort": [
          107
        ]
      },
      {
        "_index": "my_percent",
        "_type": "doc",
        "_id": "vEFvYGMB_zM1P5OLRok1",
        "_score": null,
        "_source": {
          "a": 6,
          "b": 106
        },
        "sort": [
          106
        ]
      },
      {
        "_index": "my_percent",
        "_type": "doc",
        "_id": "u0FvYGMB_zM1P5OLJImy",
        "_score": null,
        "_source": {
          "a": 5,
          "b": 105
        },
        "sort": [
          105
        ]
      }
    ]
  }
}

Обратите внимание, что результаты агрегирования percentiles являются приблизительными.

В целом, похоже, эту задачу лучше решают панды или задание Spark.

Надеюсь, это поможет!

person Nikolay Vasiliev    schedule 14.05.2018
comment
Гораздо лучше, чем мой подход! Я думаю, что это достаточно хорошо для меня. - person Dean Winchester; 15.05.2018