Заметки по Sphinx (примеры на Sphinx PHP API)
Столкнувшись с медленными выборками в MySQL, начал осваивать Sphinx и его особенности. Работаю со sphinx через sphinxphpapi.php, который находится в официальном дистрибутиве Sphinx. По мере того, как буду сталкиваться с интересными задачами, буду пополнять список заметок. Задавайте вопросы в комментарии, постараемся решить их вместе.
Касаемо установки Sphinx и тонкостей его настройки можно легко найти как в официальной документации, так и на сторонних ресурсах. Возможно, напишу по этому поводу отдельную статью, если будет такая необходимость.
1. Допустим, у вас есть список товаров, которые находятся в разных категориях. Нам нужно получить минимальную цену, максимальную цену и среднюю цену в категории. Обычный SQL выглядел бы так:
1 | SELECT MAX(price), MIN(price), AVG(price) FROM items WHERE cat_id = 2; |
По сути, запрос на минимальное и максимальное значение не особо требователен, если на поле price поставлен индекс, но если уже ставим дополнительные условия и хотим получить среднее значение, то запрос выполняется достаточно долго при большом количестве записей. В данном случае мы получим одну строку с желаемыми результатами.
В sphinx возможно получить эти же параметры
1 2 | $sphinx->SetSelect("MAX(price) as max_price, MIN(price) as min_price, AVG(price) as avg_price"); $sphinx->SetFilter("cat_id", array(2)); |
Мы получим набор записей, у которых будут непонятные, но ожидаемые значения. А дело кроется за малым и об этом нам гласит документация, до которой я добрался почему-то не в первую очередь
Starting with version 0.9.9-rc2, aggregate functions (AVG(), MIN(), MAX(), SUM()) are supported when using GROUP BY.
Группировать надо по полю, для которого значение в выборке будет везде одинаковое значение. В нашем случае это cat_id. А что если нам надо сделать подсчет сделать во всех записях, но полей с одинаковыми значениями нет? Просто делаем небольшой трюк.
1 2 | $sphinx->SetSelect("1 AS q, MAX(price) as max_price, MIN(price) as min_price, AVG(price) as avg_price"); $sphinx->SetGroupBy('q', SPH_GROUPBY_ATTR); |
Как видно, мы добавили новое поле q
, которое всегда будет равно единице и группируем по нему.
2. Данный пункт можно отнести к предыдущему. Если у нас не указана цена для товара и поле в таблице имеет NULL, то при выборке значения этих полей будет приравнено к 0. Соответственно это существенно меняет выдачу среднего и, возможно, минимального значения цены в выборке. Если у Вас отсутствуют товары с ценой, равной 0, то смело можно дописать к выборке вот такой код:
1 | $sphinx->SetFilter('price', array(0), true); |
Мы просто исключаем все записи, цена в которых равна 0. Тривиально, но довольно практично.
3. Если у записи есть некоторый набор тегов и при фильтрации по некоторым из них
1 | $sphinx->setFilter('tags', array(10,11,14)); |
в результате получим все записи, у которых встречается хотя бы одно из значений — 10,11,14. Чаще надо отсортировать так, чтобы нашлись записи, у которых присутствуют все значения одновременно, для этого мы присваиваем фильтр для каждого значения в отдельности:
1 2 3 | foreach(array(10,11,14) as $v) { $sphinx->setFilter('tags', array($v)); } |
В таком случае будут найдены записи, у которых присутствуют все три тега одновременно