Genre Expansion в Elasticsearch 6.1


(Alexander Zheludkov) #1

Здравствуйте!

Во втором эластике была возможность создавать иерархические структуры синонимов для поиска:
https://www.elastic.co/guide/en/elasticsearch/guide/current/synonyms-expand-or-contract.html

Однако в шестой версии я такой опции не вижу. Обычный поиск по синонимам работает. Есть ли возможность задать какую-то онтологию, и, скажем, при поиске pet находить документы и с cat, и с dog?


(Igor Motov) #2

Формат синонимов не менялся очень давно. Вы не могли бы показать полный пример, который работал в 2.x и не работает в 6.x?


(Alexander Zheludkov) #3

Вот так объявляю аналайзер (вместе с маппингом):

curl -XPUT 'localhost:9200/pet_index?pretty' -H 'Content-Type: application/json' -d'
{ "mappings": {
"pet_owner": {
"properties": {
"name": { "type": "text" },
"age": { "type": "integer" },
"pet": { "type": "text" },
"created": {
"type": "date",
"format": "strict_date_optional_time||epoch_millis"
}
}
}
},
"settings": {
"index" : {
"analysis" : {
"analyzer" : {
"search_synonyms" : {
"tokenizer" : "whitespace",
"filter" : ["graph_synonyms"]
}
},
"filter" : {
"graph_synonyms" : {
"type" : "synonym_graph",
"synonyms_path" : "analysis/synonym2.txt"
}
}
}
}
}
}
'

Вот так выглядит файл с синонимами:

"cat => cat,pet",
"kitten => kitten,cat,pet",
"dog => dog,pet",
"puppy => puppy,dog,pet"

Вот так - запрос:

curl -XGET 'localhost:9200/pet_index/_search?pretty' -H 'Content-Type: application/json' -d'
{
"query" : {
"match" : {
"pet" : {
"query":"pet",
"analyzer": "search_synonyms"
}
}
}
}
'


(Alexander Zheludkov) #4

Возможно, так будет нагляднее:
https://pastebin.com/mMRgVks0


(Igor Motov) #5

В вашем примере синонимы используются при поиске. Поэтому cat будет искать cat и pet, но pet будет искать только pet. Надо либо переместить синонимы из поиска в индексирование (и изменить тип на просто synonyms), либо вывернуть синонимы наизнанку:

pet => pet, cat, kitten, dog, puppy
cat => kitten, cat
dog => dog, puppy

(Alexander Zheludkov) #6

Понял, спасибо. А есть ли возможность использовать при этом вложенную иерархию, то есть указать для pet только его непосредственных потомков - cat и dog, но при этом искать по их синонимам тоже? Иначе, боюсь, мало-мальски сложная иерархия разрастется до непомерных величин, и ее очень сложно будет описывать.

И еще: подскажите, пожалуйста, аналайзер подхватывает файл с синонимами каждый раз при выполнении запроса, или только при первичной индексации? Если второе, то можно ли заставить его принудительно переиндексировать файл?


(Igor Motov) #7

Можно применить тот же самый фильтр синонимов несколько раз. Ничего более разумного в данный момент в голову не приходит.

Аналайзер перегружает файл синонимов при открытии индекса. Но при этом записи, которые уже были проиндексированы, не переиндексируются. Их можно переиндексировать с помощью команды reindex.


(Alexander Zheludkov) #8

Можно применить тот же самый фильтр синонимов несколько раз. Ничего более разумного в данный момент в голову не приходит.

К сожалению, не получается( Вы не могли бы подсказать, что я на этот раз делаю не так?

pet => pet, kitty, dog, bird
kitty => kitty, kitten
dog => dog, puppy 
bird => bird, parrot

Запрос:

curl -XPUT 'localhost:9200/pet_index?pretty' -H 'Content-Type: application/json' -d'
{"mappings": {
    "pet_owner": { 
      "properties": { 
        "name":     { "type": "text"  }, 
        "age":      { "type": "integer" },  
        "pet":      { "type": "text"  }, 
        "created":  {
          "type":   "date", 
          "format": "strict_date_optional_time||epoch_millis"
        }
      }
    }
  },
    "settings": {
        "index" : {
            "analysis" : {
                "analyzer" : {
                    "synonym_analyzer" : {
                        "tokenizer" : "whitespace",
                        "filter" : ["synonym", "synonym"]
                    }
                },              
                "filter" : {
                    "synonym" : {
                        "type" : "synonym",
                        "synonyms_path" : "analysis/synonym2.txt"
                                }
                           }
            }
        }
    }
}
'

И результат:

{
  "error" : {
    "root_cause" : [
      {
        "type" : "illegal_argument_exception",
        "reason" : "failed to build synonyms"
      }
    ],
    "type" : "illegal_argument_exception",
    "reason" : "failed to build synonyms",
    "caused_by" : {
      "type" : "parse_exception",
      "reason" : "Invalid synonym rule at line 1",
      "caused_by" : {
        "type" : "illegal_argument_exception",
        "reason" : "term: pet analyzed to a token (kitty) with position increment != 1 (got: 0)"
      }
    }
  },
  "status" : 400
}

(leov) #9

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


(Alexander Zheludkov) #10

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


(leov) #11

попробуйте и эту строчку порезать до самого минимума
попробуйте воткнуть между двумя вызовами одного фильтра что-то еще
попробуйте определить фильтр для второго прохода с другим именем
(вообще имена я бы давал какие-то более свои избегая стандартных эластиковых слов
черт его знает что он там внутри подумает и как их интерпретирует)


(Alexander Zheludkov) #12

Все это уже попробовал, к сожалению, с неизменным результатом.


(leov) #13

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


(Alexander Zheludkov) #14

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


(Alexander Zheludkov) #15

Давайте покажу, как сейчас это выглядит:

curl -XPUT 'localhost:9200/pet_index?pretty' -H 'Content-Type: application/json' -d'
{"mappings": {
    "pet_owner": { 
      "properties": { 
        "name":     { "type": "text"  }, 
        "age":      { "type": "integer" },  
        "pet":    { "type": "text"  }, 
        "created":  {
          "type":   "date", 
          "format": "strict_date_optional_time||epoch_millis"
        }
      }
    }
  },
    "settings": {
        "index" : {
            "analysis" : {
                "analyzer" : {
                    "synonym_analyzer" : {
                        "tokenizer" : "whitespace",
                        "filter" : ["synonym_filter", "do_nothing", "another_synonym_filter"]
                    }
                },              
                "filter" : {
                    "synonym_filter" : {
                        "type" : "synonym_graph",
                        "synonyms_path" : "analysis/synonym2.txt"
                    },
                    "another_synonym_filter" : {
                        "type" : "synonym_graph",
                        "synonyms_path" : "analysis/synonym3.txt"
                    },
                    "do_nothing" : {
                        "type" : "standard"
                    }
                }
            }
        }
    }
}
'

Файлы синонимов:

pet => pet, kitty, dog, bird
kitty => kitty, kitten
dog => dog, puppy 
bird => bird, parrot

и

pet, kitty, dog, bird

Ошибка та же самая:

"type" : "illegal_argument_exception",
        "reason" : "term: pet analyzed to a token (kitty) with position increment != 1 (got: 0)"

(leov) #16

а в чем собственно смысл двойного прогона одного и того-же правила?
и почему вы пишете dog => dog слева и справа одно и то-же?
по моему дак слева должно быть слово а справа синонимы
по моему тут вообще можно чарфильтр типа мапинг сделать чтобы заменяло одно на другое и все
а так как вы делаете то надо каждый уровень вашей иерархии поместить в отдельное описание синонимов и потом прогнать по очереди
вот так как-то я это понимаю


(Igor Motov) #17

Да, я про positions increment не подумал. Вы хотите эти синонимы применять при индексировании или при поиске?


(Alexander Zheludkov) #18

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


(Igor Motov) #19

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


(Alexander Zheludkov) #20

Пока у меня нет необходимых словарей, я сгенерировал образец синонимов, которые буду индексировать - 10 строчек, в которых 70 тысяч терминов матчатся на один:

 synonym1_1, synonym1_2, ... synonym1_70000 => term1
 synonym2_1, synonym2_2, ... synonym2_70000 => term2
...
 synonym10_1, synonym10_2, ... synonym10_70000 => term10

Пытаюсь сгенерировать индекс с этим фильтром, но эластик не реагирует вот уже 20 минут. Как вы считаете, это надолго? (на тестовой системе 4 ядра по 2,6 Гб, 16 гб оперативки и ssd) Можно ли запустить эластик с конкретными параметрами по производительности, чтобы он не стеснялся использовать ресурсы?