autopage / pg-search
Busca Textual com PostgreSQL no CakePHP
Installs: 525
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 2
Forks: 0
Open Issues: 1
Type:cakephp-plugin
Requires
- php: >=7.2
- cakephp/cakephp: ^4.2.2
Requires (Dev)
- cakephp/cakephp-codesniffer: ^4.0
- phpstan/phpstan: ^0.12.34
- phpunit/phpunit: ^8.5
README
Adicione suporte a Full Text Search do Postgres em sua aplicação CakePHP.
Requisitos
- PHP 7.2+
- CakePHP 4.2.2+
- PostgreSQL 9.6+
Instalar
Inclua o plugin como dependência
composer require autopage/pg-search
Uso
Para ter todos os recursos disponíveis, você deve configurar sua aplicação para usar o Driver
fornecido aqui na conexão com o banco de dados. Ele habilitará o uso do TableSchema
e PostgresSchemaDialect
incluidos no plugin que estendem as versões padrão do CakePHP para implementar o tipo de coluna tsvector
e aos índices do tipo gin
e gist
.
Configuração
No seu app_local.php
:
// ... 'Datasources' => [ 'default' => [ 'className' => \Cake\Database\Connection::class, 'driver' => \Autopage\PgSearch\Database\Driver\Postgres::class, // O restante da sua configuração vem normalmente // ... ], // ...
Outra configuração importante, mas opcional, é definir qual configuração de busca o PostgreSQL deve usar na hora de indexar ou buscar um campo do tipo tsvector
.
Você pode configurar ela com a chave PgSearch.config_name
. Supondo que você criou no seu banco de dados uma configuração de nome portuguese, o seu app_local.php
ficaria:
// ... 'PgSearch' => [ 'config_name' => 'portuguese', ], // ...
SearchableBehavior
Associe as tabelas que deseja tornar pesquisável ao behavior, desta forma, sempre que um registro for criado/editado/excluído, as informações serão propagadas para a tabela de busca.
/** * Método de inicialização da Table * * @param array $config The configuration for the Table. * @return void */ public function initialize(array $config): void { parent::initialize($config); $this->addBehavior('Searchable', [ 'foreign_key' => 'origem_id', 'mapper' => function ($entidade) { return [ 'origem_id' => $entidade->id, 'conteudo' => $entidade->descricao, ]; }, ]); }
Configurações disponíveis
- target:
string
. Nome do repositório (Table
) que deve ser usada para persistir os registros de forma pesquisável. Por padrão usa umaTable
com nome igual a que está vinculada aoBehavior
, adicionado o sufixo 'Searches'. Exemplo:Posts
->PostsSearches
. - foreign_key:
string
. Nome da coluna em target que referencia o registro original. Por padrão usa o nome da tabela no singular adicionado o sufixo '_id'. Exemplo:Posts
->post_id
- mapper:
callable
. Método/função que converte uma entidade do repositório original para uma de target. Por padrão, ele copia todos os campos da entidade original na entidade nova e associa a chave estrangeira da nova a chave primária da original. - doIndex:
bool|callable
. Se deve ou não indexar o registro. Permite um controle individual. Por padrão o valor étrue
. - doDeindex:
bool|callable
. Se deve ou não desindexar o registro. Permite um controle individual e suporte a remoção lógica (soft-delete). Por padrão o valor éfalse
.
Finder FTS
O behavior disponibiliza o finder de nome fts
. Como todo finder, ele recebe uma query e também retorna uma query. Dessa forma, você pode usar ele para preparar uma query antes ou depois de chamar ele, estendendo as condições e qualquer outra operação possível em uma query.
Os parâmetros especiais desse finder são:
- field:
string
(obrigatório). Nome do campo do tipo tsvector onde a busca será feita - value:
string
(obrigatório). Valor que deve ser comparado com o campo - highlight:
boolean
. Flag indicando se deve ou não incluir destaque nos termos encontrados. Por padrão é desativado. - highlight_field:
string
(obrigatório apenas se highlight fortrue
). Nome do campo textual onde o highlight será aplicado - exact:
boolean
. Se a comparação será do tipo exata ou aproximada. Por padrão é aproximada (false
). - ts_function:
string
. Caso seja necessário utilizar uma função diferente da aproximada ou exata para parseamento. - configuration:
string
. Nome da configuração de busca usada na comparação. Por padrão, usa a mesma definida emPgSearch.config_name
. - ranked:
boolean
. Flag indicando se a query deve ser ordenada por score.
FAQ
Preciso mesmo usar o driver do plugin na minha aplicação?
Não, não precisa. Mas ao não usar, você terá de dizer em cada Table
que possui coluna tsvector
qual é essa coluna.
Nos seus testes unitários, também não será possível criar Fixture
com esse tipo de coluna ou um dos índices gin
e gist
.
Em resumo: não precisa, mas deveria.
Por que usar uma tabela separada para indexar?
É uma decisão pessoal, tirada após trabalhar com sistemas que migram de backend de busca/indexação ao longo do tempo. Separar as tabelas te fornece mais flexibilidade. E o tradeoff é o uso um pouco maior de disco.
Essa tabela indexada pode ser uma versão desnormalizada de outra, permitindo que você faça várias consultas que exigiriam joins
e subqueries
de outra maneira. Em um segundo momento, quando passar a ter bilhões de registros, você pode querer desacoplar essa tabela do banco principal e passar para uma outra instância, ou mesmo para um serviço especializado como Elasticsearch, Solr e Sphinx.
Entendi, mas posso usar a mesma tabela do registro principal?
Até pode, mas você ganharia apenas o finder
como vantagem - os recursos de sincronização ficam desativados.