autopage / pg-search
Busca Textual com PostgreSQL no CakePHP
Installs: 563
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 1
Forks: 0
Open Issues: 1
Type:cakephp-plugin
pkg:composer/autopage/pg-search
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 umaTablecom 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.