ElasticSearch: возможно ли выполнить взвешенное среднее агрегирование, взвешенное по баллу?

Я пытаюсь выполнить среднее значение по полю цены (price.avg). Но я хочу, чтобы лучшие совпадения запроса оказывали большее влияние на среднее значение, чем последние, поэтому среднее значение должно быть взвешено по вычисляемому полю оценки. Это агрегация, которую я реализую.

{
    "query": {...},
    "size": 100,
    "aggs": {
        "weighted_avg_price": {
            "weighted_avg": {
                "value": {
                    "field": "price.avg"
                },
                "weight": {
                    "script": "_score"
                }
            }
        }
    }
}

Он должен дать мне то, что я хочу. Но вместо этого я получаю нулевое значение:

{...
    "hits": {...},
    "aggregations": {
        "weighted_avg_price": {
            "value": null
        }
    }
}

Есть что-то, чего мне не хватает? Выполним ли этот запрос агрегации? Есть ли обходной путь?


person Junqueror    schedule 27.04.2020    source источник


Ответы (2)


Когда вы отлаживаете то, что доступно внутри script

GET prices/_search
{
  "size": 0,
  "aggs": {
    "weighted_avg_price": {
      "weighted_avg": {
        "value": {
          "field": "price"
        },
        "weight": {
          "script": "Debug.explain(new ArrayList(params.keySet()))"
        }
      }
    }
  }
}

следующее выплевывается

[doc, _source, _doc, _fields]

Ни один из них не содержит информации о запросе _score, к которому вы пытаетесь получить доступ, поскольку агрегаты работают в контексте, отдельном от оценки на уровне запроса. Это означает, что значение weight должно либо

  • существуют в документе или
  • существовать в документе + быть модифицируемый или
  • быть константой времени запроса (например, 42 или 0.1)

Обходной путь может состоять в том, чтобы применить математическую функцию к полученному price, например

"script": "Math.pow(doc.price.value, 0.5)"

person Joe Sorocin    schedule 27.04.2020
comment
Большое спасибо. Не знал о опции отладки. Я догадался, что могу использовать только значения doc, но не мог этого подтвердить. Ваш обходной путь сработает, если документы оцениваются по средней цене, но это не так. Упрощение также будет работать для меня: вычислить агрегацию только в первых N результатах (N лучших результатов). Но я тоже не смог добиться этого, я полагаю, это также потому, что оценка недоступна на уровне агрегации. - person Junqueror; 27.04.2020
comment
Вот так. Вы можете написать собственные функции оценки в запросе, а затем отсортировать их по пользовательским сценариям/функциям. Насколько я понимаю, вас больше интересуют метрики на уровне документа, а не на основе агг. Я прав? - person Joe Sorocin; 27.04.2020
comment
Я думаю нет. Потому что мне нужно среднее значение поля из всех документов (цена), но рассчитать его, придавая большее значение лучшим результатам в запросе. Это должно быть более реалистичным/релевантным, чем обычное среднее значение, поскольку пользователи приложения с большей вероятностью выберут один из лучших результатов оцениваемого запроса (который оценивается не по цене, а по другим условиям). - person Junqueror; 28.04.2020
comment
Если эта оценка не зависит от запроса, вы можете добавить отдельное поле score_weight в документы, а затем использовать эту константу в своем взвешенном_среднем. Я считаю, что другого выхода нет. - person Joe Sorocin; 28.04.2020
comment
Нет, это не так, так как _score вычисляется запросом во время запроса - person Junqueror; 28.04.2020

@jzzfs Я пытаюсь использовать подход «среднее из первых N результатов (упорядоченных по _score)», используя агрегация лучших результатов:

{
    "query": {
        "bool": {
            "should": [
                ...
            ],
            "minimum_should_match": 0
        }
    },
    "size": 0,
    "from": 0,
    "sort": [
        {
            "_score": {
                "order": "desc"
            }
        }
    ],
    "aggs": {
        "top_avg_price": {
            "avg": {
                "field": "price.max"
            }
        },
        "aggs": {
            "top_hits": {
                "size": 10, // N: Changing the number of results doesn't change the top_avg_price 
                "_source": {
                    "includes": [
                        "price.max"
                    ]
                }
            }
        }
    },
    "explain": "false"
}

Среднее агрегирование выполняется по основным результатам, а не по top_hits агрегированию. Я предполагаю, что top_avg_rpice должен быть субагрегацией top_hits. Но я думаю, что это не возможно банкомат.

person Junqueror    schedule 28.04.2020