Pull to refresh

Новости 2.0.1-beta

Reading time9 min
Views3.1K

Как здесь уже заметили, недавно вышел Sphinx 2.0.1. Релиз случался в легкой спешке, тк. «совершенно неожиданно» (примерно как сессия или Новый год) еще вдобавок вышла книжка для начинающих, описывающая как раз новую версию. Книга «про транк» это таки слишком эксцентрично, поэтому пришлось оперативно публиковать версию. Хорошо, что мы месяц-другой именно к релизу и готовились: чинили баги, не сильно ломали фичи. В заметке расскажу про всякие нововведения в свежей версии 2.0.1 и планы на следующую версию, см. подкат.


Новые фичи



5 самых заметных (на мой личный взгляд) из 37 новых фич получились такие.
  • детектирование границ предложений и абзацев при индексации, операторы SENTENCE, PARAGRAPH при поиске;
  • поддержка иерархических (!) зон внутри документа при индексации, оператор ZONE при поиске;
  • новый тип словаря, который сильно ускоряет индексацию с возможностью поиска подстрок (индексация до 5-10 раз быстрее, а индекс меньше);
  • улучшенная поддержка строковых атрибутов: ORDER BY, GROUP BY, collations;
  • улучшенный SphinxQL синтаксис: избавляемся от магии, идем к SQL'92.


Про индексацию предложений и абзацев (index_sp=1, SENTENCE, PARAGRAPH)



Нужна для поиска с ограничением по соответствующей единице текста: (мама SENTENCE мыла SENTENCE раму), (дядя PARAGRAPH занемог). В язык запросов добавилась пара операторов. Аргументами операторов (те. слева и справа от SENTENCE, PARAGRAPH) может быть одно из трех: либо простое ключевое слово; либо фраза («унесенные ветром» SENTENCE тираж); либо сам оператор (мама SENTENCE мыла SENTENCE раму).

Произвольное подвыражение туда засунуть довольно нетривиально по техническим причинам. Для тех, кому любопытно, как внутри устроена матчилка запросов: эти операторы в дереве вычисления запроса получают поток вхождений ключевых слов от поддеревьев и фильтруют этот поток. Соответственно, после фильтрации на предмет вхождения в одно предложение или абзац, в общем случае нельзя уверенно сказать, все еще совпадают отфильтрованные вхождения, или уже нет. Надо каким-то образом проверять заново, это довольно технически непросто (если вдруг кому-то просто, присылайте работающий патч и сразу заодно пожелания по зарплате). Поэтому нужно, чтобы вхождения были атомарные (тогда оно либо совпадает, либо его вообще нет, фильтр убил). Ну и поэтому либо слово, либо фраза.

Чтобы отличать границы предложений и абзацев, при индексации нужно их задетектить и сохранить в индекс. Для этого приделана директива index_sp=1.

Границей абзаца считается ряд блочных (block level) HTML тегов, прописанных в коде. Поэтому для индексации абзацев нужно заодно включить HTML стриппер (html_strip=1), тк. HTML обрабатывается там.

Границы предложений детектируются на основании текста, для них стриппер включать необязательно. Границей всегда считаются вопросительный и восклицательный знаки, и с некоторыми исключениями точка. Исключения обрабатывают аббревиатуры типа U.S.A или там Goldman Sachs S.r.l., имена типа John D. Doe, и тому подобное. Могут ошибаться, разумеется, по мере накопления фидбэка будем добавлять всякие исключения. Но в целом на тестах работало неплохо.

Про индексацию зон



Бывает, внутрь документов люди норовят засунуть текст с какой-то внутренней структурой, которая на простой фиксированный список полей не ложится. Характерный пример это как раз книга: главы, секции, подсекции, приложения, сноски, и т.д.

Вот, начиная с 2.0.1 появилась поддержка такой произвольной структуры (внутри любого обычного поля). Включается директивой index_zones, и тоже использует HTML стриппер, потребуется его тоже включить. При индексации документа будут сохранены границы всех помеченных выбранными тегами зон: где начинается и кончается каждый h1, например, или там appendix. При поиске, соотв-но, можно ограничивать поиск зонами: (ZONE:h1 hello world).

Зон может быть произвольное количество. Зоны могут быть как угодно вложены друг в друга. Ограничена только длина тега, примерно 120 байт. Индексируются не все теги подряд, а только явно указанные в директиве. Можно указать либо точный тег, либо маску-префикс: index_zones = h*, chapter, section, appendix. При поиске в операторе можно указывать несколько зон, аналогично полям: (ZONE:(h1,appendix) hello world). Ограничений по названиям нет, те. можно с теми зонами индексировать HTML, можно XML. В отличие от требований XML, технически ничего страшного, если зоны перекрываются (например one twothree fourfive six). Категорически необходимо, однако, чтобы каждая открытая зона закрывалась.

Про новый словарь



На десятом году разработки мы наконец прикрутили словарь, в котором (барабанная дробь)… сохраняются таки сами ключевые слова. Раньше сохранялись исключительно хеши. Собственно, для поиска-то сами слова по-прежнему не нужны: замена хешами работает отлично. Однако со старым типом словаря (dict=crc) для поиска подстрок приходилось заранее все эти подстроки проиндексировать (поскольку подстроки по хешу искать муторно). Такая преиндексация всех возможных подстрок, кстати, работает таки максимально быстро при поиске. Однако время индексации и размер индекса сильно страдают, получается до 5-10 раз медленнее «обычной» индексации, индекс пухнет соответственно. Для мелких индексов, калибра 1-3 GB данных (чего вроде хватает для индексации чуть менее, чем всех торрентов в мире), это еще терпимо. Для индексов 100+ GB текста уже нет. Вот, в переговорах с неким клиентом выяснилось, что поиск подстроки изредка нужен и в таких коллекциях. Ну, приделали новый словарь, куда деваться.

Включается оно директивой dict=keywords в настройках индекса. По сравнению с обычной (!) индексацией медленнее примерно до 1.3 раз (на наших внутренних тестах, YMMV, все эти дела). Соответственно, по сравнению с индексацией префиксом или, того хуже, инфиксов, летает в 3+ раза быстрее и места жрет меньше.

Кстати, по очевидным причинам ощутимо меньше получается словарь (.spi файл), который по умолчанию полностью кешируется в памяти. Так что оно еще и памяти меньше жрет.

За такое адовое ускорение скорости индексации и экономию диска/памяти приходится, конечно, чем-то заплатить. Теоретически, должна страдать скорость поиска, более нечему. Это потому, что при dict=keywords каждой ключевое слово «со звездочкой» автоматически расширяется внутри в большой и толстый OR всех найденных в словаре по указанной маске слов. Запрос сложнее, времени процессора и диска жрет больше. Чем больше слов сматчит маска ВАСЯ*, тем дольше мы Васю будем искать. Практически, однако, может оказаться так, что новый словарь выходит быстрее. Это потому, что если старый индекс в память решительно не помещался и на каждый чих ходил неторопливо посидеть в iowait, почитать диск, пошуршать головками, слить кеш, и только после этого выйти из сортира syscall-а, а новый индекс помещается (или хотя бы кешируется сильно лучше), то новые «медленные» запросы к памяти будут все равно быстрее старых «быстрых» запросов к диску. Лучше десять раз по разу, чем ни разу десять раз!

Степень расширения, кстати, контролируется отдельной новой настройкой, expansion_limit. По умолчанию она сейчас равна 0, те. ограничений нету. Если хочется, чтобы запросы типа A* не заменялись OR-ом на миллион слов и не убивали демона насмерть, лучше выставить какой-нибудь разумный expansion_limit. При наличии лимита при расширении будет браться top-N наиболее частых слов.

Про строковые атрибуты



Строки у нас уже были, а делать с ними было мало что можно, непорядок. Приделали поддержку ORDER BY и GROUP BY над строковыми атрибутами, давно пора. (Осталось приделать WHERE, однако при наличии поиска по словам, это не самая насущная потребность.) Сортировка и группировка через SphinxAPI тоже работает.

Поскольку строки это вам не числа, и в зависимости от языка и-или требований к регистрозависимости сравниваются по-разному, пришлось приделать еще collations, те. на пальцах, разные функции сравнения строк. Поскольку вручную реализовывать кучу collations, будем откровенны, мальца лениво, сделали минимальный джентльменский набор: binary, utf8_general_ci, libc_ci, libc_cs. Первые две сравнивают строки либо тупо побайтово, либо по «общим» (без учета языка и регистра) правилам UTF-8 соответственно. Вторые две цинично используют libc и локаль. С удивлением выяснил, кстати, что LOCALE=ru_RU.utf-8 при запуске демона не работает во1х, плюс из коробки локали часто не особо установлены во2х. Ну, пришлось приделать директиву collation_libc_locale для выбора локали при запуске демона, и изучить команду locale -a и еще какие-то непонятные apt-get, yum.

Про SphinxQL



В предыдущих версиях SphinxQL был, по существу, очень легкой оберткой поверх «старого» механизма поиска, доступного через SphinxAPI. Через это наличествовали всякие остаточные явления: в любой result set обязательно добавлялись колонки id, weight; при группировке добавлялись магические колонки @group, count; явно запрошенный в SELECT порядок атрибутов мог нарушаться (тот, который в запросе, заменялся тем, который в индексе).

Явления такие конфликтуют со стандартом SQL'92 и здравым смыслом, было принято решение зачищать. Запросы остаются такие же, однако отклик дают другой: никаких лишних магических колонок теперь не добавляется, всякие WEIGHT() и COUNT(*) теперь надо просить явно. И порядок атрибутов теперь отдается в точности запрошенный, а не выбранный indexer-ом при построении индекса.

Однако! Внезапно (tm) менять поведение и ломать существующие приложения нельзя, нужно дать возможность обновить приложение (а затем уже внезапно поменять и все сломать). Через это появилась загадочная директива compat_sphinxql_magics, которая по умолчанию равна 1 (отдавать отклик «по-старому», с магическими колонками), но в светлом будущем везде должна быть равна 0 (отдавать отклик «по-новому», как ANSI SQL завещал).

Демон при запуске ругается warning-ом на собственное же дефолтное значение. Это символизирует необходимость двигаться в ногу с прогрессом и так и задумано.

Список изменений (он довольно небольшой) есть в документации в спец-секции про обновление SphinxQL, в принципе, все должно быть довольно интуитивно. Таки наша цель старый добрый всем известный SQL. Где ранее писали SELECT * FROM ..., теперь пишем SELECT *, WEIGHT() `weight` FROM..., где ранее писали только GROUP BY, теперь явно пишем COUNT(*) myalias и обновляем приложение, чтобы ходило в колонку myalias, и т.д.

Новые пакеты



Компилировать руками скучно, поэтому мы постепенно учимся собирать официальные бинарные пакеты под разные платформы. Релиз 2.0.1 уже собран под RH/Centos, Win32, MacOS. В планах и тестах (надеюсь, это ближайшие 1-2 недели) еще пакеты под Ubuntu, Win64. Далее фантазия отказывает и надо будет проводить опрос, под что еще хочется официальных бинарников.

Еще всякие разные новые фичи



В формате «галопом по Европам» пробегусь по ряду других потенциально интересных штук.

Сделали многопоточное и распределенное построение пакета сниппетов, для случая, когда надо их быстро строить из больших документов, лежащих на сервере. Включается при наличии dist_threads у сервера, load_files в запросе на сниппеты, и пока работает только через API.

Сделали поддержку UDF. Можно писать функции на C, подключать их на лету в сервер, и использовать в считалке выражений (см. SELECT). Интерфейс довольно похож на MySQL, хотя система типов отличается. Пересобрать UDF от MySQL под Sphinx задача довольно тривиальная.

Сделали новый формат лога, query_log_format=sphinxql. Все поисковые запросы (и через API, и через QL) конвертирует в корректный SphinxQL синтаксис и пишет, пишет. В отличие от обычного лога, оно записывает фильтры, выражения SELECT, ошибки, итп. Удобно для отладки и профайлинга. Собираемся доделать логирование всех непоисковых запросов тоже (сниппеты итп).

Сделали директиву blend_mode, чтобы последовательности с blended символами в них индексировать в нескольких разных вариантах. (Например, чтобы для «слова» @sphinxsearch# проиндексировать все три варианта @sphinxsearch, sphinxsearch#, @sphinxsearch#, а не только один последний.)

Сделали watchdog, чтобы в режиме с тредами демон сам себя поднимал за волосы, если что. Можно отключить.

Сделали поддержку загрузку id32 индексов в id64 демоны. Собираемся принудительно включать --enable-id64 всегда, готовимся вот.

Сделали поддержку мультизапросов, UPDATE на атрибуты, DESCRIBE, SHOW TABLES, DELETE… WHERE id IN (...) и еще всяких приятных мелочей в SphinxQL.

Соптимизировали английский стеммер в несколько раз, при morphology=stem_en индексация ускорилась где-то в 1.3 раза.

Соптимизировали сниппеты на больших и толстых файлах, вместе со стеммером получилось раза в 1.5 или даже 2 быстрее.

Ну, и багов исправили без счета, конечно.

Чего (и когда) ждать в 2.0.2



Очень хочется переломить порочную тенденцию выпускать релизы раз в год, а остальное время жить в транке. Поэтому 2.0.2 с подкидным дураком и поэтессами багфиксами и небольшими новыми фичами собираемся выпускать этим летом. Автоматические тесты есть, автоматические сборки под всякие платформы делаем. Теперь бы еще документацию кто-нибудь автоматически писал, и тогда совсем ого-го.

Помимо всегда присутствующих задач «баги и скорость» (внесение багов и ликвидация скорости, ага) есть краткосрочные планы про ряд фичей для RT (поддержка поиска подстрок, MVA, еще кое-что), некие секретные работы по качеству поиска, и т.п. «мелкие» улучшения. Что, кстати, плавно подводит.

Как правильно кричать в ухо



Фичи в Сфинксе обычно появляются четырьмя разными путями. Во-1х, иногда они самозарождаются из грязной одежды и горсти пшеницы, однако это в последние века (после отмены эфира и флогистона) происходит крайне редко. Во-2х, иногда мы сами сидим и думаем: а не сделать ли нам вот такую фичу? И не делаем, конечно. Думаем лучше и делаем другую какую-нибудь. В-3х, иногда приходит клиент и говорит: ужасно хочется фичу, денег готов платить даже, вот как ужасно. Клиента переубедить и жадность перебороть удается не всегда, приходится прикручивать. В-4х, иногда пользователи берут и пишут: вы почему до сих пор фичу не сделали. Мы думаем: а правда, почему мы до сих пор фичу не сделали? Посидим, подумаем, покурим, и опять же не делаем, конечно. Но вносим в план и тогда делаем потом!

Вот, чтобы появлялись нужные вам фичи, нужно нам об этом говорить, причем громко, отчетливо и периодически. Для этого можно дважды воспользоваться багтрекером. Во-1х, запросы на новые фичи нужно складывать именно туда. Во-2х, нужно подписываться на существующие запросы (Monitor Bug). На главной странице багтрекера есть неприметный списочек Top Monitored Bugs. Другого хорошего автоматизированного метода определять важность фич у нас пока нет, поэтому собираемся поглядывать туда, а вас соотв-но просим «голосовать» за фичи в том багтрекере.

Итого



Сделали кучку фич и релиз 2.0.1-beta. Параллельно написали про него книжку для начинающих и думаем про следующую книжку для продвинутых парней. Хотим победить самого страшного врага (себя) и в следующий релиз сделать много, но для разнообразия быстро. Можно нам в этом всячески помогать. Традиционно шибко ждем всякой обратной связи и характерного вала обнаруженных багов.

Такие дела.
Tags:
Hubs:
Total votes 93: ↑91 and ↓2+89
Comments30

Articles

Information

Website
sphinxsearch.com
Registered
Founded
Employees
Unknown
Location
Россия