MeiliSearch и Laravel

С выходом Scout 9 появился стандартный драйвер для MeiliSearch в Laravel. В связи с чем немного изменилась установка и настройка поиска по сайту.

Установка и настройка MeiliSearch

Для тестирования поиска устанавливал MeiliSearch в Windows. Для это достаточно зайти на страницу проекта в GitHub и найти последнюю сборку, файл с именем meilisearch-windows-amd64.exe или как-то похоже. Скачать и запустить. Собственно все.

Для установки на Linux (у меня Ubuntu), в командной строке:


echo "deb [trusted=yes] https://apt.fury.io/meilisearch/ /" > /etc/apt/sources.list.d/fury.list
apt update && apt install meilisearch-http

Чтобы каждый раз не запускать движок вручную сделаем его сервисом, для этого создадим файл:


sudo nano /etc/systemd/system/meilisearch.service

Скопируем текст в файл, задав свой мастер-ключ:


[Unit]
Description=MeiliSearch
After=systemd-user-sessions.service

[Service]
Type=simple
ExecStart=/usr/bin/meilisearch --http-addr 127.0.0.1:7700 --env production --master-key masterKey

[Install]
WantedBy=default.target

Запускаем и убеждаемся, что все работает:


systemctl enable meilisearch
systemctl start meilisearch
systemctl status meilisearch

Установка не занимает много времени, а на сайте MeiliSearch можно найти актуальные инструкции.

Установка и настройка Scout в Laravel

Для работы с поиском необходим стандартный модуль Laravel Scout. Установим его в проект с помощью composer:

composer require laravel/scout

Публикуем конфигурацию:

php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"

Устанавливаем SDK MeiliSearch:

composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle

В .env добавляем настройки:


SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=masterKey

Добавление поиска в модель

Для добавления в поиск информации необходимо настроить модель в Laravel. На примере модели Blog:


use Laravel\Scout\Searchable;

class Blog extends Model
{
    use Searchable; // Обязательно добавляем трейт Searchable

    // При желании прописываем имя индекса. Лучше прописать, если есть сомнения.
    public function searchableAs()
    {
        return 'blog';
    }

    // Настраиваем список полей, которые будут индексироваться
    // Лучше прописать, а то будет индексировать все
    public function toSearchableArray()
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'teaser' => $this->teaser,
            'body' => $this->body,
            'comments' => $this->comments->implode('comment', '; '),
        ];
    }

    // Если модель по каким-то причинам не надо включать в поиск, задаем тут:
    public function shouldBeSearchable()
    {
        return $this->public; 
    }

Индексирование и поиск

Руками создавать индекс в MeiliSearch не обязательно, достаточно выполнить команду:


php artisan scout:import "App\Models\Blog"

После чего все модели проиндексируются. Переиндексация происходит автоматически при обновлении, создании и удалении модели. Поиск по модели осуществляется с помощью функции search($text). Приведу пример ендпоинта в контроллере, который осуществляет поиск:


    public function search(Request $request)
    {
        $text = $request->get('query');
        $blogs = Blog::search($text)->take(20)->get();
        return view('page.search', ['blogs' => $blogs, 'query' => $text]);
    }

Фильтры в поиске

Если вы планируете использовать конструкцию where или whereIn необходимо вручную создать фильтр в MeiliSearch. Иначе будет вылезть ошибка вида:


Attribute `xxx` is not filterable. Available filterable attributes are: ``.

Фильтры создаются через обращение к API напрямую, например, curl:


curl \
  -X POST 'http://localhost:7700/indexes/blog/settings' \
  -H 'Content-Type: application/json' \
  -H 'X-Meili-API-Key: masterKey' \
  --data-binary '{
      "filterableAttributes": [
          "xxx"
      ]
  }'

Тут надо заменить xxx, на имя своего атрибута. Также можно создавать фильтр с помощью PHP:


use MeiliSearch\Client;

$client = new Client('http://127.0.0.1:7700');
$client->index('blog')->updateFilterableAttributes(['public']);

Обратите внимание, что фильтр создается только один раз. Пример использования фильтра:


$blogs = Blog::search($text)->where('public', 1)->get();

Лишних фильтров лучше не плодить, они занимают много места.

Пагинация результатов поиска

Поиск поддерживает стандартную пагинацию:


$blogs = Blog::search($text)->paginate(20);

В шаблоне страницы добавляем:


{{ $blogs->links() }}

Резюме

MeiliSearch хорошо работает с русским языком. Учитывает склонения, прощает ошибки и опечатки. MeiliSearch шустро работает, хотя при первом запуске может конкретно загрузить систему на несколько часов индексацией. В целом резульаты поиска весьма релевантны, оценить поиск можно на этом сайте. Можно рекомендовать MeiliSearch как поисковый движок для небольших и средних проектов. Его можно быстро установить и настроить, он не требователен к ресурсам и выдает хорошие результаты.

10.01.2022