Laravel Scout para busca full-text
1. Introdução ao Laravel Scout e busca full-text
O Laravel Scout é um pacote oficial do ecossistema Laravel que fornece uma abstração elegante para implementar busca full-text em suas aplicações. Diferente das buscas tradicionais com LIKE no SQL, que varrem tabelas inteiras e se tornam lentas com grandes volumes de dados, a busca full-text utiliza índices invertidos para retornar resultados relevantes em milissegundos.
Enquanto uma consulta WHERE title LIKE '%termo%' realiza uma varredura linear (e não utiliza índices tradicionais), o Scout opera sobre engines especializados como Algolia, Meilisearch, MySQL/PostgreSQL nativos ou engines personalizados. Isso permite:
- Busca por relevância com pontuação (scoring)
- Stemming (radicais de palavras)
- Tolerância a erros de digitação (typo tolerance)
- Filtros combinados com busca textual
Os drivers suportados oficialmente são: Algolia (SaaS), Meilisearch (self-hosted), MySQL/PostgreSQL nativos (via MATCH AGAINST), e a possibilidade de criar engines customizados.
2. Instalação e configuração inicial
A instalação é direta via Composer:
composer require laravel/scout
Após a instalação, publique o arquivo de configuração:
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
No arquivo config/scout.php, você encontrará as configurações principais:
<?php
return [
'driver' => env('SCOUT_DRIVER', 'algolia'),
'queue' => env('SCOUT_QUEUE', false),
'after_commit' => env('SCOUT_AFTER_COMMIT', false),
// ...
];
Para usar o driver MySQL/PostgreSQL nativo, instale o pacote complementar:
composer require laravel/scout
E configure seu .env:
SCOUT_DRIVER=database
DB_CONNECTION=mysql
Para MySQL, certifique-se de que suas tabelas tenham índices FULLTEXT:
ALTER TABLE posts ADD FULLTEXT INDEX fulltext_index (title, body);
3. Preparando modelos Eloquent para busca
Adicione o trait Searchable ao seu modelo:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Post extends Model
{
use Searchable;
protected $fillable = ['title', 'body', 'category_id', 'published_at'];
}
Personalize o índice com searchableAs() e toSearchableArray():
public function searchableAs()
{
return 'posts_index';
}
public function toSearchableArray()
{
$array = [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body,
'category' => $this->category->name,
'published_at' => $this->published_at->timestamp,
];
// Remova campos sensíveis ou irrelevantes
unset($array['author_ip']);
return $array;
}
4. Indexação e sincronização de dados
Importe registros existentes em massa:
php artisan scout:import "App\Models\Post"
Para sincronização automática, o Scout escuta eventos Eloquent (created, updated, deleted). Para ambientes de produção, recomenda-se usar filas:
// config/scout.php
'queue' => env('SCOUT_QUEUE', true),
Comandos úteis:
# Limpar índice
php artisan scout:flush "App\Models\Post"
# Sincronizar configurações do índice (Algolia/Meilisearch)
php artisan scout:sync-index-settings
5. Realizando buscas full-text
A sintaxe básica é extremamente simples:
$results = Post::search('Laravel Scout')->get();
Para paginação e ordenação:
$results = Post::search('busca avançada')
->where('category_id', 5)
->orderBy('published_at', 'desc')
->paginate(15);
Buscas com relevância personalizada:
$results = Post::search('framework PHP', function ($query, $callback) {
$query->with(['attributes' => [
'title' => ['boost' => 10],
'body' => ['boost' => 2],
]]);
})->get();
6. Filtros avançados e consultas complexas
Combinando Scout com Eloquent para filtros avançados:
$results = Post::search('tutorial')
->where('category_id', 3)
->where('published_at', '>=', now()->subMonth()->timestamp)
->query(function ($builder) {
$builder->with('author');
})
->get();
Para filtros booleanos e intervalos numéricos:
$results = Post::search('avançado')
->where('is_featured', true)
->where('views', '>=', 1000)
->get();
Usando raw expressions para consultas específicas do driver (Meilisearch):
$results = Post::search('php')
->whereRaw([
'filter' => 'category_id = 5 AND published_at > 1700000000'
])
->get();
7. Personalização e otimização de desempenho
Controle granular de indexação:
// Adicionar ao índice
$post->searchable();
// Remover do índice
$post->unsearchable();
// Condicional
$post->searchable() : $post->unsearchable();
Criando um engine personalizado:
<?php
namespace App\ScoutEngines;
use Laravel\Scout\Engines\Engine;
class CustomEngine extends Engine
{
public function search($query)
{
// Implementação personalizada
}
// ...
}
Estratégias de cache:
$results = Cache::remember('search_results_' . md5($query), 3600, function () use ($query) {
return Post::search($query)->get();
});
Para monitoramento, integre com Laravel Telescope:
composer require laravel/telescope
8. Considerações finais e boas práticas
Escolha do driver ideal:
| Cenário | Driver recomendado |
|---|---|
| Projeto pequeno/médio | MySQL/PostgreSQL nativo |
| Alta performance e escalabilidade | Meilisearch (self-hosted) |
| SaaS gerenciado, sem infra | Algolia |
| Necessidades muito específicas | Engine customizado |
Limitações importantes:
- Campos não indexáveis: relacionamentos muitos-para-muitos não são automaticamente indexados
- Tamanho máximo de documento: Algolia limita a 10KB por registro
- O Scout não substitui consultas Eloquent complexas; use-o apenas para busca textual
Exemplo completo: API de busca com Laravel Sanctum:
<?php
namespace App\Http\Controllers\Api;
use App\Models\Post;
use Illuminate\Http\Request;
class SearchController extends Controller
{
public function __invoke(Request $request)
{
$request->validate([
'q' => 'required|string|min:2',
'category' => 'nullable|exists:categories,id',
'per_page' => 'nullable|integer|min:1|max:100',
]);
$query = Post::search($request->q);
if ($request->category) {
$query->where('category_id', $request->category);
}
return response()->json(
$query->paginate($request->per_page ?? 15)
);
}
}
Para integração com outras ferramentas da série, lembre-se de:
- Usar PHPStan para análise estática das consultas Scout
- Configurar Horizon para processar filas de indexação
- Versionar as configurações do índice no controle de versão
O Laravel Scout transforma a implementação de busca full-text de uma tarefa complexa em algo trivial, mantendo a flexibilidade para casos avançados. Comece com o driver nativo do banco de dados e migre para soluções mais robustas conforme sua aplicação cresce.
Referências
- Documentação oficial do Laravel Scout — Guia completo de instalação, configuração e uso do Scout
- Meilisearch Documentation for Laravel — Tutorial oficial de integração Meilisearch com Laravel
- Algolia Laravel Integration Guide — Guia prático de implementação com Algolia
- Usando Scout com MySQL Full-Text Search — Artigo técnico sobre configuração do driver MySQL nativo
- Laravel Scout: The Complete Guide — Tutorial abrangente com exemplos de busca avançada e filtros
- Performance Optimization with Laravel Scout — Dicas de otimização e cache para Scout em produção