Пытаясь программировать ...иногда даже получается

Как задать произвольную компоновку фильтров в Sphinx

Когда я получил задание, связанное с поисковой системой Sphinx, то оказалось, что в Sphinx все фильтры компонуются исключительно через условие И. Мне же понадобилась компоновка через ИЛИ, причем сложная. О своем варианте решения данного вопроса я сегодня и расскажу.

Итак, речь пойдет о Sphinx'е. Что это такое можно узнать, например, здесь или здесь. Никаких инструкций об установке и настройке поисковой систему куда-либо или как-либо не будет, увы. Рассматривать работу мы будем на базе методов класса sfSphinxClient — часть пакета sfSphinxPlugin для Symfony 1.4

Основными инструментами поиска в Sphinx являются запросы и фильтры. К моему разочарованию, через методы фильтры нельзя компоновать с разными логическими условиями, например, (условие1 И условие2) ИЛИ условие3. Если вы задали фильтр, то все последущие setFilter при поиске будут сравниваться через И. Часто, такое поведение не дает решить поставленную задачу.

Что же делать, чтобы заставить фильтры в Sphinx работать через ИЛИ? Решение в методе SetSelect. Через него можно задать произвольную выборку данных из существующего индекса, подобно тому, как это делается в базах данных. В нашем случае, ключевой является возможность использовать конструкцию IF:

IF(a <= 2 OR b >= 5, 1, 0) AS cf

"Чудесно, что можно делать произвольные выборки, но чем это может помочь?", — спросите вы. А тем, что теперь мы можем задать фильтр по результату условия в SELECT cf в вышеуказанном примере) и получить желаемый результат. Давайте рассмотрим фрагмент кода, демонстрирующий сложную компоновку фильтров в Sphinx, более наглядно.

//Инициализация клиента
$sphinx = new sfSphinxClient(array('offset' => 0, 'limit' => 1));
//Определяем сложный фильтр
$sphinx->SetSelect('*, IF((a >= 2 OR b <= 5) OR (c != 0 AND c = 10), 1, 0) AS cf');
//Именно здесь мы применяем сложный фильтр к выборке. Обязательно без @ и прочих специальных символов!!!
$sphinx->SetFilter('cf', array(1));
//Получаем результат
$result = $sphinx->getRes();

Можно, также, использовать несколько IF в одном SELECT — разделяются запятой и должны , и даже проводить над результатами различные операции, например:

IF(a >= 2 OR b <= 5, 1, 0) + IF(c != 0 AND c = 10, 5, 0)

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