# Дикий \_score в match\_phrase\_prefix

**URL:** <https://discuss.elastic.co/t/score-match-phrase-prefix/249658>\
**Category:** Вопросы на русском языке\
**Created:** [September 23, 2020, 12:21pm UTC](https://discuss.elastic.co/t/score-match-phrase-prefix/249658 "2020-09-23T12:21:25Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![GreenX](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/greenx/32/51751_2.png) [@GreenX](https://discuss.elastic.co/u/GreenX)\
**Post date:** [September 23, 2020, 12:21pm UTC](https://discuss.elastic.co/t/score-match-phrase-prefix/249658/1 "2020-09-23T12:21:25Z")

</div>

Доброго времени суток!

Изначально задача выдавать результат в зависимости от приоритета подзапроса.  
Соответвенно, был dis\_max c match\_phrase, match\_phrase\_prefix и так далее. С boost соответвующим приоритету. По нашей логике match\_phrase, должен всегда быть выше остальных, затем match\_phrase\_prefix и так далее по самый мусор.

Результаты были не всегда правильными и я компенсировал это значительной разницей в boost (обычно на 2-3 порядка, 10000 -\> 100 -\> 1). На днях ко мне обратился коллега за помощью в решении подобной задачи... В общем, мы пришли к выводу, что это не самое правильное решение, т.к. зависит от данных.

В результате опытов мы выяснили, что на таких же запросах \_score match\_phrase\_prefix мог быть в 10-20 больше match\_phrase у того же документа. Выяснилось, что \_score зависит от количества префиксов попадающих в запрос. Т.е. делаем запрос максимально приближенных к простому match\_phrase:

```auto
{
  "explain": true,
  "from": 0,
  "size": 1,
  "_source": {
    "includes": [
      "title",
      "content"
    ]
  },
  "query": {
    "match_phrase_prefix": {
      "content.original": {
        "max_expansions": 1,
        "query": "налоговый анализ",
        "boost": 100
      }
    }
  }
}

```

Получаем ответ на 100% совпадающий с match\_phrase. Включаем explain и шаг за шагом увеличиваем max\_expansions. Видим, что количество секций в подсчете \_scale увеличивается в соответвенно. Видим, что на \_scale влияют слова которых даже нет в документе и они его не только не уменьшают (это бы м.б. я понял ), а увеличивают.

> **[{ "took" : 6, "timed\_out" : false, "\_shards" : { "total" : 2, -...](https://pastebin.com/8L0Gsiuf)**
>
> Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.

mapping простейший: `"tokenizer": "standard", "filter": "lowercase"`

Нашел подобную issue, но там тишина:

> <https://github.com/elastic/elasticsearch/issues/50577>
>
> Elasticsearch version (bin/elasticsearch --version):
> 6.3.2
> Plugins installed: \[\]
> JVM version (java -version):
> openjdk version "1.8.0\_222"
> OS version (uname -a if on a Unix-like system):
> Linux contrib-stretch 4.9.0-6-amd64...

**Вопрос: М.б. мы делаем, что-то не так и эту задачу можно решить другим путём?**  
P.S. естественно документы их количество и сам набор не менялись,

---

<div class="post-metadata">

**Author:** ![Igor\_Motov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/igor_motov/32/45193_2.png) [@Igor\_Motov](https://discuss.elastic.co/u/Igor_Motov)\
**Post date:** [September 23, 2020, 2:47pm UTC](https://discuss.elastic.co/t/score-match-phrase-prefix/249658/2 "2020-09-23T14:47:06Z")

</div>

Вы не могли бы более подробно описать задачу?

> Изначально задача выдавать результат в зависимости от приоритета подзапроса.

Тут может быть много трактовок. Хотелось-бы конкретных деталей.

---

<div class="post-metadata">

**Author:** ![GreenX](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/greenx/32/51751_2.png) [@GreenX](https://discuss.elastic.co/u/GreenX)\
**Post date:** [September 23, 2020, 3:12pm UTC](https://discuss.elastic.co/t/score-match-phrase-prefix/249658/3 "2020-09-23T15:12:17Z")

</div>

Есть коллекция документов. В простейшем варианте два поля "title","body".  
По запросу я должен выдать список в котором документы упорядочены так:

1. Сначала идут документы в которых есть точное совпадение фразы в поле "title".
2. Потом точное совпадение фразы с префиксом в "title"
3. Всё остальное с морфологией в "title". (по возможности без мусора AND или 75%)
4. Тоже самое по полю "body"
5. -\-
6. -\-

---

<div class="post-metadata">

**Author:** ![GreenX](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/greenx/32/51751_2.png) [@GreenX](https://discuss.elastic.co/u/GreenX)\
**Post date:** [September 24, 2020, 7:29am UTC](https://discuss.elastic.co/t/score-match-phrase-prefix/249658/4 "2020-09-24T07:29:55Z")

</div>

Так то и dis\_max устраивает, если бе не \_score в match\_phrase\_prefix.

---

<div class="post-metadata">

**Author:** ![Igor\_Motov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/igor_motov/32/45193_2.png) [@Igor\_Motov](https://discuss.elastic.co/u/Igor_Motov)\
**Post date:** [September 24, 2020, 2:34pm UTC](https://discuss.elastic.co/t/score-match-phrase-prefix/249658/5 "2020-09-24T14:34:30Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![GreenX](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/greenx/32/51751_2.png) [@GreenX](https://discuss.elastic.co/u/GreenX)\
**Post date:** [September 25, 2020, 6:02am UTC](https://discuss.elastic.co/t/score-match-phrase-prefix/249658/6 "2020-09-25T06:02:08Z")

</div>

> Я бы предпочел небольшой перехлест

Полностью с вами согласен. Примерно так и получается, но в некоторых конкретных случаях \_score в match\_phrase\_prefix очень перевешивает. Особенно на тестовых данных.

> Я бы прибавлял score

Через function\_score ?

---

<div class="post-metadata">

**Author:** ![Igor\_Motov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/igor_motov/32/45193_2.png) [@Igor\_Motov](https://discuss.elastic.co/u/Igor_Motov)\
**Post date:** [September 26, 2020, 4:21pm UTC](https://discuss.elastic.co/t/score-match-phrase-prefix/249658/7 "2020-09-26T16:21:25Z")

</div>

> [@GreenX](#):
>
> Особенно на тестовых данных.

Это обычно бывает, когда тестовых данных мало либо и не используется флаг [`dfs_query_then_fetch`](https://www.elastic.co/blog/understanding-query-then-fetch-vs-dfs-query-then-fetch), либо в тестовых данных много повторений, которые отсутствуют в реальных данных. Вообще, отладка релевантности на тестовых данных - дело бестолковое.

> [@GreenX](#):
>
> Через function\_score ?

Да, идея такая, чтобы разнести score. То есть самый нерелевантный запрос будет давать score между 0-10, следующий - 1000-1010, следующий 2000-2010 и т.д. То есть можно подобрать шаг так, чтобы они никак не пересекались.

---

<div class="post-metadata">

**Author:** ![GreenX](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/greenx/32/51751_2.png) [@GreenX](https://discuss.elastic.co/u/GreenX)\
**Post date:** [October 16, 2020, 2:12pm UTC](https://discuss.elastic.co/t/score-match-phrase-prefix/249658/8 "2020-10-16T14:12:47Z")

</div>

Только сел за реализацию. Получилось примерно следующее:

```auto
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "function_score": {
            "query": {
              "match_phrase": {
                "name.original": {
                  "query": "query"
                }
              }
            },
            "boost_mode": "replace",
            "script_score": {
              "script": {
                "source": "200000 + _score"
              }
            }
          }
        },
        {
          "function_score": {
            "query": {
              "match_phrase_prefix": {
                "name.original": {
                  "query": "query",
                  "max_expansions": 20
                }
              }
            },
            "boost_mode": "replace",
            "script_score": {
              "script": {
                "source": "100000 + _score"
              }
            }
          }
        }
      ]
    }
  }
}

```

В таком варианте `score` берется только из скрипта.

Если `boost_mode` оставить по-дефотлу, то `score` в сочетании `boost` в подзапроса `match_phrase_prefix` спокойно перебивает `score` в `match_phrase` (В моём случа там получаются миллионы).

И то, остается вопрос какую дельту закладывать в этом случае, т.к. все равно результат зависит от данных. Насколько я понимаю, минимум логарифм от общего количества документов в степени количества префиксов. (Но это не точно 😉 )

В идеале, получить бы `max_score` в подзапросе, чтобы можно было маштабировать итоговый `score` значение точно от "A" до "B" и/или иметь возможность настраивать вычисление `score` для `match_phrase_prefix`

---

<div class="post-metadata">

**Author:** ![Igor\_Motov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/igor_motov/32/45193_2.png) [@Igor\_Motov](https://discuss.elastic.co/u/Igor_Motov)\
**Post date:** [October 17, 2020, 5:25am UTC](https://discuss.elastic.co/t/score-match-phrase-prefix/249658/9 "2020-10-17T05:25:24Z")

</div>

> [@GreenX](#):
>
> Если `boost_mode` оставить по-дефотлу, то `score` в сочетании `boost` в подзапроса

По умолчанию происходит умножение (`multiply`). Вам нужно сложение (`sum`) если использовать `"weight" : 100000` или `replace` если использовать скрипт, как у вас.

> [@GreenX](#):
>
> (В моём случа там получаются миллионы).

Можно поделить эти миллионы на 10,000, или взять от них логарифм, или еще как-нибудь привести из в чувства чтобы они точно поместились в 100,000. Конкретное значение ведь не так важно. Важно чтобы порядок значений не менялся.

---

<div class="post-metadata">

**Author:** ![GreenX](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/greenx/32/51751_2.png) [@GreenX](https://discuss.elastic.co/u/GreenX)\
**Post date:** [October 18, 2020, 12:12pm UTC](https://discuss.elastic.co/t/score-match-phrase-prefix/249658/10 "2020-10-18T12:12:34Z")

</div>

> [@Igor\_Motov](#):
>
> Конкретное значение ведь не так важно.

Как раз важно, т.к. надо попасть в диапазон, чтобы один подзапрос не пересекался с другим. Теоретически`score` лежит в диазоне от 0 до +∞, поэтому мы не можем гарантированно ограничить вернюю границу.

---

<div class="post-metadata">

**Author:** ![Igor\_Motov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/igor_motov/32/45193_2.png) [@Igor\_Motov](https://discuss.elastic.co/u/Igor_Motov)\
**Post date:** [October 19, 2020, 4:41pm UTC](https://discuss.elastic.co/t/score-match-phrase-prefix/249658/11 "2020-10-19T16:41:39Z")

</div>

> [@GreenX](#):
>
> Теоретически `score` лежит в диазоне от 0 до +∞

Теоретически, оно ограничено float в java. Так что score не может превышать 3.40282347e38. log от него - 38.5.

Практически, оно скорее всего меньше. Я бы попробовал на практике посмотреть куда это значение обычно влезает, взял бы запасов в 10 раз и добавил бы условие, что если оно максимум превышает, то дать ему это максимальное значение.

---

<div class="post-metadata">

**Author:** ![GreenX](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/greenx/32/51751_2.png) [@GreenX](https://discuss.elastic.co/u/GreenX)\
**Post date:** [October 19, 2020, 5:18pm UTC](https://discuss.elastic.co/t/score-match-phrase-prefix/249658/12 "2020-10-19T17:18:47Z")

</div>

> [@Igor\_Motov](#):
>
> Теоретически, оно ограничено float в java. Так что score не может превышать 3.40282347e38. log от него - 38.5.

Это технически, а не теоретически... 😄 в общем, да что-то я не подумал, что скору всё ранво сколько у меня индексов в алисе.  
В целом, так и сделал - разделил с запасом.  
Спасибо!

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)\
**Post date:** [November 16, 2020, 5:18pm UTC](https://discuss.elastic.co/t/score-match-phrase-prefix/249658/13 "2020-11-16T17:18:53Z")

</div>

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.
