За последние несколько лет в сообществе Laravel приобрели большую популярность поисковые инструменты ElasticSearch и Algolia — мощные средства для индексирования и поиска ваших данных. Бен Корлет проделал невероятную работу, представив ElasticSearch на Laracon Eu 2014, а я написал запрос на включение в Laravel написанного мной индексирования для документов на основе ElasticSearch в 2015 году. Но перед тем, как мой запрос был одобрен, люди из Algolia взяли мой код и переделали его на основе Algolia (она быстрее, и у неё лучше пользовательский интерфейс!), и теперь именно этот поиск вы видите в документации Laravel.
Если вы посмотрите на мой или на их код, то поймёте, что интеграция полнотекстового поиска в ваш сайт — не самая простая задача. Позже Algolia выпустили свободный продукт Algolia DocSearch, который позволяет легко добавить поисковый виджет Algolia на страницы с документацией. Но в остальных случаях вам по-прежнему надо писать интеграцию собственноручно — так было до сих пор.
Знакомство с Laravel Scout
Scout — это полнотекстовый поиск на основе драйвера для Eloquent. Scout позволяет легко индексировать и искать содержимое ваших моделей Eloquent. Сейчас он работает с Algolia и ElasticSearch, но Тейлор просит сообщество поучаствовать в реализации поддержки других сервисов полнотекстового поиска.
Scout — отдельный пакет Laravel, как Cashier, который вам надо установить с помощью Composer. Мы добавим в наши модели типажи (traits), которые указывают Scout, что он должен слушать события, возникающие при изменении экземпляров этих моделей, и обновлять поисковый индекс.
Взгляните на синтаксис полнотекстового поиска любого PHPReview
, содержащего слово Llew:
Review::search('Llew')->get();
Review::search('Llew')->paginate(20);
Review::search('Llew')->where('account_id', 2)->get();
Это требует минимальных настроек. Замечательная вещь.
Установка Scout
Сначала получите пакет (актуальный, с поддержкой приложений Laravel 5.3):
shcomposer require laravel/scout
Затем добавьте сервис-провайдер Scout (PHPLaravel\Scout\ScoutServiceProvider::class
) в раздел providers в файле config/app.php.
Теперь настроим конфигурацию нашего Scout. Выполните shphp artisan vendor:publish
и вставьте свои учётные данные Algolia в config/scout.php.
И наконец, если вы используете Algolia, установите Algolia SDK:
shcomposer require algolia/algoliasearch-client-php
Маркировка модели для индексирования
Теперь откройте свою модель (для примера мы используем модель книжных обзоров PHPReview
). Импортируйте типаж PHPLaravel\Scout\Searchable
. Вы можете задать доступные для поиска свойства с помощью метода PHPtoSearchableArray()
(по умолчанию он соответствует методу PHPtoArray()
), и задать имя индекса модели с помощью метода PHPsearchableAs()
(по умолчанию это имя таблицы).
После этого мы можем зайти и проверить свою страницу индекса Algolia на их сайте. Когда вы добавляете, обновляете или удаляете записи PHPReview
, изменяется ваш индекс Algolia. Вот так просто.
Поиск вашего индекса
Мы уже видели этот пример, но теперь рассмотрим, как выполнять поиск:
// Получить все записи из Review с термином "Llew"
Review::search('Llew')->get();
// Получить все записи из Review с термином "Llew",
// ограничить вывод по 20 на страницу и читать параметр запроса ?page,
// как в страничном выводе Eloquent
Review::search('Llew')->paginate(20);
// Получить все записи из Review с термином "Llew"
// и значением поля account_id равным 2
Review::search('Llew')->where('account_id', 2)->get();
Что вернут эти методы поиска? Коллекцию моделей Eloquent, воссозданных из вашей базы данных. ID хранятся в Algolia, которая возвращает список найденных в результате поиска ID, по ним Scout получает записи из БД и возвращает их в виде объектов Eloquent.
У вас нет всех возможностей SQL-команд where, зато доступен основной функционал проверок сравнения, как вы увидели в приведённых примерах.
Очереди
Вы могли догадаться, что при каждом запросе на изменение записи в БД происходит HTTP-запрос к Algolia. Это может очень быстро замедлить работу приложения, поэтому было бы неплохо выполнять эти операции в очереди. Это довольно просто.
В файле config/scout.php задайте параметру queue значение PHPtrue
, тогда обновления будут индексироваться асинхронно. Это так называемая «согласованность в конечном счёте» (eventual consistency) — изменения в записях БД будут происходить немедленно, а обновления поисковых индексов пройдут через очередь так быстро, насколько позволяет ваш обработчик очереди.
Особые случаи
Рассмотрим некоторые особые случаи.
Выполнение операций без индексирования
Если вам надо выполнить набор операций и при этом избежать индексирования, просто оберните их в метод PHPwithoutSyncingToSearch()
на вашей модели:
Review::withoutSyncingToSearch(function () {
// например, создать несколько обзоров
factory(Review::class, 10)->create();
});
Ручной вызов индексирования в коде
Предположим, вы хотите выполнить индексирование после успешного завершения массовой операции. Как это сделать?
Просто добавьте PHPsearchable()
в конец любого запроса Eloquent, и он проиндексирует все найденные этим запросом записи:
Review::all()->searchable();
Также вы можете ограничить набор записей для выполнения запроса только теми записями, которые хотите проиндексировать, но стоит отметить, что индексирование вставит новые записи и обновит старые, поэтому будет неплохо запустить его над теми записями, которые могут быть уже проиндексированными.
$user->reviews()->searchable();
Также вы можете отменить индексирование любых записей с помощью метода PHPunsearchable()
, используя тот же способ сцепки запросов:
Review::where('sucky', true)->unsearchable();
Ручной вызов индексирования в командной строке
Вот Artisan-команда для этого:
shphp artisan scout:import App\\Review
Она возьмёт все модели Review и проиндексирует их.
Заключение
Вот и всё! Теперь у вас есть полноценный полнотекстовый поиск по вашим моделям Eloquent.
Комментарии (2)
Можно ли использовать с версией 5.1
Смотрел я Laravel Scout. Не очень убедительная штука. Во-первых, ясно, что Тейлор за Algolia, т.к. она платная и у него конечно есть тут коммерческий интерес. Поддержку Elasticsearch он то добавлял, то исключал, потом опять включал. Есть в его действиях какая-то непоследовательность. Ему даже в твиттере об этом кто-то написал.
Поддержка бесплатного и прекрасного Elasticsearch в Laravel Scout очень ограничена. Например, там нет агрегаций. Вывод такой — вместо использования скаута можно спокойно использовать полнотекстовый поиск mysql. А если нужен серьезный поиск по большой базе, то гораздо лучше поставить стандартный php-клиент от сообщества Elasticsearch, который может реально все!