Не корректно работает поиск с использованием Edge n-gram token filter

Здравствуйте, нужна помощь в корректировке настроек поиска. Вкратце опишу проблему.

Когда мы забиваем в поиск слово например "Приора" то помимо реальных документов где встречается слово приора вылетают такие документы где встречается слова типа "привода", "приготовления", "пример" и тд.

Сейчас настроен edge_ngran таким образом, что режет слово на куски в диапазное от 3 - 4 вхождений, например - "при", "прио", "приор" и вот где встречается один из кусков, возвращаются результаты.

Мне кажется надо задать в параметрах количество вхождений кусков слова хотябы 2 или 3. То есть если в слове есть "при", "прио" и "приор" то слово проходит, иначе отсеивается. Таким образом отсеются всякие "прицепы" "примеры" и др.

Но я пока не разобрался как это сделать. Возможно я ошибаюсь и решается это иначе, короче говоря нужна помощь, если кто вкурсе как это делается, помогите пожалуйста буду очень благодарен :slight_smile:
Я использую php клиент для работы с elasticsearch
Конфиг:

        $this->client->indices()->create([
        'index' => 'posts',
        'body' => [
            'mappings' => [
                'properties' => [
                    'id' => [
                        'type' => 'integer',
                    ],
                    'published_at' => [
                        'type' => 'date',
                    ],
                    'name' => [
                        'type' => 'text',
                    ]
                ],
            ],
            'settings' => [
                'analysis' => [
                    'char_filter' => [
                        'replace' => [
                            'type' => 'mapping',
                            'mappings' => [
                                '&=> and '
                            ],
                        ],
                    ],
                    'filter' => [
                        'word_delimiter' => [
                            'type' => 'word_delimiter',
                            'split_on_numerics' => false,
                            'split_on_case_change' => true,
                            'generate_word_parts' => true,
                            'generate_number_parts' => true,
                            'catenate_all' => true,
                            'catenate_numbers' => true,
                        ],
                        'trigrams' => [
                            'type' => 'edge_ngram',
                            'min_gram' => 3,
                            'max_gram' => 4,
                        ],
                    ],
                    'analyzer' => [
                        'default' => [
                            'type' => 'custom',
                            'char_filter' => [
                                'html_strip',
                                'replace',
                            ],
                            'tokenizer' => 'whitespace',
                            'filter' => [
                                'lowercase',
                                'word_delimiter',
                                'trigrams',
                            ],
                        ],
                    ],
                ],
            ],
        ],
    ]);

Запрос:

$this->client->search([
        'index' => 'posts',
        'body' => [
            'bool' => [
                'match' => [
                    [
                        'multi_match' => [
                            'query'    => $value
                            'fields'   => ['name'],
                            'operator' => 'and',
                        ],
                    ]
                ],
            ],
            'size' => 1000,
            'highlight' => [
                'fields' => [
                    'name' => new \stdClass()
                ],

                'number_of_fragments' =>  0
            ]
        ]
    ])

После некоторого времени работы с elasticsearch, немного удалось подкрутить поиск под нужды, но все равно остались вопросы.
Маппинг:

"mappings": {
        "properties": {
            "id": {
                "type": "integer"
            },
            "name": {
                "type": "text",
                "analyzer": "app_analyzer"
            },
            "info": {
                "type": "text",
                "analyzer": "app_analyzer"
            }
        }
    }

Настройки:

"settings": {
        "analysis": {
            "analyzer": {
                "app_analyzer": {
                    "type": "custom",
                    "tokenizer": "standard",
                    "filter": [
                        "stopwords",
                        "asciifolding",
                        "lowercase",
                        "snowball"
                    ]
                }
            },
            "filter" : {
                "snowball": {
                    "type": "snowball",
                    "language": "Russian"
                },
                "stopwords": {
                    "type": "stop",
                    "stopwords": "_russian_",
                    "ignore_case": true
                },
                "app_ngram": {
                    "type": "nGram",
                    "min_gram": 5,
                    "max_gram": 7
                }
            },
            "tokenizer": {
                "nGram": {
                    "type": "nGram",
                    "min_gram": 5,
                    "max_gram": 7
                }
            }
        }
    }

Запрос:

"query": {
        "bool": {
            "must": {
                "multi_match": {
                    "query": "Автомобиль камри",
                    "fields": ["name^5", "lot_info^2"],
                    "operator": "or"
                }
            }
        }
    }

Сейчас по слову ищет прекрасно, но если задать часть слова то соответственно ничего не находит. Если в filter добавить app_ngram, или в качестве tokenizer выбрать nGram то в таком случае выдача получается не корректной, помимо нужных вариантов попадает много лишнего, например: "автомат", "автоматизация" и тд.

Так же если в названии записи значится "Автомобиль тойота камри" и мы будем искать с operator and и tokenizer nGram то в выдачу эти записи не попадают.

Если же выбрать tokenizer standard то все работает как надо, только не ищет подстроку. Например если нам нужно найти запись в которой упоминается Сервер и мы введем серве то в выдачу ничего не попадет.

Подскажите можно как-то улучшить этот поиск чтобы получать нужные результаты?

Вы не пробовали задать 2 анализатора - один на поиск без ngram, а другой для индексации - точно такой-же но с ngram?

1 Like

Наверное не правильно вас понял, или что-то сделал не так.
Вот что вышло

маппинг :

"mappings": {
    "properties": {
        "id": {
            "type": "integer"
        },
        "name": {
            "type": "text",
            "analyzer": "app_analyzer",
            "search_analyzer": "app_search_analyzer"
        },
        "lot_info": {
            "type": "text",
            "analyzer": "app_analyzer"
            "search_analyzer": "app_search_analyzer"
        }
    }
}

настройки:

"settings": {
    "analysis": {
        "analyzer": {
            "app_analyzer": {
                "type": "custom",
                "tokenizer": "standard",
                "filter": [
                    "stopwords",
                    "asciifolding",
                    "lowercase",
                    "snowball",
                    "app_ngram"
                ]
            },
            "app_search_analyzer": {
                "type": "custom",
                "tokenizer": "standard",
                "filter": [
                    "stopwords",
                    "asciifolding",
                    "lowercase",
                    "snowball"
                ]
            }
        },
        "filter" : {
            "snowball": {
                "type": "snowball",
                "language": "Russian"
            },
            "stopwords": {
                "type": "stop",
                "stopwords": "_russian_",
                "ignore_case": true
            },
            "app_ngram": {
                "type": "nGram",
                "min_gram": 5,
                "max_gram": 7
            }
        },
        "tokenizer": {
            "nGram": {
                "type": "nGram",
                "min_gram": 5,
                "max_gram": 7
            }
        }
    }
}

Запрос тот же. Так вот при таком раскладе вообще пустая поисковая выдача. Точней даже не то что пустая, работает иначе и как-то не предсказуемо. Например по предыдущему запросу Автомобиль камри ничего не находит, но если ввести авто то находится все где есть вхождения авто.

А если так?

DELETE test

PUT test
{
  "mappings": {
    "properties": {
      "id": {
        "type": "integer"
      },
      "name": {
        "type": "text",
        "analyzer": "snowball_analyzer",
        "fields": {
          "ngram": {
            "type": "text",
            "analyzer": "ngram_index_analyzer",
            "search_analyzer": "ngram_search_analyzer"
          }
        }
      },
      "lot_info": {
        "type": "text",
        "analyzer": "snowball_analyzer",
        "fields": {
          "ngram": {
            "type": "text",
            "analyzer": "ngram_index_analyzer",
            "search_analyzer": "ngram_search_analyzer"
          }
        }
      }
    }
  },
  "settings": {
    "analysis": {
      "analyzer": {
        "ngram_index_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "stopwords",
            "asciifolding",
            "lowercase",
            "app_ngram"
          ]
        },
        "ngram_search_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "asciifolding",
            "lowercase",
            "stopwords"
          ]
        },
        "snowball_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "asciifolding",
            "lowercase",
            "stopwords",
            "snowball"
          ]
        }
      },
      "filter": {
        "snowball": {
          "type": "snowball",
          "language": "Russian"
        },
        "stopwords": {
          "type": "stop",
          "stopwords": "_russian_"
        },
        "app_ngram": {
          "type": "ngram",
          "min_gram": 4,
          "max_gram": 4
        }
      }
    }
  }
}


POST test/_bulk?refresh
{ "index": {"_id": "1" } }
{ "name": "Автомобиль тойота камри" }



GET test/_search
{
  "query": {
    "bool": {
      "must": {
        "multi_match": {
          "query": "Автомобиль камри",
          "fields": [
            "name^5",
            "lot_info^2",
            "name.ngram^3",
            "lot_info.ngram"
          ],
          "operator": "or"
        }
      }
    }
  }
}
1 Like

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

Первый связанный с highlights подсветкой искомых слов, а именно если ввести авто то не подсвечиваются в найденных элементах, если только это не отдельное слово. Например слово авто подсвечивается а вот в слове автомобиль, не подсвечивается. Реально ли как-то повлиять на это?

Параметры подсветки для запроса

"body": {
        "highlight" : {
            "fields" : {
                "name": {}
            },
            "number_of_fragments": 0
        }
    }

Второй момент это например, если мы вводим автомобиль то все прекрасно отрабатывает, но если ввести например автомобил, или автомоби то возвращается пустая поиcковая выдача. Но если сократить до автом то находится искомое. Я так понимаю тут надо играться с nGram ?

  "highlight": {
    "fields": {
      "name": {},
      "name.ngram": {}
    },
    "number_of_fragments": 0
  }
1 Like

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

Так же поделюсь впечатлением, я доволен elasticsearch, это очень крутой продукт, жалею только о том, что не начал его юзать раньше :slight_smile:

Вы какой версией пользуетесь?

POST test/_bulk?refresh
{ "index": {"_id": "1" } }
{ "name": "Автомобиль тойота камри" }
{ "index": {"_id": "2" } }
{ "name": "Мой крутой сервер" }

GET test/_search
{
  "query": {
    "bool": {
      "must": {
        "multi_match": {
          "query": "крут серв",
          "fields": [
            "name^5",
            "lot_info^2",
            "name.ngram^3",
            "lot_info.ngram"
          ],
          "operator": "or"
        }
      }
    }
  },
  "highlight": {
    "fields": {
      "name": {},
      "name.ngram": {}
    },
    "number_of_fragments": 0
  }
}

У меня все подсвечивает:

{
  "took" : 6,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 6.0996957,
    "hits" : [
      {
        "_index" : "test",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 6.0996957,
        "_source" : {
          "name" : "Мой крутой сервер"
        },
        "highlight" : {
          "name" : [
            "Мой <em>крутой</em> сервер"
          ],
          "name.ngram" : [
            "Мой <em>крутой</em> <em>сервер</em>"
          ]
        }
      }
    ]
  }
}
1 Like

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

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