shopsys/postgres-search-bundle
Shopsys Postgres Search Bundle brings native full-text search to Shopsys projects using PostgreSQL, letting you index and query products quickly without running a separate search engine.
What this package is, in TPM terms. A thin Symfony bundle that exposes PostgreSQL's native full-text search (tsvector/tsquery, GIN indexes, ts_rank_cd) behind Doctrine DBAL. The fork's own description says the only delta from intaro/postgres-search-bundle is a synthetic 0.1 tag added "for easier version management" — an administrative fork to give Composer a resolvable constraint, not a functional divergence. For a TPM the important framing is that this is a technique packaged as a dependency: the valuable, reusable part is a few hundred lines of SQL and query-building logic; everything else is Symfony wiring.
Architecture fit. Split the judgment:
Integration feasibility. Low as a dependency, high as a transplant. The reusable core — to_tsvector/websearch_to_tsquery construction, ts_rank_cd ordering, a maintained search_vector column, a GIN index — is small enough to re-implement and own. What we cannot reuse: bundle plumbing, Doctrine entity mapping, and listener-based synchronization (Eloquent's model events differ, and in both ecosystems bulk writes, upsert, and raw statements bypass model events — the inherited assumption is the weakest part of the design).
Technical risk.
0.1. The opportunity score (47.56) reads as keyword-derived and is not corroborated by any adoption evidence — treat it as a false positive.0.1 pin to, and does Shopsys intend to carry it forward? The description implies a single internal consumer.unaccent, language config, pg_trgm interplay). These produce relevance regressions, not errors — the hardest class of bug to catch without a golden query set.Key questions before any commitment.
0.1 correspond to, and is there a stated commitment to track upstream?Stack fit. Treat this as a technique to adopt, not a dependency to install. Target stack is Laravel + Eloquent + PostgreSQL; we consume the idea (native FTS + GIN) through Laravel's query builder or a light DBAL layer, not through the Symfony bundle. The integration point is a framework-neutral SearchService interface (search(string $q, array $filters): Collection) that everything above it codes against — which also keeps the Elasticsearch/Meilisearch exit door open.
Migration path.
LIKE/ILIKE — low risk, high payoff. Add a search_vector column, backfill, add a GIN index CONCURRENTLY, swap the query builder behind the existing search service. Trivially reversible behind a flag.Compatibility checks before writing code. Confirm the real PHP constraint, Doctrine DBAL major, and PostgreSQL floor. websearch_to_tsquery requires PG 11+; ts_rank_cd normalization flags and unaccent availability vary by deployment; managed Postgres (RDS, Cloud SQL, Supabase) may restrict extension creation — verify unaccent/pg_trgm can be enabled in every environment, including the ones we do not control.
Sequencing.
SearchService and route existing search through it with the current implementation behind it. No behavior change.Explicitly rejected path. Adding shopsys/postgres-search-bundle to composer.json and attempting to boot a Symfony bundle in Laravel. The DI/console/Doctrine-event surface is dead weight, and we would be pinned to a 0.1 fork with no upgrade story for code we can own outright.
Maintenance. Owning the SQL is cheaper than owning this dependency. The maintenance logic is a trigger or generated column plus one index; the query builder is a few dozen lines. Depending on an unmaintained administrative fork means we inherit the obligation to patch it anyway, plus the overhead of tracking a fork-of-a-fork. No meaningful security surface (it is SQL generation), but also no upstream fixing it if Postgres or DBAL shifts underneath.
Support. No community, no substantive issue history, no documented upgrade path. Escalation for defects is effectively "read the source." That is acceptable at this code size — but it must be an accepted cost, not a surprise. Recommend naming an internal owner for the search subsystem before commitment, with a documented fork/diff/vendor policy if the package is ever pulled in.
Scaling. Native FTS scales vertically and predictably: single-digit-millisecond lookups on GIN indexes into the low millions of rows, cost driven by index size and shared_buffers. Limits to plan for and state up front:
setweight A/B/C/D, and dictionary config.Failure modes to instrument.
unaccent config changes alter results with no error. Mitigate with a fixed golden query set asserted in CI.tsquery should not take down checkout.unaccent/pg_trgm present in staging but absent in a new managed-Postgres environment; assert at deploy time.Ramp-up. Low for the SQL, moderate for relevance judgment. Any backend engineer can produce a tsvector query in a day; knowing whether a ranking change improved search is a product-owner skill that needs a curated query set and a relevance-review habit. Budget a short internal spike (1–2 days) to prototype on real data before committing scope, and assign a named owner for the search subsystem — the absence of upstream support makes internal ownership a hard prerequisite, not a nice-to-have.
How can I help you explore Laravel packages today?