Вторая часть серии о полнотекстовом поиске несколько задержалась - работа не давала продохнуть. Впрочем, это лирика, приступим к делу.
Итак, я не буду повторять массу открыто доступной информации о Microsoft Full-Text Search (общее описание от Microsoft (eng/рус) и Wikipedia, архитектура), не буду и переписывать простые и не очень примеры. Сосредоточимся на следующем сценарии: у нас имеется некоторое не слишком сложное приложение (веб-приложение, два-три слоя, до несколько Гб данных (до десятка-другого), пара-тройка вспомогательных сервисов) и нам необходимо обеспечить "интеллектуальный поиск" для некоторых сущностей этого приложения. "Интеллектуальность" поиска - маркетинговый прием, которым sales/accounts привлекают пользователей. С чисто технической точки зрения за ним может скрываться следующее:
- находить не только точные вхождения слов из поискового запроса, но и словоформ (мама/маме/мамы/маму ...)
- находить не только слова, введенные пользователем, но и синонимы
- ранжировать результаты поиска по релевантности
- искать не только по атрибутам сущности, но и в теле файла, который с этой сущностью связан
- выполнение сложных запросов (вроде "чтобы вот эти два термина находились поблизости", задание весов для слов в поисковом запросе и много чего еще)
Из описания можно заключить что скорее всего этот функционал все-таки в большей степени ориентирован на конечного пользователя (и скорее всего "продвинутого пользователя") и врядли будет использоваться в каких-то вспомогательных сервисах (хотя как знать ...). Теперь рассмотрим насколько сложно/просто будет все это сделать и отгрузить заказчику. Обратимся к следующим основным вопросам: изменения в архитектуре приложения, поддержка/сопровождение и альтернативы.
Архитектура
Здесь Microsoft потрудилась на славу: технология достается нам бесплатно не только в смысле денег (идет в комплекте с SQL Server и есть даже у SQL Server Express (with Advanced Services)) но и в смысле интеграции. Вся сложность ложится на плечи SQL Server-а:
- Создаем полнотекстовый каталог
- Создаем полнотекстовый индекс
- Используем специальные функции (CONTAINS, CONTAINSTABLE, FREETEXT, FREETEXTTABLE) для обращения к индексам из T-SQL кода
Таким образом, код приложения может быть полностью абстрагирован от того, каким образом получены данные: с применением полнотекстового поиска или без него. В обмен на эту простоту на стороне СУБД нас поджидает:
- Необходимость проектирования физического уровня: сколько каталогов нам нужно и где они будут храниться
- Логический уровень: какие индексы нам нужны, какие колонки индексировать
- Подробности алгоритмов обработки данных: стоп-список (noise words (в SQL Server 2005 - фиксированный, один на сервер, в SQL Server 2008 можно создавать пользовательские)), алгоритм разбиения на слова (word breaker), выделение словоформ (stammer), фильтрация содержимого (content filters)
Первые два уровня для начала/в несложных случаях, пожалуй, можно отдать на откуп SQL Server-у, а вот третий пункт таит в себе массу подводных камней, которые могут значительно усложнить жизнь "среднестатистическому разработчику":
- стоп-список: слова, входящие в него, попросту игнорируются и не включаются в индекс (поэтому неудивительно, что найти их тоже не получится)
- разбиение на слова: зависит от выбранного языка (локали), может быть нейтральным (по пробелам/знакам препинания)
- выделение словоформ: зависит от языка, список доступных языков можно узнать из системного представления sys.fulltext_languages (зависит от версии SQL Server, в 2005м нужно было некоторые языки (в том числе и русский) регистрировать вручную). Если выберем "неправильный" язык (например в поле, индексируемом согласно правилам английского языка, будем хранить русский текст), то словоформы работать не будут - все слова будут искаться "как есть" (буквально). Проблемы также возникнут с синонимами и "похожестью" слов.
- фильтрация содержимого: фактически дает возможность проиндексировать документы, хранящиеся в БД (в поле типа image/varbinary(max)). Хорошая новость заключается в том, что поддерживаются все форматы, которые "понимает" Windows, на которой выполняется SQL Server - нужно лишь задать специальное поле, в котором для данной строки будет храниться формат потока (фактически расширение файла).
Самая большая проблема, конечно же, заключается в правильной обработке мультилокальных данных (и в нашем глобальном плоском мире с каждым днем все меньше и меньше приложений, который могут обойтись поддержкой одного языка). Альтернатив целых две:
- Хранить данные для всех языков в одних и тех же полях вперемешку - проще всего, но теряется значительная часть функциональности
- Для каждого языка создавать отдельное поле/поля (например Title_EN, Title_RU и т.д.) - функциональность остается при нас, но придется довольно много "плясать" вокруг этих данных (в том числе и в приложении - чтобы правильно сохранить/загрузить)
Что бы мы ни выбрали в конечном итоге, за языковыми настройками индексации каждого поля (как и за collation ;-) ) надо следить.
Поддержка/сопровождение
Здесь хорошая новость заключается в том, что полнотекстовые каталоги являются частью базы данные и, как следствие, входят в состав резервной копии/восстанавливаются. Более сложным является вопрос поддержания полнотекстовых индексов в актуальном состоянии. Здесь есть два основных варианта:
- автоматическое наполнение/обновление (CHANGE_TRACKING AUTO): сервер сам будет следить за актуальностью индексов - хорошее решение со многих точек зрения, но могут возникнуть некоторые неожиданные побочные эффекты, связанные с тем, что к нашей базе данных будет обращаться еще один (системный) процесс, накладывающий некоторое количество блокировок
- наполнение/синхронизация индексов по запросу (CHANGE_TRACKING MANUAL): больше ответственности, но и больше уверенности в том что и когда мы делаем - может потребоваться в основном при большой загрузке, когда мы хотим максимально разгрузить сервер БД в течении рабочего времени и готовы смириться с некоторой степенью неактуальности информации в индексах
Довольно сложно дела обстоят и с мониторингом/оптимизацией производительности. Microsoft не разглашает внутреннюю структуру хранения полнотекстовых индексов поэтому на них не распространяется опыт оптимизации "обычных" индексов. Единственное, что можно сказать наверняка: чем больше индексов (в том числе и полнотекстовых) "навернуто" на таблицу, тем больше времени будут занимать операции добавления/обновления/удаления данных.
Ниже приведено сравнение планов добавления записи в две одинаковые таблицы, отличающиеся только тем, что для одной из них создан полнотекстовый индекс по одному из полей с автоматическим обновлением. Та часть плана первого запроса, которая не влезла на экран, полностью совпадает с планом второго запроса.
Выводы делаем сами (впрочем, я думаю и так понятно, что бесплатного в этом мире ничего не бывает).
Альтернативы
Если не рассматривать в качестве альтернативы переход на другую СУБД (в PostgreSQL, Oracle и MySql есть аналогичные технологии), то остается применение неких внешних по отношению к нашему приложению сервисов, производящих индексацию содержимого. Тут можно упомянуть:
- Windows Search - встроен в Windows (следовательно "бесплатен"), позволяет индексировать файлы, хранящиеся на компьютере
- Google Desktop - аналогичный продукт от Google
- Apache Lucene / Solr - индексатор / поисковый сервер от Apache Foundation
Первые два продукта скорее ориентированы на индексацию файлов и подойдут скорее в том случае, если перед нами поставлена задача индексировать какие-то документы (довольно широкая тема), в то время как последний - именно внешний индекс, который может "втянуть" почти все, что угодно, физически представляет из себя веб-службу, поддерживает уйму разного функционала, распределенные индексы, очень гибко настраивается и много чего еще. Об этом чудо-средстве я планирую написать отдельный пост (а может быть и не один).
HTH
AlexS