PHPackages                             lucasbrito-wdt/laravel-database-fts - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. lucasbrito-wdt/laravel-database-fts

ActiveLibrary[Database &amp; ORM](/categories/database)

lucasbrito-wdt/laravel-database-fts
===================================

Full-Text Search nativo para Laravel com suporte a PostgreSQL (pg\_trgm) e MySQL (FULLTEXT), ACL e recursos avançados

v1.1.6(1mo ago)0298↓50%MITPHPPHP ^8.1

Since Jan 8Pushed 1mo agoCompare

[ Source](https://github.com/lucasbrito-wdt/laravel-database-fts)[ Packagist](https://packagist.org/packages/lucasbrito-wdt/laravel-database-fts)[ Docs](https://github.com/lucasbrito-wdt/laravel-database-fts)[ RSS](/packages/lucasbrito-wdt-laravel-database-fts/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (8)Dependencies (10)Versions (12)Used By (0)

Laravel Database Full-Text Search
=================================

[](#laravel-database-full-text-search)

Busca Full-Text nativa para Laravel com suporte a **PostgreSQL** (pg\_trgm) e **MySQL** (FULLTEXT), incluindo ACL e busca parcial automática.

Características
---------------

[](#características)

- ✅ **Multi-Driver**: Suporta PostgreSQL (pg\_trgm) e MySQL (FULLTEXT)
- ✅ **Detecção Automática**: Detecta automaticamente o banco de dados e usa o driver apropriado
- ✅ **Motor nativo**: Zero dependências externas, sem serviços adicionais
- ✅ **Índices otimizados**: GIN com gin\_trgm\_ops (PostgreSQL) ou FULLTEXT (MySQL)
- ✅ **Busca parcial automática**: Funciona com termos incompletos (ex: "adm" encontra "admin") - PostgreSQL
- ✅ **Sem colunas extras**: Não cria colunas adicionais, apenas índices
- ✅ **Detecção automática**: Lê campos da model automaticamente via trait
- ✅ **Método customizado no Blueprint**: Use `$table->searchableIndex()` diretamente em `Schema::table()`
- ✅ **Helpers na migration gerada**: Métodos `createSearchableIndex()` e `dropSearchableIndex()` disponíveis nas migrations geradas pelo comando
- ✅ **ACL**: Controle de acesso baseado em visibilidade
- ✅ **Similaridade configurável**: Threshold ajustável para precisão vs recall
- ✅ **Suporte a estruturas customizadas**: Funciona com `Domains/*/Models/*` e outros namespaces

Requisitos
----------

[](#requisitos)

- PHP &gt;= 8.1
- Laravel &gt;= 10.0
- **PostgreSQL &gt;= 12.0** (com extensão `pg_trgm` - criada automaticamente) **OU**
- **MySQL &gt;= 5.7** (com engine InnoDB ou MyISAM)

Instalação
----------

[](#instalação)

```
composer require lucasbrito-wdt/laravel-database-fts
```

Publique o arquivo de configuração:

```
# Usando a tag específica
php artisan vendor:publish --tag=fts-config

# Ou usando a tag completa
php artisan vendor:publish --tag=laravel-database-fts-config
```

Isso criará o arquivo `config/fts.php` na sua aplicação Laravel com todas as configurações padrão do pacote.

Configuração
------------

[](#configuração)

O arquivo de configuração `config/fts.php` contém todas as opções:

```
return [
    /*
    |--------------------------------------------------------------------------
    | Driver de Busca
    |--------------------------------------------------------------------------
    |
    | Define qual driver de busca será usado. Opções:
    | - 'auto': Detecta automaticamente baseado na conexão do banco de dados
    | - 'postgres': Força uso do driver PostgreSQL (pg_trgm)
    | - 'mysql': Força uso do driver MySQL (FULLTEXT)
    |
    | Recomendado: 'auto' para detecção automática baseada na conexão ativa.
    |
    */
    'driver' => env('FTS_DRIVER', 'auto'),

    /*
    |--------------------------------------------------------------------------
    | Threshold de Similaridade
    |--------------------------------------------------------------------------
    |
    | Threshold padrão para busca por similaridade.
    | Valores entre 0.0 e 1.0. Quanto menor, mais resultados serão retornados.
    |
    | Para PostgreSQL (pg_trgm):
    |   - Controla a similaridade de trigramas (0.0 a 1.0)
    |   - Valores menores retornam mais resultados
    |
    | Para MySQL (FULLTEXT):
    |   - Se >= 0.3: usa NATURAL LANGUAGE MODE (busca mais precisa)
    |   - Se < 0.3: usa BOOLEAN MODE (busca mais flexível)
    |
    */
    'similarity_threshold' => env('FTS_SIMILARITY_THRESHOLD', 0.2),

    /*
    |--------------------------------------------------------------------------
    | Configurações de ACL (Access Control List)
    |--------------------------------------------------------------------------
    |
    | Configurações para controle de acesso baseado em visibilidade.
    |
    */
    'acl' => [
        'column' => env('FTS_ACL_COLUMN', 'visibility'),
        'ranking_multipliers' => [
            'public' => 1.2,
            'internal' => 1.0,
            'private' => 0.5,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Métricas e Logging
    |--------------------------------------------------------------------------
    |
    | Quando habilitado, todas as buscas são logadas com informações de
    | performance (termo, tempo de execução, quantidade de resultados, driver usado).
    |
    | Útil para monitoramento e otimização de queries de busca.
    |
    */
    'metrics' => [
        'enabled' => env('FTS_METRICS_ENABLED', true),
        'log_channel' => env('FTS_LOG_CHANNEL', 'daily'),
    ],
];
```

### Variáveis de Ambiente

[](#variáveis-de-ambiente)

Você pode configurar via `.env`:

```
# Driver de busca (auto, postgres, mysql)
FTS_DRIVER=auto

# Threshold de similaridade (0.0 a 1.0)
FTS_SIMILARITY_THRESHOLD=0.2

# ACL
FTS_ACL_COLUMN=visibility

# Métricas
FTS_METRICS_ENABLED=true
FTS_LOG_CHANNEL=daily
```

Uso Rápido
----------

[](#uso-rápido)

### Escolha o Método de Criação do Índice

[](#escolha-o-método-de-criação-do-índice)

MétodoQuando UsarExemplo**`$table->searchableIndex()`**Ao criar índice em tabela existente`Schema::table()`**`make:searchable`**Para tabelas existentes ou múltiplas models`php artisan make:searchable Post`**Trait PostgresFullTextMigration**Controle manual avançado`$this->addSearchableIndex($table, ...)`**Helpers (migration gerada)**Só em migrations geradas pelo comando`$this->createSearchableIndex()`### 1. Configurar o Model

[](#1-configurar-o-model)

Primeiro, adicione o trait `Searchable` e defina os campos pesquisáveis:

```
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use LucasBritoWdt\LaravelDatabaseFts\Traits\Searchable;

class Post extends Model
{
    use Searchable;

    protected static array $searchable = [
        'title',
        'body',
    ];
}
```

**Para estruturas customizadas (ex: Domains):**

```
namespace App\Domains\Blog\Models;

use Illuminate\Database\Eloquent\Model;
use LucasBritoWdt\LaravelDatabaseFts\Traits\Searchable;

class Post extends Model
{
    use Searchable;

    protected static array $searchable = [
        'title',
        'body',
    ];
}
```

### 2. Gerar Migration Automaticamente

[](#2-gerar-migration-automaticamente)

#### Opção 1: Para uma model específica

[](#opção-1-para-uma-model-específica)

Use o comando Artisan para gerar a migration para uma model específica. **Não precisa passar os campos** - eles são lidos automaticamente da model:

```
php artisan make:searchable Post
```

#### Opção 2: Para todas as models automaticamente

[](#opção-2-para-todas-as-models-automaticamente)

Gere migrations para **todas as models** que usam o trait `Searchable` de uma vez:

```
php artisan make:searchable --all
```

Ou simplesmente:

```
php artisan make:searchable
```

Este comando:

- 🔍 Busca automaticamente todas as models em `App\Models\`, `App\` e `App\Domains\*\Models\`
- ✅ Verifica quais usam o trait `Searchable`
- ✅ Verifica se têm o array `$searchable` definido
- 📝 Gera migrations para todas as models encontradas
- ⚠️ Ignora models que já têm migration existente
- 📊 Mostra resumo com sucessos e erros

**Exemplo de saída:**

```
Buscando todas as models que usam o trait Searchable...

Encontradas 3 model(s):
  - App\Models\Post
  - App\Domains\Auth\Models\User
  - App\Domains\Blog\Models\Article

Deseja gerar migrations para todas essas models? (yes/no) [yes]:
  ✅ Post: Migration criada
  ✅ User: Migration criada
  ⚠️  Article: Migration já existe (2026_01_08_030322_add_searchable_index_to_articles_table.php)

Concluído! 2 migration(s) criada(s), 0 erro(s).

```

**Detalhes do comando:**

- Detecta automaticamente a model (busca em `App\Models\`, `App\` e `App\Domains\{domain}\Models\{model}`)
- Verifica se a model usa o trait `Searchable`
- Lê os campos do array `$searchable` automaticamente
- Cria migration que lê os campos da model na execução
- Cria índice GIN usando `gin_trgm_ops`
- Cria extensão `pg_trgm` automaticamente

**A migration gerada lê automaticamente os campos da model quando executada!**

### 3. Criar Índice no Schema::create()

[](#3-criar-índice-no-schemacreate)

**⚠️ IMPORTANTE:** Para usar `searchableIndex()` dentro de `Schema::create()`, você precisa criar o índice **APÓS** a tabela ser criada:

```
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        // Primeiro, cria a tabela
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('body');
            $table->timestamps();
        });

        // Depois, cria o índice (após a tabela existir)
        Schema::table('posts', function (Blueprint $table) {
            $table->searchableIndex(['title', 'body']);

            // Ou com nome customizado
            // $table->searchableIndex(['title', 'body'], 'posts_custom_search_idx');
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};
```

**Alternativa:** Use o trait `PostgresFullTextMigration`:

```
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use LucasBritoWdt\LaravelDatabaseFts\Traits\PostgresFullTextMigration;

return new class extends Migration
{
    use PostgresFullTextMigration;

    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('body');
            $table->timestamps();
        });

        // Cria o índice após a tabela ser criada
        Schema::table('posts', function (Blueprint $table) {
            $this->addSearchableIndex($table, ['title', 'body']);
        });
    }

    public function down(): void
    {
        Schema::table('posts', function (Blueprint $table) {
            $this->dropSearchableIndex($table);
        });

        Schema::dropIfExists('posts');
    }
};
```

**Nota:** O método `createSearchableIndex()` só está disponível nas migrations geradas pelo comando `make:searchable`. Para migrations manuais, use `Schema::table()` com `searchableIndex()` ou o trait `PostgresFullTextMigration`.

**Para adicionar a uma tabela existente:**

```
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->searchableIndex(['title', 'body']);
        });
    }

    public function down(): void
    {
        Schema::table('posts', function (Blueprint $table) {
            // O método dropSearchableIndex não está disponível no Blueprint
            // Use o helper da migration ou SQL direto
        });
    }
};
```

### 4. Usar Helpers na Migration

[](#4-usar-helpers-na-migration)

A migration gerada pelo comando `make:searchable` inclui métodos helper que podem ser usados manualmente:

```
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        $tableName = 'posts';
        $fields = ['title', 'body'];

        // Usa o helper para criar o índice
        $this->createSearchableIndex($tableName, $fields);

        // Ou com nome customizado
        // $this->createSearchableIndex($tableName, $fields, 'posts_custom_idx');
    }

    public function down(): void
    {
        $tableName = 'posts';

        // Usa o helper para remover o índice
        $this->dropSearchableIndex($tableName);

        // Ou com nome customizado
        // $this->dropSearchableIndex($tableName, 'posts_custom_idx');
    }

    /**
     * Helper para criar índice GIN com pg_trgm para busca por similaridade.
     * Pode ser chamado manualmente se necessário.
     */
    protected function createSearchableIndex(
        string $tableName,
        array $fields,
        ?string $indexName = null
    ): void {
        // Implementação automática na migration gerada
    }

    /**
     * Helper para remover índice de busca por similaridade.
     * Pode ser chamado manualmente se necessário.
     */
    protected function dropSearchableIndex(
        string $tableName,
        ?string $indexName = null
    ): void {
        // Implementação automática na migration gerada
    }
}
```

### 5. Usar Trait PostgresFullTextMigration (Método Legado)

[](#5-usar-trait-postgresfulltextmigration-método-legado)

Alternativamente, você pode usar a trait `PostgresFullTextMigration` diretamente na sua migration:

```
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use LucasBritoWdt\LaravelDatabaseFts\Traits\PostgresFullTextMigration;

class CreatePostsTable extends Migration
{
    use PostgresFullTextMigration;

    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('body');
            $table->timestamps();

            // Adiciona índice para busca por similaridade
            $this->addSearchableIndex($table, ['title', 'body']);
        });
    }

    public function down(): void
    {
        Schema::table('posts', function (Blueprint $table) {
            $this->dropSearchableIndex($table);
        });

        Schema::dropIfExists('posts');
    }
}
```

### 6. Métodos Disponíveis para Criar Índices

[](#6-métodos-disponíveis-para-criar-índices)

O pacote oferece três formas de criar índices de busca:

#### Opção 1: Método Customizado no Blueprint (Mais Simples) ⭐

[](#opção-1-método-customizado-no-blueprint-mais-simples-)

Use diretamente em `Schema::create()` ou `Schema::table()`:

```
Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->text('body');

    // Cria índice automaticamente
    $table->searchableIndex(['title', 'body']);

    // Com nome customizado
    // $table->searchableIndex(['title', 'body'], 'posts_custom_idx');
});
```

**Vantagens:**

- ✅ Mais simples e direto
- ✅ Funciona com method chaining
- ✅ Não precisa de traits ou imports extras
- ✅ Cria extensão `pg_trgm` automaticamente

#### Opção 2: Helpers na Migration (Gerada pelo Comando)

[](#opção-2-helpers-na-migration-gerada-pelo-comando)

A migration gerada pelo `make:searchable` inclui métodos helper:

```
public function up(): void
{
    // Helper para criar índice
    $this->createSearchableIndex('posts', ['title', 'body']);

    // Com nome customizado
    // $this->createSearchableIndex('posts', ['title', 'body'], 'custom_idx');
}

public function down(): void
{
    // Helper para remover índice
    $this->dropSearchableIndex('posts');

    // Com nome customizado
    // $this->dropSearchableIndex('posts', 'custom_idx');
}
```

**Vantagens:**

- ✅ Disponível automaticamente na migration gerada pelo comando `make:searchable`
- ✅ Permite controle total sobre a criação do índice
- ✅ Os métodos `createSearchableIndex()` e `dropSearchableIndex()` são incluídos automaticamente

**Nota:** Esses métodos helper só estão disponíveis nas migrations geradas pelo comando `make:searchable`. Para migrations manuais, use `Schema::table()` com `searchableIndex()` ou o trait `PostgresFullTextMigration`.

#### Opção 3: Trait PostgresFullTextMigration (Método Legado)

[](#opção-3-trait-postgresfulltextmigration-método-legado)

Para compatibilidade com código existente:

```
use LucasBritoWdt\LaravelDatabaseFts\Traits\PostgresFullTextMigration;

class CreatePostsTable extends Migration
{
    use PostgresFullTextMigration;

    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $this->addSearchableIndex($table, ['title', 'body']);
        });
    }
}
```

### 7. Buscar

[](#7-buscar)

**Busca simples:**

```
$results = Post::search('gestão corporativa')->paginate(10);
```

**Busca com threshold customizado:**

```
// Threshold menor = mais resultados (menos preciso)
$results = Post::search('gest', 0.1)->get();

// Threshold maior = menos resultados (mais preciso)
$results = Post::search('gestão', 0.5)->get();
```

**Busca com filtro ACL:**

```
$results = Post::search('termo', null, ['public', 'internal'])->get();
```

**Busca parcial automática:**

```
// Funciona mesmo com termo incompleto
$results = Post::search('adm')->get(); // Encontra "admin", "administrador", etc.
```

Como Funciona a Migration Automática
------------------------------------

[](#como-funciona-a-migration-automática)

A migration consolidada (`2024_01_01_000000_add_searchable_indexes_to_all_tables.php`) é carregada automaticamente pelo `FtsServiceProvider` e:

1. **Detecta automaticamente** todas as models que:

    - Estendem `Illuminate\Database\Eloquent\Model`
    - Usam o trait `LucasBritoWdt\LaravelDatabaseFts\Traits\Searchable`
    - Têm o array `$searchable` definido e não vazio
2. **Busca em múltiplos namespaces:**

    - `App\Models\*`
    - `App\*`
    - `App\Domains\*\Models\*` (estrutura de domínios)
3. **Cria índices automaticamente:**

    - Verifica se o índice já existe (idempotente)
    - Cria apenas índices que não existem
    - Usa a mesma expressão imutável do índice para garantir compatibilidade
4. **É segura para executar múltiplas vezes:**

    - Usa `CREATE INDEX IF NOT EXISTS`
    - Verifica existência antes de criar
    - Não duplica índices

**Vantagens:**

- ✅ Zero configuração - funciona automaticamente
- ✅ Detecta novas models automaticamente
- ✅ Idempotente - pode executar múltiplas vezes sem problemas
- ✅ Suporta estruturas customizadas (Domains, etc.)

Funcionalidades Avançadas
-------------------------

[](#funcionalidades-avançadas)

### Detecção Automática de Campos

[](#detecção-automática-de-campos)

A migration gerada pelo comando `make:searchable` **lê automaticamente** os campos do array `$searchable` da model quando executada. Isso significa:

- ✅ Não precisa passar campos no comando
- ✅ Não precisa editar a migration manualmente
- ✅ Se você mudar os campos na model, basta recriar a migration
- ✅ Funciona com qualquer estrutura (App\\Models, Domains, etc.)

**Como funciona:**

1. O comando `make:searchable Post` gera uma migration
2. A migration busca automaticamente a model que corresponde à tabela
3. Lê o array `$searchable` via Reflection
4. Cria o índice com os campos encontrados

### Suporte a Estruturas Customizadas

[](#suporte-a-estruturas-customizadas)

O pacote detecta automaticamente models em:

- `App\Models\*`
- `App\*`
- `App\Domains\*\Models\*` (estrutura de domínios)

**Exemplo com Domains:**

```
// app/Domains/Auth/Models/User.php
namespace App\Domains\Auth\Models;

use LucasBritoWdt\LaravelDatabaseFts\Traits\Searchable;

class User extends Model
{
    use Searchable;

    protected static array $searchable = ['name', 'email'];
}
```

```
# O comando encontra automaticamente
php artisan make:searchable User
```

### ACL (Access Control List)

[](#acl-access-control-list)

Filtre resultados por visibilidade:

```
// Busca apenas em itens públicos e internos
$results = Post::search('termo', null, ['public', 'internal'])->get();
```

### Como Funciona

[](#como-funciona)

O pacote detecta automaticamente o banco de dados e usa o driver apropriado:

#### PostgreSQL (pg\_trgm)

[](#postgresql-pg_trgm)

1. Divide strings em trigramas (grupos de 3 caracteres)
2. Calcula similaridade entre strings usando a função `similarity()`
3. Usa índice GIN com `gin_trgm_ops` para busca rápida
4. Combina múltiplos campos usando concatenação para busca unificada

#### MySQL (FULLTEXT)

[](#mysql-fulltext)

1. Usa índices FULLTEXT nativos do MySQL
2. Busca usando `MATCH() AGAINST()` para relevância
3. Suporta modos NATURAL LANGUAGE e BOOLEAN
4. Ranking automático por relevância

**Recursos disponíveis:**

- **PostgreSQL**: Termos parciais (ex: "adm" encontra "admin"), erros leves de digitação
- **MySQL**: Busca por palavras completas com ranking de relevância
- Ambos: Múltiplos campos simultaneamente

### Threshold de Similaridade

[](#threshold-de-similaridade)

O threshold controla quantos resultados serão retornados:

- **0.0 - 0.2**: Muitos resultados, menos preciso (padrão: 0.2)
- **0.3 - 0.5**: Balanceado
- **0.6 - 1.0**: Poucos resultados, muito preciso

Ajuste conforme necessário:

```
// Mais resultados
Post::search('termo', 0.1)->get();

// Menos resultados, mais precisos
Post::search('termo', 0.4)->get();
```

SearchService Multi-Model
-------------------------

[](#searchservice-multi-model)

Busque em múltiplos models simultaneamente com ranking unificado:

```
use LucasBritoWdt\LaravelDatabaseFts\Services\SearchService;

$results = app(SearchService::class)
    ->register(Post::class)
    ->register(Document::class)
    ->register(Ticket::class)
    ->search('fluxo de caixa', null, ['public', 'internal']);
```

Os resultados são ordenados por score (similaridade) independente do model de origem.

Estrutura do Banco de Dados
---------------------------

[](#estrutura-do-banco-de-dados)

O pacote cria automaticamente os índices apropriados para cada banco:

**PostgreSQL:**

- Extensão `pg_trgm` (se não existir)
- Índice GIN com `gin_trgm_ops` na expressão concatenada

**MySQL:**

- Índice FULLTEXT nas colunas especificadas

**Não cria colunas extras** - apenas índices para performance.

Exemplo de Índice Criado
------------------------

[](#exemplo-de-índice-criado)

O pacote cria índices apropriados para cada banco de dados:

### PostgreSQL

[](#postgresql)

```
CREATE EXTENSION IF NOT EXISTS pg_trgm;

CREATE INDEX IF NOT EXISTS posts_search_trgm_idx
ON posts
USING GIN (
    (COALESCE(title::text, '') || ' ' || COALESCE(body::text, '')) gin_trgm_ops
);
```

**Por que não usar `concat_ws`?**

- A função `concat_ws` não é `IMMUTABLE` no PostgreSQL
- Índices requerem funções imutáveis
- A solução usa `COALESCE` e concatenação `||` que são imutáveis

### MySQL

[](#mysql)

```
CREATE FULLTEXT INDEX posts_search_ft_idx
ON posts (title, body);
```

Fluxo Completo de Uso
---------------------

[](#fluxo-completo-de-uso)

### Opção A: Usando o Comando Artisan (Recomendado)

[](#opção-a-usando-o-comando-artisan-recomendado)

1. **Configure o Model:**

```
class Post extends Model
{
    use Searchable;

    protected static array $searchable = ['title', 'body'];
}
```

1. **Gere a Migration:**

```
php artisan make:searchable Post
```

1. **Execute a Migration:**

```
php artisan migrate
```

A migration automaticamente:

- Encontra a model `Post`
- Lê os campos `['title', 'body']` do array `$searchable`
- Cria o índice com esses campos

1. **Use a Busca:**

```
Post::search('termo')->get();
```

### Opção B: Criando Manualmente no Schema::create()

[](#opção-b-criando-manualmente-no-schemacreate)

1. **Configure o Model:**

```
class Post extends Model
{
    use Searchable;

    protected static array $searchable = ['title', 'body'];
}
```

1. **Crie a Migration Manualmente:**

```
Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->text('body');
    $table->timestamps();

    // Adiciona índice de busca
    $table->searchableIndex(['title', 'body']);
});
```

1. **Execute a Migration:**

```
php artisan migrate
```

1. **Use a Busca:**

```
Post::search('termo')->get();
```

Diferenças entre Drivers
------------------------

[](#diferenças-entre-drivers)

### PostgreSQL (pg\_trgm)

[](#postgresql-pg_trgm-1)

- ✅ Suporta busca parcial (ex: "adm" encontra "admin")
- ✅ Suporta busca por similaridade (tolerante a erros de digitação)
- ✅ Funciona bem com termos curtos
- ⚠️ Requer extensão `pg_trgm`

### MySQL (FULLTEXT)

[](#mysql-fulltext-1)

- ✅ Busca nativa FULLTEXT do MySQL
- ✅ Ranking automático por relevância
- ✅ Suporta modos NATURAL LANGUAGE e BOOLEAN
- ⚠️ Requer palavras completas (não suporta busca parcial como pg\_trgm)
- ⚠️ Funciona melhor com palavras de 3+ caracteres
- ⚠️ Tem lista de stopwords que pode afetar resultados

Quando NÃO usar
---------------

[](#quando-não-usar)

Esta solução não é adequada para:

- ❌ Busca semântica / IA
- ❌ Busca em logs massivos
- ❌ Multitenancy extremo com shards

Nesses casos, Elasticsearch / Meilisearch são mais adequados.

Comparação com Outras Soluções
------------------------------

[](#comparação-com-outras-soluções)

SoluçãoQuando Usar**Este pacote (PostgreSQL pg\_trgm / MySQL FULLTEXT)**Busca parcial (PostgreSQL), autocomplete, simplicidade, zero infra extra**PostgreSQL FTS (tsvector)**Busca por palavras completas, stemming, múltiplos idiomas**Meilisearch / Elasticsearch**Busca semântica, autocomplete avançado, escalabilidade extremaTroubleshooting
---------------

[](#troubleshooting)

### Erro: "function similarity does not exist"

[](#erro-function-similarity-does-not-exist)

A extensão `pg_trgm` não está habilitada. O pacote tenta criá-la automaticamente, mas se falhar, execute manualmente:

```
CREATE EXTENSION IF NOT EXISTS pg_trgm;
```

### Erro: "Não foi possível encontrar os campos pesquisáveis"

[](#erro-não-foi-possível-encontrar-os-campos-pesquisáveis)

A migration não conseguiu encontrar a model ou ler os campos. Verifique:

1. A model usa o trait `Searchable`?
2. A model define o array `$searchable`?
3. O namespace da model está em `App\Models\`, `App\` ou `App\Domains\*\Models\`?

**Solução:** Você pode passar os campos manualmente na migration usando uma das opções:

**Opção 1: Método customizado no Blueprint (Recomendado):**

```
Schema::table('posts', function (Blueprint $table) {
    $table->searchableIndex(['title', 'body']);
});
```

**Opção 2: Trait PostgresFullTextMigration:**

```
use LucasBritoWdt\LaravelDatabaseFts\Traits\PostgresFullTextMigration;

class AddSearchableIndexToPostsTable extends Migration
{
    use PostgresFullTextMigration;

    public function up(): void
    {
        Schema::table('posts', function (Blueprint $table) {
            $this->addSearchableIndex($table, ['title', 'body']);
        });
    }
}
```

**Opção 3: Helper na migration gerada pelo comando:**

```
// Só disponível em migrations geradas por: php artisan make:searchable Post
$this->createSearchableIndex('posts', ['title', 'body']);
```

### Performance lenta

[](#performance-lenta)

Certifique-se de que o índice foi criado:

```
\d+ posts  -- Lista índices da tabela
```

Verifique se o índice `*_search_trgm_idx` existe.

### Muitos/poucos resultados

[](#muitospoucos-resultados)

Ajuste o threshold de similaridade:

```
// Mais resultados
Post::search('termo', 0.1)->get();

// Menos resultados
Post::search('termo', 0.4)->get();
```

### Model não encontrada em estrutura customizada

[](#model-não-encontrada-em-estrutura-customizada)

Se sua model está em um namespace customizado que não é detectado automaticamente, você pode:

1. Passar o namespace completo no comando (se o Laravel suportar)
2. Ou criar a migration manualmente passando os campos

Contribuindo
------------

[](#contribuindo)

Contribuições são bem-vindas! Por favor, abra uma issue ou pull request.

Licença
-------

[](#licença)

MIT License. Veja o arquivo [LICENSE](LICENSE) para mais detalhes.

Referências
-----------

[](#referências)

Este pacote é baseado nas melhores práticas documentadas em:

- [PostgreSQL pg\_trgm Documentation](https://www.postgresql.org/docs/current/pgtrgm.html)
- [Laravel Documentation](https://laravel.com/docs)

###  Health Score

43

—

FairBetter than 91% of packages

Maintenance90

Actively maintained with recent releases

Popularity16

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity49

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~8 days

Total

11

Last Release

46d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/f7804accedd190804443c7150e8f0185530d3db578825b437e4e59010aae1d6c?d=identicon)[lucasbrito-wdt](/maintainers/lucasbrito-wdt)

---

Top Contributors

[![lucasbrito-wdt](https://avatars.githubusercontent.com/u/30214900?v=4)](https://github.com/lucasbrito-wdt "lucasbrito-wdt (9 commits)")

---

Tags

searchlaraveldatabasemysqlpostgresqlfulltextfull text searchftspg\_trgm

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/lucasbrito-wdt-laravel-database-fts/health.svg)

```
[![Health](https://phpackages.com/badges/lucasbrito-wdt-laravel-database-fts/health.svg)](https://phpackages.com/packages/lucasbrito-wdt-laravel-database-fts)
```

###  Alternatives

[tucker-eric/eloquentfilter

An Eloquent way to filter Eloquent Models

1.8k4.8M26](/packages/tucker-eric-eloquentfilter)[pmatseykanets/laravel-scout-postgres

PostgreSQL Full Text Search Driver for Laravel Scout

164213.7k2](/packages/pmatseykanets-laravel-scout-postgres)[toponepercent/baum

Baum is an implementation of the Nested Set pattern for Eloquent models.

3154.7k](/packages/toponepercent-baum)[devnoiseconsulting/laravel-scout-postgres-tsvector

PostgreSQL Full Text Search Driver for Laravel Scout

58110.1k](/packages/devnoiseconsulting-laravel-scout-postgres-tsvector)[baril/sqlout

MySQL fulltext driver for Laravel Scout.

4512.9k](/packages/baril-sqlout)[moharrum/laravel-adminer

Adminer database management tool for your Laravel application.

451.0k](/packages/moharrum-laravel-adminer)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
