Sphinx - Настраиваем поиск
Несомненно, одной из основных причин, почему вам может потребоваться использовать Sphinx, является возможность полнотекстового поиска. Благодаря большому количеству настроек, можно отрегулировать поиск таким образом, чтобы он максимально удовлетворял потребностям каждой конкретной задачи. В этой статье я постараюсь рассказать про некоторые из этих настроек более подробно, а также приведу примеры их использования.
Опция «charset_table»
Когда Sphinx индексирует документы или обрабатывает поисковой запрос, прежде всего он разбивает текст на слова (токены), но для того, чтобы правильно это сделать, ему необходимо знать, какие символы в тексте могут являться частью слов, а какие не могут.
Опция charset_table
задаёт таблицу допустимых символов. Если несколько допустимых символов в тексте идут один за
другим, то Sphinx воспринимает их как одно слово. Если символ не указан в таблице символов и,
соответственно, не считается допустимым, то Sphinx воспринимает его как разделитель.
По умолчанию для индексов с кодировкой utf-8 опция charset_table
выглядит следующим
образом:
charset_table = 0..9, A..Z->a..z, _, a..z, \
U+410..U+42F->U+430..U+44F, U+430..U+44F, U+401->U+451, U+451
Допустимые символы в таблице символов указываются через запятую. Записи 0..9
и
a..z
обозначают, что все символы идущие между указанными символами считаются
допустимыми. Можно также указывать отдельные символы, как это сделано с символом _
.
Запись A..Z->a..z
обозначает, что символы A..Z
считаются допустимыми,
но при обработке заменяются символами a..z
, т.е. по сути приводятся к нижнему
регистру. Это необходимо, чтобы поиск не был чувствителен к регистру.
Наконец, вторая строка по аналогии определяет правила обработки символов для кирилицы, которые
заданы в формате U+xxx
, что обязательно для всех символов, код которых больше 127.
По сути это означает следующее:
charset_table = 0..9, A..Z->a..z, _, a..z, \
А..Я->а..я, а..я, Ё->ё, ё
Пример
По умолчанию Sphinx воспринимает символы «Е» и «Ё» как различные. Мы можем изменить это поведение, чтобы, например, иметь возможность по запросу «сгущёнка» также находить документы со словом «сгущенка». Для этого в таблице символов необходимо изменить правила для символов «Ё» (U+401) и «ё» (U+451) тамим образом, чтобы эти символы отображались на символ «е» (U+0435).
charset_table = 0..9, A..Z->a..z, _, a..z, \
U+410..U+42F->U+430..U+44F, U+430..U+44F, U+401->U+0435, U+451->U+0435
Теперь во всех запросах символы «Е» и «Ё» будут считаться одинаковыми.
select * from recipes where match('сгущёнка');
Опция «blend_chars»
Опция blend_chars
похожа на опцию charset_table
, но задаёт смешанные символы, которые будут считаться
и допустимыми, и разделитеелями одновременно. При нахождении таких символов в тексте во время
индексации, Sphinx будет индексировать все возможные варианты. Например, если символ «&»
считается смешанным, а в индексируемом документе встретится текст «Рога&Копыта», то
проиндексированы будут три слова: «рога», «копыта» и «рога&копыта». При поиске же всегда будет
использоваться вариант, в котором все смешанные символы считаются допустимыми. Например, если
поисковой запрос будет содержать слово «рога&копыта», то поиск будет производиться именно по
нему, а не по словам «рога» и «копыта».
Опцию blend_chars
можно использовать вместе со связанной опцией
blend_mode
,
которая задаёт, какие комбинации допустимых и смешанных символов следует проиндексировать.
Например, можно индексировать слова со смешанными символами в начале слова, отбросив при этом
смешанные символы в конце, или наоборот. При этом можно указать несколько значений,
проиндексировав тем самым несколько различных комбинаций.
Пример
Предположим, что мы хотим организовать поиск книг по названию и найти книни по языкам «C#» или «C++»:
select * from book where match('C#');
select * from book where match('C++');
Поскольку символы «#» и «+» не являются допустимым (не указаны в таблице символов), то Sphinx
считает их разделителями и производит поиск только по слову «C». Добавить эти символы в таблицу
символов было бы неправильно. Вместо этого, укажем их в опции blend_chars
. Обратите
внимание, что символ «#» указан как «U+23», поскольку иначе он бы считался началом комментария.
blend_chars = +, U+23
Обновив индекс и повторив поиск, мы убедимся в том, что теперь Sphinx ищет именно по словам «C#» и «C++».
select * from book where match('C#');
select * from book where match('C++');
Опция «ignore_chars»
Опция ignore_chars
задаёт символы, которые Sphinx будет игнорировать, как будто их вообще нет в тексте. Таким
образом, если два слова разделены только игнорируемым символом, они «склеятся» и будут считаться
одним словом. Обычно, эту опцию следует использовать, если в индексируемом тексте присутствуют
символы мягкого переноса, поэтому чаще всего её использование выглядит следующим образом:
ignore_chars = U+AD
Опция «morphology»
После того, как обрабатываемый текст разбит на слова, в дело вступают морфологические алгоритмы
(если они, конечно, включены), которые заменяют все найденные слова на их нормализованную форму,
позволяя тем самым, например, по запросу «книга» находить документы, содержащие слова «книги»,
«книгу» или «книгой». В Sphinx могут использоваться три типа морфологических алгоритмов: стеммер
(stemmer), лемматайзер (lemmatizer) или фонетические алгоритмы. Для использования одного из них
необходимо в настройках индекса указать опцию
morphology
.
Стеммер
Стеммер является наиболее простым и быстрым алгоритмом, позволяющим найти основу слова (часть слова, которая является одинаковой для разных его форм) без использования дополнительных словарей и основываясь только на определённых правилах удаления суффиксов и окончаний для конкретного языка. Основным минусом стеммера является то, что он далеко не всегда позволяет точно определить основу слова. Например, слова «девушка» и «девушек» будут считаться различными, поскольку после обработки стеммером первое слово превратится в «девушк», а второе не изменится. Возможна и обратная ситуация, когда два морфологически различных слова приводятся стеммером к одной и той же основе и поэтому считаются одинаковыми.
Вот пример того, как стеммер нормализует слова «девушка», «девушки» и «девушек»:
Для использования стеммера для русского и английского языков необходимо в опции
morphology
указать значение stem_enru
.
morphology = stem_enru
Лемматайзер
Лемматайзер (появившийся только в версии 2.1.1-beta), в отличие от стеммера, использует морфологические словари, поэтому позволяет не просто находить основу слова, а приводить его к нормальной (словарной) форме. Поиск с использованием лемматайзера более точен, но за всё приходится платить - работает он немного медленнее, чем стеммер.
Вот пример того, как слова «девушка», «девушки» и «девушек» нормализует лемматайзер:
Поддержка лемматайзера в настоящее время реализована только для русского языка, реализация для
английского языка скорее всего будет доступна в следующих версиях Sphinx. Для использования
лемматайзера в первую очередь необходимо скачать
морфологические словари с сайта sphinxsearch.com и в
файле конфигурации в блоке indexer
указать путь к папке со словарями при помощи
опции lemmatizer_base
.
indexer
{
...
lemmatizer_base = c:/sphinx/data/dict/
}
После этого в опции morphology
нужно указать значение lemmatize_ru
или
lemmatize_ru_all
. При использовании опции lemmatize_ru_all
, Sphinx
будет индексировать все нормальные формы слова, если их несколько. Например, для слова «чайку»
нормальной формой может быть как «чайка», так и «чаёк».
morphology = lemmatize_ru
Фонетические алгоритмы
Помимо стеммера и лематайзера Sphinx также поддерживает два фонетических алгоритма: Soundex и Metaphone, которые, однако, работают только для английского яыка. Эти алгоритмы заменяют слова на их фонетический код таким образом, что разные по написанию, но схожие по звучанию слова будут считаться одинаковыми. Наиболее полезными фонетические алгоритмы могут оказаться, например, для поиска по фамилиям.
Для использования фонетических алгоритмов необходимо в опции morphology
указать
значение soundex
или metaphone
.
morphology = metaphone
Опция «wordforms»
Опция wordforms
задаёт путь к пользовательскому файлу словоформ, который имеет два основных назначения.
Во-первых, этот файл может быть использован, чтобы указать правильную нормализованную форму слова в тех случаях, когда стеммер делает это неправильно. Например, если необходимо указать, что слово «девушек» всё таки является словоформой от слова «девушка», то в файл словоформ можно дорбавить следующую строку:
девушек => девушк
Обратите внимание, что при использовании стеммера после знака =>
должна идти именно
основа слова («девушк», а не «девушка»), т.к. именно по основе слова впоследствии будет
производится поиск. Также обратите внимание, что если вы используете индекс в кодировке utf-8,
то файл словоформ тоже обязательно должен быть сохранён в этой же кодировке.
В результате слова «девушка» и «девушек» станут приводиться к одной основе «девушк» и будут считаться одинаковыми при поиске.
Во-вторых, при помощи файла словоформ можно организовать словарь поисковых синонимов. Например, если слову «Эппл» необходимо находить документы, содержащие слово «Яблоко», то в файл словоформ нужно добавить одну из следующих строк (в зависимости от того, пользуетесь вы стеммером или лемматайзером):
эппл => Яблок # Стеммер
эппл => Яблоко # Лемматайзер
Такие результаты будут получены при поиске по слову «Эппл» для стеммера:
А такие для лемматайзера
Опция «stopwords»
Опция stopwords
задаёт путь к файлу стоп-слов, которые игнорируются при индексации и поиске. Обычно в этот файл
рекомендуется помещать слова, которые встречаются в индексе настолько часто, что никак не влияют
на результат. Как правило это различные предлоги или союзы.
Например, если мы хотим, чтобы союз «и» игнорировался при поиске, мы можем добавить его в список стоп-слов. Тогда при поиске он будет игнорироваться:
Заключение
Если подытожить, то Sphinx имеет достаточно большое количество опций, правильная настройка которых может значительно влиять на качество поиска. Помимо опций, описанных в этой статье, он также поддерживает поиск по точному совпадению и по подстроке, а также поиск в html-документах.
А в следующей статье про Sphinx я планирую немного рассказать про распределённый поиск и привести несколько примеров.
Подскажите, пожалуйста, а как запустить использование лемматайзера для rt-индекса, для которого не определяется блок indexer?
ОтветитьУдалитьА блок indexer ни для какого индекса не определяется =)
ОтветитьУдалитьЭтот блок один на весь конфиг, как и блок searchd, и нужен только чтобы путь к словарям указать, поэтому там абсолютно всё тоже самое.
В блоке index ставим morphology = lemmatize_ru, делаем insert в rt индекс и вставляемые данные будут обрабатываться лемматайзером (только для полей, само собой).
Ага, спасибо, заведу этот блок, а то из-за того, что одни rt-индексы используются, его вообще нет в конфиге.
ОтветитьУдалитьВ транке поддерживается лемматизатор для английского и немецкого языков (вдобавок к русскому).
ОтветитьУдалить+ появилась в конфиге секция common. Сейчас в неё кладётся путь к словарям лемматизатора (перемещён туда из indexer) и ещё семь разных параметров, связанных с RLP и JSON.
(соответственно, транковая версия при наличии в старом конфиге lemmatizer_base не заведётся, нужно будет строчку перенести в common)
Кажись в примере указывать строчную "ё" ("U+451") в конце не надо, она и так входит в объявленный диапазон "а-я".
ОтветитьУдалитьНе входит, это можно проверить по таблице символов. Буква "я" имеет код U+44F, а "ё" - U+451, т.е. идёт после через несколько других символов.
ОтветитьУдалитьО, не знал, спасибо. Тогда значит дефолтные настройки charset_table (как в документации) лишают людей слов с буквой "ё"? И как разработчиков ещё не замучили жалобами...
ОтветитьУдалитьВ документации была ошибка, в последних версиях её уже поправили, кажется.
ОтветитьУдалитьЯ бы через wordforms попробовал, наверное.
ОтветитьУдалитьЯ пробовал через wordforms "с++ > c++", но похоже что замена не происходит. После переиндексации вижу две разные выдачи. До этого было через exceptions, но blend_chars с ними конфликтуют. Т.к. exceptions регистрозависимый то там получается много писать нужно. С blend_chars решение красивее, вот только бы еще эту проблему победить)
ОтветитьУдалитьУ меня файл создан в vi, первая строчка обрабатывается номально. Там в этом файле уже есть правила, они обрабатываются хорошо. А вот С++ не хочет воспринимать. Наверно да, стоит спросить у разработчиков.
ОтветитьУдалитьПодскажите как получить пример с девушками? У меня выдает пустой результат. Эти слова должны быть в базе данных проиндекированной сфинксом?
ОтветитьУдалитьmysql> call keywords('девушка девушки девушек', 'testrt');
Empty set (0.00 sec)
mysql> call keywords('девушка девушки девушек', 'test1');
Empty set (0.00 sec)
mysql> call keywords('черный', 'test1');
Empty set (0.01 sec)
mysql> call keywords('черный', 'testrt');
Empty set (0.00 sec)
mysql>
Кодировку выставил. Версия mysql новая (Centos 6.5). Версия сфинкса 2.1.8. Через php русские слова находятся. Вот только morphology = lemmatize_ru_all не работает. (словарь скачан и прописан)То есть ищется всегда точное совпадение, а словоформы не находятся.
ОтветитьУдалитьЯ бы проверил сделующее: перестроить индекс, перезапустить сфинкс, проверить, нет ли ошибок в логах при запуске, возможно словарь не там лежит. А может это просто баг и нужно написать в их баг-трекер, и такое бывает.
ОтветитьУдалитьПроблема оказалась в следующем. 1. морфологию нужно указывать для обоих интедксов (я зыбал для первого). 2. мофрология letimizzer_ru_all работает только в бете. В не бете работает просто ru. Было бы полезно увидеть конфиг файл с подробным описанием каждой строки. Я например не очень понимаю чем sql_QUERY отличается от sql_auery_info
ОтветитьУдалитьВ других статьях есть ссылки на архивы с конфигами.
ОтветитьУдалитьsql_query_info ни разу не испрользовал, а значит он скорее всего не нужен.
Подскажите, пожалуйста. Вот есть база фильмов. В ней, соответственно, названия фильмов, которые в индексе сфинкса. Как можно сделать, чтобы запросы типа "Терминатор 2" или "Поворот не туда 5" использовали числительное в поиске? Если есть настройка, позволяющая их прилеплять в к словам? или стоит в индекс загонять уже слепленные числительные с предыдущим словом? Но тогда, как я понимаю, накроется морфология. Либо способ отранжировать такие вещи наверх? То есть слеплять при покладке в индекс, и считать crc32. Может есть другое решение какой-нибудь хитрой настройкой?
ОтветитьУдалитьЧто значит "использовали числительное в поиске"? Не очень понял. Можно примеры запросов и результатов, которые нужно получить?
ОтветитьУдалитьИндекс строится по полю "название фильма" и еще дополнительным полям с меньшем весом.
ОтветитьУдалитьИспользуется следующий ранкер: SPH_RANK_SPH04 с добавлением вынесения в топ записей с точным совпадением.
$sphinx->SetRankingMode(SPH_RANK_EXPR, "sum((4*lcs+2*(min_hit_pos==1)+exact_hit)*user_weight+if(origtitlecrc==$querycrc,1000,0)+if(titlecrc==$querycrc,1000,0))*1000+bm25");
Если ищем строку "Терминатор 2", то выдается "Терминатор: Да придет спаситель", "Терминатор 3: Восстание машин", "Терминатор","Терминатор 2: Судный день"...
Хотелось бы видеть "Терминатор 2" первым в поиске. Слова подряд, с первого символа, что может быть лучше? Как я понимаю, это связано с тем, что числительное из одного символа (короткое), и не учитывается при поиске. Может ошибаюсь, ибо ставил min_word_len = 1 - не помогло. Что я делаю не так?
charset_table = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F, U+401->U+0435, U+451->U+0435
Спасибо за ответ. Я тут другими делами отвлекся.
ОтветитьУдалитьЯ сразу не сказал почему-то, но индекс строится из нескольких полей, не только заголовок, но и синопсис фильма и год там присутствуют, так что "2" там есть, но где-то много дальше.
Mysql сейчас попробую. Еще не пробовал так никогда.
Подскажите, как организовать поиск слов, содержащих символы "!" . При поиске выдает ошибку: ERROR 1064 (42000) syntax error, unexpected $end near ''. Добавление в blend_chars, в charset_table не помогло, все равно поиск вылетает с ошибкой
ОтветитьУдалитьВозможно его надо экранировать как-то так: \! или \\! (или возможно даже так \\\\!, точно не помню)
ОтветитьУдалитьспасибо огромное Вам. \\! помогло, ошибка больше не появляется
ОтветитьУдалитьСпасибо тебе, добрый человек!
ОтветитьУдалитьПожалуйста! =)
ОтветитьУдалитьЗдравствуйте, подскажите, пожалуйста как сделать следующее:
ОтветитьУдалитьЕсть каталог товаров, например товар называется IRF740PBF , он находится, все ок, нет никаких проблем.
Но нужно сделать так, что бы этот товар находился по запросам, например: IRF 740 PBF, IRF 740PBF
что бы игнорировались пробелы, но при этом есть опасение, что весь текст "слипнется" и индекс будет гигантским
Пробовал сделать через
ignore_chars = U+20 - не помогло
Подсознательно подозреваю, что надо делать через
blend_chars = U+20, но тоже не помогает (
Нет готового механизма, насколько знаю.
ОтветитьУдалитьЕдинственный вариант - при индексации искать слова содержащие буквы и цифры и заменять их на все возможные комбинации.
Или попробовать regexp_filter, который вроде есть не во всех сборках.
http://sphinxsearch.com/docs/latest/conf-regexp-filter.html
Разбивать через него слова содержащие и буквы и цыфры на отдельные слова
Понял 8)) Спасибо большое за ответ 8))
ОтветитьУдалитьЗдравствуйте, нужна помощь в такой задачке:
ОтветитьУдалитьВ БД есть таблица сотрудников, необходим поиск по фамилиям.
Есть такой случай: ищем "Иванова", но первые в результате находятся:
Алексеев Иван,
Абрамов Иван и т.д.
Я понял, что это из-за настроек конфига, а точнее морфологии: morphology = stem_ru
Если его убрать, то всё норм, но вот в чём загвоздка. Мне нужно реализовать выпадающий список найденных сотрудников, скажем 10 результатов. Т.е. начинаем вводить "Ива" формируется такой список:
Иванов Иван Иванович
Иванов Алексей Сергеевич
Иванов Владимир Петрович
и т.д.
Но такого не получается сделать, т.к. отключена морфология и происходит поиск целого
слова. Помогите, как правильно настроить sphinx для такой задачи?
Включите
ОтветитьУдалитьhttp://sphinxsearch.com/docs/latest/conf-index-exact-words.html
http://sphinxsearch.com/docs/latest/conf-expand-keywords.html
тогда точны совпадения будут иметь больший вес.
Поиск по части слова включается через конфиг в зависимости от версии (в последних включен по умолчанию)
Фамилию выгружайте в отдельное поле, и при поиске задавайте ему больший вес, тогда в запросе
http://sphinxsearch.com/docs/latest/sphinxql-select.html
можно указать field_weights, чтобы совпадения с фамилией опять же имело больший вес.
lemmatizer_base = /srv/lemitaizer_sphinx/
ОтветитьУдалитьподключается в блоке common{}
долго копал в чем у вас в статье ошибка
Это не ошибка. Просто примеры в статье для версии сфинкса 2.1.1. В ней не было блока common =)
ОтветитьУдалить