# Сортировка по началу запроса

**URL:** <https://discuss.elastic.co/t/topic/198575>\
**Category:** Вопросы на русском языке\
**Created:** [September 8, 2019, 6:48pm UTC](https://discuss.elastic.co/t/topic/198575 "2019-09-08T18:48:51Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![111206](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/111206/32/53788_2.png) [@111206](https://discuss.elastic.co/u/111206)\
**Post date:** [September 8, 2019, 6:48pm UTC](https://discuss.elastic.co/t/topic/198575/1 "2019-09-08T18:48:51Z")

</div>

Использую эластик как поиск вместо БД.  
Не могу добиться правильной релевантности результатов и не очень понимаю в какую сторону копать. Нужна помошь в этом понимании.  
Есть поле "наименование" и хочется видеть по нему релевантность ответов как при автокомплите, те сначала совпадение по всему, потом совпадение по началу, а после по вхождению. Причём могут быть одновременно много  
разных фильтров, но именно по полю наименование нужна "автокомплитоподобная" сортировка.

Сделал поле "наименование" с типом текст со своим edge\_ngram токенайзером, разбивающим от 1‑го до 10. Делаю match query и получаю результат, где подстрока находится в рандомных местах поля.  
Пример как должно быть:  
пишем слово Иван  
В ответ  
Иван  
Иван и сыновья  
Иванов  
Автоматизированные системы ивановского

Как бы можно было добиться результата в SQL  
ORDER BY name ILIKE concat(#{подстрока}, '%') DESC, name

Сейчас нет идей как можно добиться подобного результата, кроме как посылать первый запрос regexp "\*подстрока", а после него второй, что явно костыли.

---

<div class="post-metadata">

**Author:** ![111206](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/111206/32/53788_2.png) [@111206](https://discuss.elastic.co/u/111206)\
**Post date:** [September 9, 2019, 11:03am UTC](https://discuss.elastic.co/t/topic/198575/2 "2019-09-09T11:03:28Z")

</div>

Узнал, что в эластике есть скоринг и он конечно значительно правит результат поиска в нужную сторону, но не работает идеально (

---

<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 9, 2019, 2:00pm UTC](https://discuss.elastic.co/t/topic/198575/3 "2019-09-09T14:00:38Z")

</div>

> [@111206](#):
>
> но не работает идеально

там много ручек, которые можно подергать. Давайте разбираться, что не идеально.

---

<div class="post-metadata">

**Author:** ![111206](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/111206/32/53788_2.png) [@111206](https://discuss.elastic.co/u/111206)\
**Post date:** [September 9, 2019, 6:53pm UTC](https://discuss.elastic.co/t/topic/198575/4 "2019-09-09T18:53:27Z")

</div>

Ok, попробую описать с максимальной детализацией.

Маппинг самого поля от которого хочу нужной мне релевантности

> **Маппинг**
>
> ```auto
> "names": {
> "properties": {
> "entityCode": {
> "type": "keyword"
> },
> "localizedName": {
> "type": "nested",
> "properties": {
> "lang": {
> "type": "keyword"
> },
> "name": {
> "type": "text",
> "fields": {
> "sort": {
> "type": "keyword"
> }
> },
> "analyzer": "partial_analyzer"
> }
> }
> }
> }
> }
> 
> ```

Представляет собой структуру для хранения пар язык - наименование.

Использованный в нём аналайзер

> **Аналайзер**
>
> ```auto
> "analysis": {
> "analyzer": {
> "partial_analyzer": {
> "filter": [
> "lowercase"
> ],
> "type": "custom",
> "tokenizer": "partial_tokenizer"
> }
> },
> "tokenizer": {
> "partial_tokenizer": {
> "token_chars": [
> "letter",
> "digit"
> ],
> "type": "ngram",
> "min_gram": "1",
> "max_gram": "10"
> }
> }
> }
> 
> ```

Построенный запрос на поиск в java выливается в такой вот json

> **Пример поискового запроса**
>
> ```auto
> { 
> "from":0,
> "size":50,
> "query":{ 
> "bool":{ 
> "must":[ 
> { 
> "nested":{ 
> "query":{ 
> "bool":{ 
> "must":[ 
> { 
> "match":{ 
> "names.localizedName.name":{ 
> "query":"арт",
> "operator":"AND",
> "prefix_length":0,
> "max_expansions":50,
> "fuzzy_transpositions":true,
> "lenient":false,
> "zero_terms_query":"NONE",
> "auto_generate_synonyms_phrase_query":true,
> "boost":1.0
> }
> }
> }
> ],
> "adjust_pure_negative":true,
> "boost":1.0
> }
> },
> "path":"names.localizedName",
> "ignore_unmapped":false,
> "score_mode":"avg",
> "boost":1.0
> }
> }
> ],
> "adjust_pure_negative":true,
> "boost":1.0
> }
> }
> }
> 
> ```

До скоринга была вообще беда и у меня были сомнения, что как то это можно упорядочить, был полный рандом по вхождению.  
С использованием скоринга на вышестоящий запрос возвращается ответ вида

```auto
АРТ"
АРТ+
АРТУР
Рябов Артур Артур
Клюкин Артем Артем
ТК Арт
АРТУР.
АРТЕМ
АРТУР
Артур
АРТ И КО

```

Первые два ок. Но почему строки Рябов и Клюкин вмешиваются? Почему АРТ и КО находится внизу?

---

<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 10, 2019, 1:31pm UTC](https://discuss.elastic.co/t/topic/198575/5 "2019-09-10T13:31:32Z")

</div>

> [@111206](#):
>
> Но почему строки Рябов и Клюкин вмешиваются?

Потому что у вас не edge ngram, a обычный ngram. У вас вот что индексирутся:

```auto
POST test/_analyze
{
  "text": ["Рябов Артур Артур"],
  "field": "localizedName.name"
}

{
  "tokens" : [
    {
      "token" : "р",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "ря",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "ряб",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "word",
      "position" : 2
    },
    {
      "token" : "рябо",
      "start_offset" : 0,
      "end_offset" : 4,
      "type" : "word",
      "position" : 3
    },
    {
      "token" : "рябов",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "word",
      "position" : 4
    },
    {
      "token" : "я",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "word",
      "position" : 5
    },
    {
      "token" : "яб",
      "start_offset" : 1,
      "end_offset" : 3,
      "type" : "word",
      "position" : 6
    },
    {
      "token" : "ябо",
      "start_offset" : 1,
      "end_offset" : 4,
      "type" : "word",
      "position" : 7
    },
    {
      "token" : "ябов",
      "start_offset" : 1,
      "end_offset" : 5,
      "type" : "word",
      "position" : 8
    },
    {
      "token" : "б",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "word",
      "position" : 9
    },
    {
      "token" : "бо",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "word",
      "position" : 10
    },
    {
      "token" : "бов",
      "start_offset" : 2,
      "end_offset" : 5,
      "type" : "word",
      "position" : 11
    },
    {
      "token" : "о",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "word",
      "position" : 12
    },
    {
      "token" : "ов",
      "start_offset" : 3,
      "end_offset" : 5,
      "type" : "word",
      "position" : 13
    },
    {
      "token" : "в",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "word",
      "position" : 14
    },
    {
      "token" : "а",
      "start_offset" : 6,
      "end_offset" : 7,
      "type" : "word",
      "position" : 15
    },
    {
      "token" : "ар",
      "start_offset" : 6,
      "end_offset" : 8,
      "type" : "word",
      "position" : 16
    },
    {
      "token" : "арт",
      "start_offset" : 6,
      "end_offset" : 9,
      "type" : "word",
      "position" : 17
    },
    {
      "token" : "арту",
      "start_offset" : 6,
      "end_offset" : 10,
      "type" : "word",
      "position" : 18
    },
    {
      "token" : "артур",
      "start_offset" : 6,
      "end_offset" : 11,
      "type" : "word",
      "position" : 19
    },
    {
      "token" : "р",
      "start_offset" : 7,
      "end_offset" : 8,
      "type" : "word",
      "position" : 20
    },
    {
      "token" : "рт",
      "start_offset" : 7,
      "end_offset" : 9,
      "type" : "word",
      "position" : 21
    },
    {
      "token" : "рту",
      "start_offset" : 7,
      "end_offset" : 10,
      "type" : "word",
      "position" : 22
    },
    {
      "token" : "ртур",
      "start_offset" : 7,
      "end_offset" : 11,
      "type" : "word",
      "position" : 23
    },
    {
      "token" : "т",
      "start_offset" : 8,
      "end_offset" : 9,
      "type" : "word",
      "position" : 24
    },
    {
      "token" : "ту",
      "start_offset" : 8,
      "end_offset" : 10,
      "type" : "word",
      "position" : 25
    },
    {
      "token" : "тур",
      "start_offset" : 8,
      "end_offset" : 11,
      "type" : "word",
      "position" : 26
    },
    {
      "token" : "у",
      "start_offset" : 9,
      "end_offset" : 10,
      "type" : "word",
      "position" : 27
    },
    {
      "token" : "ур",
      "start_offset" : 9,
      "end_offset" : 11,
      "type" : "word",
      "position" : 28
    },
    {
      "token" : "р",
      "start_offset" : 10,
      "end_offset" : 11,
      "type" : "word",
      "position" : 29
    },
    {
      "token" : "а",
      "start_offset" : 12,
      "end_offset" : 13,
      "type" : "word",
      "position" : 30
    },
    {
      "token" : "ар",
      "start_offset" : 12,
      "end_offset" : 14,
      "type" : "word",
      "position" : 31
    },
    {
      "token" : "арт",
      "start_offset" : 12,
      "end_offset" : 15,
      "type" : "word",
      "position" : 32
    },
    {
      "token" : "арту",
      "start_offset" : 12,
      "end_offset" : 16,
      "type" : "word",
      "position" : 33
    },
    {
      "token" : "артур",
      "start_offset" : 12,
      "end_offset" : 17,
      "type" : "word",
      "position" : 34
    },
    {
      "token" : "р",
      "start_offset" : 13,
      "end_offset" : 14,
      "type" : "word",
      "position" : 35
    },
    {
      "token" : "рт",
      "start_offset" : 13,
      "end_offset" : 15,
      "type" : "word",
      "position" : 36
    },
    {
      "token" : "рту",
      "start_offset" : 13,
      "end_offset" : 16,
      "type" : "word",
      "position" : 37
    },
    {
      "token" : "ртур",
      "start_offset" : 13,
      "end_offset" : 17,
      "type" : "word",
      "position" : 38
    },
    {
      "token" : "т",
      "start_offset" : 14,
      "end_offset" : 15,
      "type" : "word",
      "position" : 39
    },
    {
      "token" : "ту",
      "start_offset" : 14,
      "end_offset" : 16,
      "type" : "word",
      "position" : 40
    },
    {
      "token" : "тур",
      "start_offset" : 14,
      "end_offset" : 17,
      "type" : "word",
      "position" : 41
    },
    {
      "token" : "у",
      "start_offset" : 15,
      "end_offset" : 16,
      "type" : "word",
      "position" : 42
    },
    {
      "token" : "ур",
      "start_offset" : 15,
      "end_offset" : 17,
      "type" : "word",
      "position" : 43
    },
    {
      "token" : "р",
      "start_offset" : 16,
      "end_offset" : 17,
      "type" : "word",
      "position" : 44
    }
  ]
}

```

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

---

<div class="post-metadata">

**Author:** ![111206](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/111206/32/53788_2.png) [@111206](https://discuss.elastic.co/u/111206)\
**Post date:** [September 10, 2019, 2:59pm UTC](https://discuss.elastic.co/t/topic/198575/6 "2019-09-10T14:59:55Z")

</div>

Моя вина, ошибся с описанием (

> **Текущая конфигурация**
>
> ```auto
> {
> "codec": "best_compression",
> "analysis": {
> "analyzer": {
> "partial_analyzer": {
> "filter": [
> "lowercase"
> ],
> "type": "custom",
> "tokenizer": "partial_tokenizer"
> }
> },
> "tokenizer": {
> "partial_tokenizer": {
> "token_chars": [
> "letter",
> "digit"
> ],
> "type": "edge_ngram",
> "min_gram": "1",
> "max_gram": "10"
> }
> }
> }
> }
> 
> ```

Поведение сохраняется. Видимо, что-то не так с поисковым запросом. Я не очень понимаю что имеется в виду и где я использую анализатор (

---

<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 10, 2019, 3:35pm UTC](https://discuss.elastic.co/t/topic/198575/7 "2019-09-10T15:35:20Z")

</div>

> [@111206](#):
>
> Я не очень понимаю что имеется в виду и где я использую анализатор

При поиске по n-gram-ам обычно n-gram-ы только применяются к индексированию. Поиск осуществаялется по полной строке. В противном случае у вас будет доминровать префикс. То есть в качестве [search\_analyzer](https://www.elastic.co/guide/en/elasticsearch/reference/7.4/search-analyzer.html) можно применить такойже анализатор только с `keyword` токенизатором.

Ваши результаты получены с [dfs\_query\_then\_fetch](https://www.elastic.co/guide/en/elasticsearch/reference/7.4/search-request-body.html#dfs-query-then-fetch) или без?

---

<div class="post-metadata">

**Author:** ![111206](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/111206/32/53788_2.png) [@111206](https://discuss.elastic.co/u/111206)\
**Post date:** [September 11, 2019, 12:38pm UTC](https://discuss.elastic.co/t/topic/198575/8 "2019-09-11T12:38:15Z")

</div>

> [@Igor\_Motov](#):
>
> Ваши результаты получены с [dfs\_query\_then\_fetch](https://www.elastic.co/guide/en/elasticsearch/reference/7.4/search-request-body.html#dfs-query-then-fetch) или без

Полный урл запроса, созданный при помощи Java библиотеки выглядит так

> /\_search?typed\_keys=true&ignore\_unavailable=false&expand\_wildcards=open&allow\_no\_indices=true&search\_type=query\_then\_fetch&batched\_reduce\_size=512

Я так понял в маппинг надо добавить ещё search\_analyzer

> **Как то так?**
>
> ```
> "names": {
> "properties": {
> "entityCode": {
> "type": "keyword"
> },
> "localizedName": {
> "type": "nested",
> "properties": {
> "lang": {
> "type": "keyword"
> },
> "name": {
> "type": "text",
> "fields": {
> "sort": {
> "type": "keyword"
> }
> },
> "analyzer": "partial_analyzer",
> "search_analyzer": "keyword"
> }
> }
> }
> }
> }
> 
> ```

Возможно имелось в виду что надо создать второй кастомный аналайзер у которого токенайзер - кейворд. Но не совсем понимаю как создать так тимплейт с маппингом и настройками, что бы описать там два разных аналайзера.  
Вышеописанная конфигурация не помогает(

---

<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 11, 2019, 6:26pm UTC](https://discuss.elastic.co/t/topic/198575/9 "2019-09-11T18:26:54Z")

</div>

Вот этот кусок `search_type=query_then_fetch` должен быть `search_type=dfs_query_then_fetch` в java это достигается так:

```auto
request.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)

```

Проблема с `"search_analyzer": "keyword"` в том, что это не будет работать с пробелами пунктуацией и верхним регистром. Надо чтобы search analyzer делал все тоже, что и index кроме n-gram.

---

<div class="post-metadata">

**Author:** ![111206](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/111206/32/53788_2.png) [@111206](https://discuss.elastic.co/u/111206)\
**Post date:** [September 11, 2019, 6:46pm UTC](https://discuss.elastic.co/t/topic/198575/10 "2019-09-11T18:46:01Z")

</div>

А можно/как создать тимплейт сразу с двумя аналайзерами? Не нашёл никаких подтверждений/ примеров что их вообще можно много и не нашёл документации как бы это делать

> **Тимлейт**
>
> /\_template/template\_name?master\_timeout=30s  
> {  
> "index\_patterns": [  
> "название индекса"  
> ],  
> "order": 0,  
> "version": 1,  
> "settings": {  
> "analysis": {  
> "analyzer": {  
> "partial\_analyzer": {  
> "filter": [  
> "lowercase"  
> ],  
> "type": "custom",  
> "tokenizer": "partial\_tokenizer"  
> }  
> },  
> "tokenizer": {  
> "partial\_tokenizer": {  
> "token\_chars": [  
> "letter",  
> "digit"  
> ],  
> "min\_gram": "1",  
> "type": "edge\_ngram",  
> "max\_gram": "10"  
> }  
> }  
> },
> 
> ```
> "codec": "best_compression"
> },
> "mappings": {"маппинг},
> "aliases": {}
> }
> 
> ```

---

<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 11, 2019, 6:51pm UTC](https://discuss.elastic.co/t/topic/198575/11 "2019-09-11T18:51:08Z")

</div>

> [@111206](#):
>
> А можно/как создать тимплейт сразу с двумя аналайзерами?

Да сколько хотите:

```auto
"settings": {
  "analysis": {
    "analyzer": {
        "my_first_analyzer": { ..... },
        "my_second_analyzer": { ..... },
        "my_third_analyzer": { ..... }
    }
  }
}

```

---

<div class="post-metadata">

**Author:** ![111206](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/111206/32/53788_2.png) [@111206](https://discuss.elastic.co/u/111206)\
**Post date:** [September 11, 2019, 9:59pm UTC](https://discuss.elastic.co/t/topic/198575/12 "2019-09-11T21:59:00Z")

</div>

К сожалению, не вижу никаких изменений после этого(  
Возможно всё ещё делаю что то не то и не так  
Урл по которому зову

```
http://localhost:9200/index_name/_search?typed_keys=true&ignore_unavailable=false&expand_wildcards=open&allow_no_indices=true&search_type=dfs_query_then_fetch&batched_reduce_size=512

```

> **Запрос**
>
> ```
> {
> "from": 0,
> "size": 50,
> "query": {
> "bool": {
> "must": [
> {
> "nested": {
> "query": {
> "bool": {
> "must": [
> {
> "match": {
> "names.localizedName.name": {
> "query": "арт",
> "operator": "AND",
> "prefix_length": 0,
> "max_expansions": 50,
> "fuzzy_transpositions": true,
> "lenient": false,
> "zero_terms_query": "NONE",
> "auto_generate_synonyms_phrase_query": true,
> "boost": 1
> }
> }
> }
> ],
> "adjust_pure_negative": true,
> "boost": 1
> }
> },
> "path": "names.localizedName",
> "ignore_unmapped": false,
> "score_mode": "avg",
> "boost": 1
> }
> }
> ],
> "adjust_pure_negative": true,
> "boost": 1
> }
> }
> }
> 
> ```

> **Маппинг запрашиваемого поля**
>
> ```
> "names": {
> "properties": {
> "entityCode": {
> "type": "keyword"
> },
> "localizedName": {
> "type": "nested",
> "properties": {
> "lang": {
> "type": "keyword"
> },
> "name": {
> "type": "text",
> "fields": {
> "sort": {
> "type": "keyword"
> }
> },
> "analyzer": "partial_analyzer",
> "search_analyzer": "search_keyword_analyzer"
> }
> }
> }
> }
> }
> 
> ```

> **Аналайзеры**
>
> ```
> {
> "codec": "best_compression",
> "analysis": {
> "analyzer": {
> "partial_analyzer": {
> "filter": [
> "lowercase"
> ],
> "type": "custom",
> "tokenizer": "partial_tokenizer"
> },
> "search_keyword_analyzer": {
> "filter": [
> "lowercase"
> ],
> "type": "custom",
> "tokenizer": "partial_keyword_tokenizer"
> }
> },
> "tokenizer": {
> "partial_tokenizer": {
> "token_chars": [
> "letter",
> "digit"
> ],
> "type": "edge_ngram",
> "min_gram": "1",
> "max_gram": "10"
> },
> "partial_keyword_tokenizer": {
> "token_chars": [
> "letter",
> "digit"
> ],
> "type": "keyword"
> }
> }
> }
> }
> 
> ```

записей в индексе 100к  
версия elasticsearch:6.8.1, бежит в докер контейнере

---

<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 12, 2019, 2:00pm UTC](https://discuss.elastic.co/t/topic/198575/13 "2019-09-12T14:00:07Z")

</div>

Начнем с того, что токенизатор `keyword` не работает с `token_chars`. Этот параметер не будет иметь никакого эфекта.

Чтобы разобраться, почему релевантность все-равно не такая как нужно, надо запустить запрос с флагом `explain=true`. Это нам даст подробную информацию о том, что происходит.

Я поигрался с вашими данными и у меня результат такой:

```auto
    "_score" : 0.066942096,
        "name" : "АРТ-"
    "_score" : 0.066942096,
        "name" : "АРТ+"
    "_score" : 0.058082115,
        "name" : "АРТУР"
    "_score" : 0.058082115,
        "name" : "ТК Арт"
    "_score" : 0.058082115,
        "name" : "АРТЕМ"
    "_score" : 0.058082115,
        "name" : "Артур"
    "_score" : 0.054477017,
        "name" : "АРТ И КО"
    "_score" : 0.053372756,
        "name" : "Рябов Артур Артур"
    "_score" : 0.05179782,
        "name" : "Клюкин Артем Артем"

```

То есть все как ожидается - короткие имена в начале, длинные потом. Если у вас порядок другой - то это возможно из-за [IDF](https://ru.wikipedia.org/wiki/TF-IDF) некоторых префиксов. Из вывода explain должно быть понятно. Вот скрипт, который я использовал:

```auto
DELETE test

PUT test
{
  "settings": {
    "codec": "best_compression",
    "analysis": {
      "analyzer": {
        "partial_analyzer": {
          "filter": [
            "lowercase"
          ],
          "type": "custom",
          "tokenizer": "partial_tokenizer"
        },
        "search_keyword_analyzer": {
          "filter": [
            "lowercase"
          ],
          "type": "custom",
          "tokenizer": "partial_keyword_tokenizer"
        }
      },
      "tokenizer": {
        "partial_tokenizer": {
          "token_chars": [
            "letter",
            "digit"
          ],
          "type": "edge_ngram",
          "min_gram": "1",
          "max_gram": "10"
        },
        "partial_keyword_tokenizer": {
          "token_chars": [
            "letter",
            "digit"
          ],
          "type": "keyword"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "entityCode": {
        "type": "keyword"
      },
      "localizedName": {
        "type": "nested",
        "properties": {
          "lang": {
            "type": "keyword"
          },
          "name": {
            "type": "text",
            "fields": {
              "sort": {
                "type": "keyword"
              }
            },
            "analyzer": "partial_analyzer",
            "search_analyzer": "search_keyword_analyzer"
          }
        }
      }
    }
  }
}

POST test/_doc/1
{
  "localizedName": {
    "name": "АРТ-"
  }
}

POST test/_doc/2
{
  "localizedName": {
    "name": "АРТ+"
  }
}

POST test/_doc/3
{
  "localizedName": {
    "name": "АРТУР"
  }
}

POST test/_doc/4
{
  "localizedName": {
    "name": "Рябов Артур Артур"
  }
}

POST test/_doc/5
{
  "localizedName": {
    "name": "Клюкин Артем Артем"
  }
}

POST test/_doc/6
{
  "localizedName": {
    "name": "ТК Арт"
  }
}

POST test/_doc/7
{
  "localizedName": {
    "name": "АРТЕМ"
  }
}

POST test/_doc/8
{
  "localizedName": {
    "name": "Артур"
  }
}

POST test/_doc/9
{
  "localizedName": {
    "name": "АРТ И КО"
  }
}

POST test/_analyze
{
  "text": ["Рябов++"],
  "analyzer": "search_keyword_analyzer"
}

POST test/_search
{
  "from": 0,
  "size": 50,
  "query": {
    "bool": {
      "must": [
        {
          "nested": {
            "query": {
              "bool": {
                "must": [
                  {
                    "match": {
                      "localizedName.name": {
                        "query": "арт",
                        "operator": "AND",
                        "prefix_length": 0,
                        "max_expansions": 50,
                        "fuzzy_transpositions": true,
                        "lenient": false,
                        "zero_terms_query": "NONE",
                        "auto_generate_synonyms_phrase_query": true,
                        "boost": 1
                      }
                    }
                  }
                ],
                "adjust_pure_negative": true,
                "boost": 1
              }
            },
            "path": "localizedName",
            "ignore_unmapped": false,
            "score_mode": "avg",
            "boost": 1
          }
        }
      ],
      "adjust_pure_negative": true,
      "boost": 1
    }
  }
}

```

---

<div class="post-metadata">

**Author:** ![111206](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/111206/32/53788_2.png) [@111206](https://discuss.elastic.co/u/111206)\
**Post date:** [September 12, 2019, 3:12pm UTC](https://discuss.elastic.co/t/topic/198575/14 "2019-09-12T15:12:39Z")

</div>

Создал новый максимально лёгкий индекс с минимум полей

> **Запрос**
>
> ```
> {
> "explain": true,
> "from": 0,
> "size": 25,
> "query": {
> "bool": {
> "must": [
> {
> "nested": {
> "query": {
> "bool": {
> "must": [
> {
> "match": {
> "names.localizedName.name": {
> "query": "арт",
> "operator": "AND",
> "prefix_length": 0,
> "max_expansions": 50,
> "fuzzy_transpositions": true,
> "lenient": false,
> "zero_terms_query": "NONE",
> "auto_generate_synonyms_phrase_query": true,
> "boost": 1.0
> }
> }
> }
> ],
> "adjust_pure_negative": true,
> "boost": 1.0
> }
> },
> "path": "names.localizedName",
> "ignore_unmapped": false,
> "score_mode": "avg",
> "boost": 1.0
> }
> }
> ],
> "adjust_pure_negative": true,
> "boost": 1.0
> }
> }
> 
> ```
> 
> }

[explain](https://pastebin.com/FWu4EyXt)

---

<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 12, 2019, 3:29pm UTC](https://discuss.elastic.co/t/topic/198575/15 "2019-09-12T15:29:31Z")

</div>

Все работает точно, как вы запросили. Во всех ответах есть совпадение токинов. Ответы отсортированы по размеру анализированой стротки (tfNorm).

Я думаю проблема не в том, как что elastic неправильно обрабатывает ваш запрос, а в том что то что вы ожидаете от вашего запроса не соответсвует действительности.

Может, наченем с начала? Вы не могли бы объяснить, что в вашем понимении "правильная релевантность результатов". И тогда, на основе этой информации, мы попробуем создать маппинг и запрос, который бы лучше соответсвовал вашему пониманию?

---

<div class="post-metadata">

**Author:** ![111206](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/111206/32/53788_2.png) [@111206](https://discuss.elastic.co/u/111206)\
**Post date:** [September 12, 2019, 3:37pm UTC](https://discuss.elastic.co/t/topic/198575/16 "2019-09-12T15:37:35Z")

</div>

> [@Igor\_Motov](#):
>
> Я думаю проблема не в том, как что elastic неправильно обрабатывает ваш запрос, а в том что то что вы ожидаете от вашего запроса не соответсвует действительности.

Вполне возможно я и не говорю что конкретно скоринг непременно побеждает тут всё, он просто даёт самый точный результат, а хочется точнее и не в кучу запросов а в один)  
У меня есть форма с большим количеством разных запросов, в том числе среди них есть запрос по имени.  
Мне хочется что бы поведение при условии поиска по имени было подобно автокомплиту.  
Вводим букву а получаем

1. Всё что совпадает те А
2. Всё что начинается Арём Артур Афанасий
3. Поиск по вхождению

Есть ещё нюанс, заключающийся в виде большого количества разных спец символов и использований разных языков включая китайский

Если бы это был поиск на бд это решалось бы сортировкой типо  
ORDER BY name ILIKE concat(#{подстрока}, '%') DESC, name

В текущей конфигурации например на запрос арт первый результат ТК Арт и это не очень подходит (

---

<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 12, 2019, 3:48pm UTC](https://discuss.elastic.co/t/topic/198575/17 "2019-09-12T15:48:09Z")

</div>

> [@111206](#):
>
> В текущей конфигурации например на запрос арт первый результат ТК Арт и это не очень подходит (

Это происходит из-за вот этого куска:

```auto
          "token_chars": [
            "letter",
            "digit"
          ],

```

он ввод разбивает по всем символам, которые не буквы и не цифры

То есть `ТК Арт` превращается в следующее

```auto
POST test/_analyze
{
  "text": ["ТК Арт"],
  "analyzer": "partial_analyzer"
}

{
  "tokens" : [
    {
      "token" : "т",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "тк",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "а",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "word",
      "position" : 2
    },
    {
      "token" : "ар",
      "start_offset" : 3,
      "end_offset" : 5,
      "type" : "word",
      "position" : 3
    },
    {
      "token" : "арт",
      "start_offset" : 3,
      "end_offset" : 6,
      "type" : "word",
      "position" : 4
    }
  ]
}

```

Если вы уберете это разбиение, то получиться вот что:

```auto
{
  "tokens" : [
    {
      "token" : "т",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "тк",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "тк ",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "word",
      "position" : 2
    },
    {
      "token" : "тк а",
      "start_offset" : 0,
      "end_offset" : 4,
      "type" : "word",
      "position" : 3
    },
    {
      "token" : "тк ар",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "word",
      "position" : 4
    },
    {
      "token" : "тк арт",
      "start_offset" : 0,
      "end_offset" : 6,
      "type" : "word",
      "position" : 5
    }
  ]
}

```

и это совпадение вообще не появиться в результатах

---

<div class="post-metadata">

**Author:** ![111206](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/111206/32/53788_2.png) [@111206](https://discuss.elastic.co/u/111206)\
**Post date:** [September 12, 2019, 5:12pm UTC](https://discuss.elastic.co/t/topic/198575/18 "2019-09-12T17:12:08Z")

</div>

Не уверен что это вообще реально сделать одним запросом(  
Проблема в том, что хочется что бы ТК Арт тоже был, но он был не в топе списка, а после всего что начинается с буквы А

---

<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 12, 2019, 5:44pm UTC](https://discuss.elastic.co/t/topic/198575/19 "2019-09-12T17:44:34Z")

</div>

> [@111206](#):
>
> Не уверен что это вообще реально сделать одним запросом(

А в чем проблема? Просто ищем и так и так и бустим точное совпадение.

```auto
DELETE test

PUT test
{
  "settings": {
    "analysis": {
      "tokenizer": {
        "word_tokenizer": {
          "type": "char_group",
          "tokenize_on_chars": [
            "whitespace",
            "punctuation"
          ]
        }
      }, 
      "filter": {
        "edge_ngram_1_10": {
          "type": "edge_ngram",
          "min_gram": "1",
          "max_gram": "10"
        }
      }, 
      "analyzer": {
        "prefix_index_analyzer": {
          "type": "custom",
          "tokenizer": "keyword",
          "filter": [
            "lowercase",
            "edge_ngram_1_10"
          ]
        },
        "prefix_search_analyzer": {
          "type": "custom",
          "tokenizer": "keyword",
          "filter": [
            "lowercase"
          ]
        },
        "word_prefix_index_analyzer": {
          "type": "custom",
          "tokenizer": "word_tokenizer",
          "filter": [
            "lowercase",
            "edge_ngram_1_10"
          ]
        },
        "word_prefix_search_analyzer": {
          "type": "custom",
          "tokenizer": "word_tokenizer",
          "filter": [
            "lowercase"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "entityCode": {
        "type": "keyword"
      },
      "localizedName": {
        "type": "nested",
        "properties": {
          "lang": {
            "type": "keyword"
          },
          "name": {
            "type": "text",
            "fields": {
              "sort": {
                "type": "keyword"
              },
              "word": {
                "type": "text",
                "analyzer": "word_prefix_index_analyzer",
                "search_analyzer": "word_prefix_search_analyzer"     
              }
            },
            "analyzer": "prefix_index_analyzer",
            "search_analyzer": "prefix_search_analyzer"
          }
        }
      }
    }
  }
}

POST test/_bulk
{ "index" : { "_id" : "1" } }
{"localizedName": {"name": "АРТ-"} }
{ "index" : { "_id" : "2" } }
{"localizedName": {"name": "АРТ+"} }
{ "index" : { "_id" : "3" } }
{"localizedName": {"name": "АРТУР"} }
{ "index" : { "_id" : "4" } }
{"localizedName": {"name": "Рябов Артур Артур"} }
{ "index" : { "_id" : "5" } }
{"localizedName": {"name": "Клюкин Артем Артем"} }
{ "index" : { "_id" : "6" } }
{"localizedName": {"name": "ТК Арт"} }
{ "index" : { "_id" : "7" } }
{"localizedName": {"name": "АРТЕМ"} }
{ "index" : { "_id" : "8" } }
{"localizedName": {"name": "Артур"} }
{ "index" : { "_id" : "9" } }
{"localizedName": {"name": "АРТ И КО"} }

POST test/_analyze
{
  "text": ["ТК Арт"],
  "analyzer": "partial_analyzer"
}

POST test/_search?search_type=dfs_query_then_fetch
{
  "from": 0,
  "size": 50,
  "query": {
    "bool": {
      "must": [
        {
          "nested": {
            "query": {
              "bool": {
                "should": [
                  {
                    "match": {
                      "localizedName.name": {
                        "query": "арт",
                        "boost": 10
                      }
                    }
                  },
                   {
                    "match": {
                      "localizedName.name.word": {
                        "query": "арт"
                      }
                    }
                  }
                ]
              }
            },
            "path": "localizedName",
            "ignore_unmapped": false,
            "score_mode": "avg",
            "boost": 1
          }
        }
      ]
    }
  }
}

```

---

<div class="post-metadata">

**Author:** ![111206](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/111206/32/53788_2.png) [@111206](https://discuss.elastic.co/u/111206)\
**Post date:** [September 12, 2019, 7:22pm UTC](https://discuss.elastic.co/t/topic/198575/20 "2019-09-12T19:22:46Z")

</div>

Воо спасибо! Погонял запросы и выглядит прям хорошо

[Next page](https://discuss.elastic.co/t/topic/198575.md?page=2)
