ddmaster/postgre-search-bundle
Installation:
composer require ddmaster/postgre-search-bundle:dev-master
Add to AppKernel.php:
new Ddmaster\PostgreSearchBundle\PostgreSearchBundle(),
Configure DBAL and ORM in config.yml:
doctrine:
dbal:
types:
tsvector: Ddmaster\PostgreSearchBundle\Dbal\TsvectorType
mapping_types:
tsvector: tsvector
orm:
entity_managers:
default:
dql:
string_functions:
TSQUERY: Ddmaster\PostgreSearchBundle\DQL\TsqueryFunction
TSRANK: Ddmaster\PostgreSearchBundle\DQL\TsrankFunction
First Use Case:
Add a tsvector field to an entity (e.g., Article):
use Doctrine\ORM\Mapping as ORM;
/**
Query with full-text search:
```php
$query = $entityManager->createQuery(
'SELECT a FROM AppBundle:Article a
WHERE a.searchContent @@ :query'
)->setParameter('query', $entityManager->getConnection()->getDatabasePlatform()->getTsQueryParameter('search term'));
Indexing Data:
tsvector columns to store pre-computed search vectors (e.g., via lifecycle callbacks or cron jobs).searchContent in prePersist/preUpdate:
public function prePersist(LifecycleEventArgs $args) {
$entity = $args->getEntity();
$entity->setSearchContent($this->generateTsvector($entity));
}
Querying:
@@ operator for exact matches:
$query = $em->createQuery('SELECT a FROM AppBundle:Article a WHERE a.searchContent @@ :query')
->setParameter('query', $platform->getTsQueryParameter('laravel symfony'));
ts_rank for relevance scoring:
$query = $em->createQuery('
SELECT a, TS_RANK(a.searchContent, :query) as rank
FROM AppBundle:Article a
WHERE a.searchContent @@ :query
ORDER BY rank DESC
');
Dynamic Indexing:
tsvector columns on-the-fly (e.g., for partial updates):
$entity->setSearchContent($em->getConnection()->getDatabasePlatform()->getTsvectorExpression(
['title', 'content'], ['title', 'content']
));
text field for queries:
$builder->add('query', TextType::class, [
'attr' => ['placeholder' => 'Search...']
]);
$results = $query->getResult();
return $this->json(array_map(fn($item) => [
'id' => $item[0]->getId(),
'title' => $item[0]->getTitle(),
'rank' => $item[1]
], $results));
StashBundle) to avoid recomputing tsvector values.Outdated Bundle:
doctrine/dbal, doctrine/orm) manually.Case Sensitivity:
tsvector is case-sensitive by default. Use to_tsvector('english', ...) for case-insensitive searches or configure a custom dictionary:
$platform->getTsvectorExpression(['title'], ['title'], 'english');
Performance:
tsvector in queries: Store pre-computed values in the DB.tsvector columns:
CREATE INDEX idx_article_search_content ON article USING gin(search_content);
DQL Function Conflicts:
TSQUERY/TSRANK clash with existing functions, alias them:
string_functions:
TSQUERY: Ddmaster\PostgreSearchBundle\DQL\TsqueryFunction
TSRANK_CUSTOM: Ddmaster\PostgreSearchBundle\DQL\TsrankFunction
Then use TSRANK_CUSTOM in queries.log_min_messages = LOG in postgresql.conf to debug tsvector operations.EXPLAIN ANALYZE in PostgreSQL to verify query plans:
EXPLAIN ANALYZE SELECT * FROM article WHERE search_content @@ to_tsquery('search term');
$conn->executeQuery('SELECT * FROM article WHERE search_content @@ :query', ['query' => 'search term']);
Custom Tokenization:
TsvectorType to support custom tokenizers (e.g., for URLs or special formats):
class CustomTsvectorType extends TsvectorType {
public function convertToDatabaseValueSql($value, AbstractPlatform $platform) {
return $platform->getTsvectorExpression(
$value, ['title', 'content'], 'custom_dictionary'
);
}
}
Register in config.yml:
doctrine:
dbal:
types:
tsvector: AppBundle\DBAL\CustomTsvectorType
Add More DQL Functions:
plainto_tsquery):
// src/DQL/PlainToTsqueryFunction.php
class PlainToTsqueryFunction extends AbstractFunctionNode {
public function parse(\Doctrine\ORM\Query\Parser $parser) { /* ... */ }
}
Register in config.yml:
doctrine:
orm:
entity_managers:
default:
dql:
string_functions:
PLAIN_TO_TSQUERY: AppBundle\DQL\PlainToTsqueryFunction
Event Listeners:
prePersist/preUpdate to auto-generate tsvector:
$eventManager->addEventListener(
KernelEvents::CONTROLLER,
function (ControllerEvent $event) {
$entity = $event->getController()->getArgument('article');
if ($entity instanceof Article) {
$entity->setSearchContent($this->generateTsvector($entity));
}
}
);
How can I help you explore Laravel packages today?