asticsearch - фильтр по процентили - PullRequest
0 голосов
/ 04 мая 2018

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

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

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

Мне нужно отфильтровать верхние десятые из них по 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. Я могу изменить конфигурацию, конечно, но всегда есть ограничение).

1 Ответ

0 голосов
/ 15 мая 2018

Я сомневаюсь, что есть способ сделать это в одном запросе, если точные значения 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 .

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

...