Как построить запрос типа WHERE MATCH AGAINST в Kohana3
Нравиться мне Фреймворк Kohana, он легкий простой, пока дает все что мне нужно от моих задач.
И вот случилось у меня некая трудность, как я думал в начале, с написание запроса в условиях которого содержится конструкция MATCH(col1, col2,…) AGAINST(»text search»). На данный момент ORM я не юзаю, т.к. не представляю как можно там получить записи при объединении нескольких таблиц, ил запись с таблицы со сложным условием, но то ли еще будет…
Немного отступлю от проблемы, вкратце расскажу, для непосвященных, что это за конструкция. Это конструкция позволяет делать полнотекстовый поиск в наборе указанных полей. Имеет некие настройки для поиска, ищет в указанных полях. Для работы нужна таблица только! типа MyISAM, а также индекс FULLTEXT на искомые поля. Поля должны быть типа VARCHAR, CHAR, TEXT. Более детально на официальной документации.
И так, нужно составить запрос типа:
SELECT * FROM `table` WHERE MATCH(`col1`) AGAINST("search text");
Можно поступить двумя способами.
1 Просто написать такой запрос и запихнуть его в конструкцию:
$data = DB::query(Database::SELECT, 'SELECT * FROM `table` WHERE MATCH(`col1`) AGAINST("search text")')->execute()->as_array();
В переменной $data у нас будет результат запроса.
Это самый простой способ получения запроса, но иногда такого мало. Например у меня есть запрос на поиск, который включает себя много полей, разные условия поиска и еще разные условия сортировки. В таком случае уж трудно будет изменять запрос… (У меня в модели несколько методов наразные части запроса — очень удобно управлять запросом).
2 Более сложным, но более эффективным путем при решении сложных запросов лучше пользоваться конструктором запросов. Данный пример вверху можно записать так (пока без части с уловием):
$data = DB::select()->from('table')->execute()->as_array();
Теперь как же быть с условием? Для условий where() существует целый класс Database_Query_Builder_Where в нем содержаться куча методов для построения запросов с логикой AND и OR.
Сам запрос сводиться к 3-м обязательным параметрам:
$db->where('column', 'operator', 'value'); например WHERE id=2 будет записано в виде: $db->where('id', '=', 2);
Причем если значение это integer кавычек не будет, а если string система сама поставит кавычки.
Вернемся к нашему выражению.
SELECT * FROM `table` WHERE MATCH(`col1`) AGAINST("search text")
Что же здесь у нас столбец, что оператор и что значение??? Поставив такое выражение
$db->where('MATCH(col1)', '', 'AGAINST ("search text")');
я получи вид
WHERE `MATCH(col1)` 'AGAINST(\"search text\")'
естественно запрос не сработал… Немного поразмыслив я попытался обратиться к классу DB::expr(), который позволяет вставить в запрос выражение «как есть», т.е. не обрамляет его символами ` . И вот что получилось:
$query_m = DB::expr(' MATCH(`col1`) '); $query_a = DB::expr(' AGAINST(addslashes("search text")) '); $db->where($query_m, '', $query_a);
Естественно при использовании класса DB::expr() приходиться самому заботиться об SQL-инъекциях добавляя ф-цию addslashes, в итоге получилось выражение вида:
WHERE MATCH(`col1`) AGAINST("search text")
И так, подводим итог всей этой ночной писанины:
Для использования в условиях запроса сложных выражений при использовании конструктора запросов, необходимо прибегать к помощи класса DB::expr().
Вот полный пример:
$query_m = DB::expr(' MATCH(col1) '); $query_a = DB::expr(' AGAINST(addslashes("search text")) '); DB::select()->from('table')->where($query_m, '', $query_a); получим запрос вида: SELECT * FROM `table` WHERE MATCH(col1) AGAINST("search text")
Также можно построить такой запрос:
SELECT * FROM `table` WHERE FIELD_IN_SET('1,2,4,5', `column`);
пишем
$f = DB::expr(' FIND_IN_SET('1,2,4,5', '); $c = DB::expr(' `column` '); DB::select()->from('table')->where($f, '', $c); В итоге SELECT * FROM `table` WHERE FIELD_IN_SET('1,2,4,5', `column`)
Всем удачи в работе!
Статья просмотренна 114840 раз, зашло посетителей 29440
Почему не использовать Database::instance()->escape($var)?
1. Данный метод удобно экранирует различные значения (числа, строки, массивы, объекты Database_Expression).
2. Он не привязан к конкретной СУБД — все решает текущий драйвер.
В целом, реализацию таких СУБД-зависимых конструкций желательно было бы прятать в реализацию драйвера. Но в принципе и так неплохо (если все это будет осуществляться в методах модели, а не контроллера).
Эээ… ну честно как-то не вглядывался в дебри класса, спасибо большое за наводку, буду юзать его. Сейчас вот глянув в указанный тобой класс и увидел много плюшек. Спасибо.
Почему бы не использовать бинд переменные? Это будет самое правильное решение…
Покажи как можно ими пользоваться, какие + и -. Развей тему.
Чтож! Не плохой вариант для поиска. Конечно как и все варианты имеет свои особенности, но куда без них. Многопараметровый поиск заинтересовал.
DB::select()->from('table')->where(DB::expr(' MATCH(col1) '), '', DB::expr(' AGAINST(:search) ', array(':search' => $search_text)));
А чем Ваше выражение отличается от моего? Только тем что я вынес две составляющие в разные переменные?
Статья хорошая. Единственное можно добавить про AGAINST по больше и про релевантность выдачи.
Павел, рассказ о работе данной конструкции выходит за рамки данной статьи, статья была о том, как именно делать такие запросы на данном фрейме.