Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Postgres Search Bundle Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

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:

  • The SQL layer fits a Laravel/PostgreSQL stack well. If we already run Postgres and search is "find matching rows, ranked," native FTS removes an entire Elasticsearch/Algolia/Meilisearch dependency, its sync pipeline, and its on-call surface. For catalogs in the 10⁴–10⁷ row range this is a defensible cost-avoidance decision.
  • The bundle layer does not fit at all. It is a Symfony bundle — DI extension, console command, Doctrine event subscribers, entity mapping. In Laravel none of that wiring boots. Consuming the package as a package means paying for a framework integration we cannot use.

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.

  • Provenance and support: 0 stars, 0 dependents, no visible CI, no release history beyond the synthetic 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.
  • Fork-of-a-fork lineage: adds a supply-chain question. Which upstream commit does 0.1 pin to, and does Shopsys intend to carry it forward? The description implies a single internal consumer.
  • Silent semantic drift: vectors go stale on bulk/queue writes, schema changes, and Postgres dictionary/config changes (unaccent, language config, pg_trgm interplay). These produce relevance regressions, not errors — the hardest class of bug to catch without a golden query set.
  • Durable schema coupling: adds columns and indexes to core tables — a migration we own forever, even if we drop the package.

Key questions before any commitment.

  1. Which upstream commit/tag does 0.1 correspond to, and is there a stated commitment to track upstream?
  2. Are there consumers beyond Shopsys' own monolith, and is there a test suite we can run?
  3. What PHP / Symfony / Doctrine DBAL / PostgreSQL versions are actually exercised (not just declared)?
  4. What is the supported story for vector freshness under bulk writes — triggers, generated columns, or listeners only?
  5. If the answer to (1) is "internal convenience," why depend on the fork at all rather than upstream via a VCS constraint or inline repository entry?

Integration Approach

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.

  • From 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.
  • From Elasticsearch/Algolia — do not cut over in one step. Dual-write for a release cycle, log query pairs, compare top-N overlap on a curated query set offline, then cut over per surface and keep the vendor as read fallback for one more cycle.

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.

  1. Benchmark gate. Seed a production-shaped dataset; measure p50/p95 latency and relevance against both the current implementation and the incumbent vendor. This decision gates the project; do not skip it.
  2. Abstract. Introduce SearchService and route existing search through it with the current implementation behind it. No behavior change.
  3. Schema + maintenance. Add the vector column and GIN index, backfill in batches, and choose trigger or generated-column maintenance over application listeners — bulk writes bypass model events.
  4. Shadow-run. Diff native FTS results against the incumbent on real traffic.
  5. Cut over behind a flag, surface by surface (search page, typeahead, admin).
  6. Re-evaluate third-party packages. Likely none still warranted.

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.

Operational Impact

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:

  • No faceting/aggregation engine, no typo tolerance, no synonyms-management UI, no learning-to-rank. If any of those are on the roadmap, native FTS runs out of road and the migration should be budgeted now.
  • Relevance tuning is limited to rank normalization flags, setweight A/B/C/D, and dictionary config.
  • GIN indexes are write-amplifying — heavy write workloads need autovacuum tuning and bloat monitoring.
  • Search shares a database with transactional load; there is no isolation between an expensive search query and checkout latency.

Failure modes to instrument.

  • Stale index — bulk/queue/raw writes skip listeners. Mitigate with trigger-based maintenance plus a periodic reconciliation job counting rows where the stored vector disagrees with a recomputed one.
  • Silent relevance regression — dictionary or unaccent config changes alter results with no error. Mitigate with a fixed golden query set asserted in CI.
  • Index bloat / autovacuum lag — monitor GIN index size and dead tuples; alert on sustained growth.
  • Query cost spikes — log slow search queries and cap result windows; a runaway tsquery should not take down checkout.
  • Extension availability drift — 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.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings