Синонимы для словосочетаний, сохранение исходных слов


(Ilya Shipilov) #1

Есть задача сохранять в индексе синонимы для словосочетаний.
Пример:

жидкость омывателя -> омывайка
монопод для селфи, палка для селфи -> селфипалка

Настроил анализатор следующим образом:

PUT /multiwords_synonyms_index
{
  "settings": {
    "analysis": {
      "filter": {
        "my_synonym_filter": {
          "type": "synonym",
          "synonyms": [
            "монопод для селфи, палка для селфи=>селфипалка"
          ]
        }
      },
      "analyzer": {
        "my_synonyms": {
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "my_synonym_filter"
          ]
        }
      }
    }
  }
}

Фильтр отрабатывает, заменяя словосочетания на указанный в таблице терм:

POST /multiwords_synonyms_index/_analyze?analyzer=my_synonyms
{
  "text": "у меня есть монопод для селфи" 
}

POST /multiwords_synonyms_index/_analyze?analyzer=my_synonyms
{
  "text": "у меня есть селфипалка" 
}

{
   "tokens": [
      {
         "token": "text",
         "start_offset": 5,
         "end_offset": 9,
         "type": "<ALPHANUM>",
         "position": 1
      },
      {
         "token": "у",
         "start_offset": 22,
         "end_offset": 23,
         "type": "<ALPHANUM>",
         "position": 4
      },
      {
         "token": "меня",
         "start_offset": 24,
         "end_offset": 28,
         "type": "<ALPHANUM>",
         "position": 5
      },
      {
         "token": "есть",
         "start_offset": 29,
         "end_offset": 33,
         "type": "<ALPHANUM>",
         "position": 6
      },
      {
         "token": "селфипалка",
         "start_offset": 34,
         "end_offset": 51,
         "type": "SYNONYM",
         "position": 7
      }
   ]

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

Использую такой запрос:

GET /multiwords_synonyms_index/data/_search
{
   "from": 0,
   "size": 10,
   "query": {
      "filtered": {
         "query": {
            "function_score": {
               "query": {
                  "simple_query_string": {
                     "query": "монопод для селфи",
                     "fields": [
                        "name"
                     ],
                     "default_operator": "or",
                     "analyzer": "my_synonyms",
                     "minimum_should_match": "1"
                  }
               }
            }
         }
      }
   }
}

Как можно решить эту задачу? Может быть, есть возможность сохранять исходные термы, или обязательно использовать match_phrase?


(Igor Motov) #2

За редким исключением, данные должны быть индексированы с тем же самым анализатором, который вы используете при поиске. См. https://www.elastic.co/guide/en/elasticsearch/guide/current/mapping-intro.html#_analyzer


(Ilya Shipilov) #3

Игорь, так я и не разобрался с этой проблемой.

Насколько понимаю, мой индекс настроен так что и индексация и поиск происходят с синонимами.
Но в результате все равно после использования simple contraction (=>) я теряю возможность найти документ по оригинальным словам (как в примере выше).

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


(Igor Motov) #4

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


(Ilya Shipilov) #5

Да, конечно.

Настройки:

PUT /multiwords_synonyms_index
{
  "settings": {
    "analysis": {
      "filter": {
        "my_synonym_filter": {
          "type": "synonym",
          "synonyms": [
            "монопод для селфи, палка для селфи=>селфипалка"
          ]
        }
      },
      "analyzer": {
        "my_synonyms": {
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "my_synonym_filter"
          ]
        }
      }
    }
  }
}

Маппинг:

PUT /multiwords_synonyms_index/_mapping/goods
{
   "properties": {
      "good_name": {
         "type": "string",
         "store": true,
         "index": "analyzed",
         "search_analyzer": "my_synonyms",
         "index_analyzer": "my_synonyms",
         "term_vector": "with_positions_offsets_payloads"
      }
   }
}

Тестовые данные (документы 1,2,3):

PUT /multiwords_synonyms_index/goods/1
{
    "good_name" : "монопод для селфи"
}

PUT /multiwords_synonyms_index/goods/2
{
    "good_name" : "палка для селфи"
}

PUT /multiwords_synonyms_index/goods/3
{
    "good_name" : "селфипалка"
}

Запрос:

GET /multiwords_synonyms_index/goods/_search
{
   "query": {
      "filtered": {
         "query": {
            "function_score": {
               "query": {
                  "simple_query_string": {
                     "query": "палка для селфи",
                     "fields": [
                        "good_name"
                     ],
                     "default_operator": "and",
                     "minimum_should_match": "1"
                  }
               }
            }
         }
      }
   }
}

Результат запроса:

{
   "took": 3,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 0,
      "max_score": null,
      "hits": []
   }
}

Идентичный результат для запроса "монопод для селфи".

А вот для запроса "селфипалка" в результате все три документа:

{
   "took": 7,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 3,
      "max_score": 0.30685282,
      "hits": [
         {
            "_index": "multiwords_synonyms_index",
            "_type": "goods",
            "_id": "1",
            "_score": 0.30685282,
            "_source": {
               "good_name": "монопод для селфи"
            }
         },
         {
            "_index": "multiwords_synonyms_index",
            "_type": "goods",
            "_id": "2",
            "_score": 0.30685282,
            "_source": {
               "good_name": "палка для селфи"
            }
         },
         {
            "_index": "multiwords_synonyms_index",
            "_type": "goods",
            "_id": "3",
            "_score": 0.30685282,
            "_source": {
               "good_name": "селфипалка"
            }
         }
      ]
   }
}

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


(Igor Motov) #6

А версия Elasticsearch какая?


(Ilya Shipilov) #7

Elasticsearch 1.5.1


(Igor Motov) #8

Проблема в том, как simple_query_string обрабатывает запросы. Дело в том, что при парсинге запрос сначала разбивается по пробелам, и только потом пропускается через анализ. То есть получается что мы анализируем по отдельности токены палка, для, и селфи, и эти токены по отдельности никаким синонимам не соответсвует. Проблема эта давняя и была разрешена только в 5.1.1. В 1.5.1 можно перейти на multi_match, который обрабатывает такие запросы правильно, отказаться от многословных синонимов, или отказаться от simple contraction и индексировать все синонимы, а при поиске синонимы не использовать.


(Ilya Shipilov) #9

Спасибо за разъяснения, Игорь.

В итоге выбрал вариант с simple contraction + дублирующие поля без фильтра синонимов, и поиск идет по обоим полям.


(system) #10