finds.dev← search

// the find

Casecommons/pg_search

★ 1,570 · Ruby · MIT · updated Jun 2026

pg_search builds ActiveRecord named scopes that take advantage of PostgreSQL’s full text search

pg_search wraps PostgreSQL's native full-text search, trigram, and Double Metaphone into ActiveRecord named scopes. It's for Rails apps that want to avoid Elasticsearch/Solr but still need decent search. If you're already on Postgres and don't need real-time distributed search, this is the sensible choice.

1. Three search modes in one gem — tsearch (fast, stemming, weighted columns), trigram (typo-tolerant, no extra infrastructure), and dmetaphone (soundalike matching) — and you can combine them with a custom ranking expression like ':dmetaphone + (0.25 * :trigram)'. 2. tsvector column support is a real performance path: precompute the search vector via a Postgres trigger, point the scope at it, and you skip the runtime expression cost entirely. 3. The :highlight option gives you ts_headline integration out of the box, which is the kind of thing most projects hand-roll badly. 4. Multi-search across model types is genuinely useful and correctly returns an ActiveRecord::Relation you can chain — not some custom result object you have to unwrap.

1. Searching through associations is explicitly documented as unindexable — it always does a JOIN that can't use a column index. For anything beyond a prototype this will hurt. 2. The :if conditional on multisearchable runs in an after_save hook, so time-based conditions (scheduled publishing) silently produce stale index documents until the record is touched again. The gem documents this but doesn't offer a better escape hatch. 3. No vector/semantic search support — this is purely lexical. As soon as users want 'find things similar to X' rather than 'find things containing X', you're off the map. 4. The custom :ranked_by string is just interpolated SQL, and the docs hint you can mix in raw column references like 'books.num_pages'. That's a footgun: nothing stops you from accidentally making a scope that's trivially injectable if you ever expose that option to user input.

View on GitHub → Homepage ↗

// want more like this?

We dig through GitHub every week and send a few repos picked for what you actually care about — each with an honest take like this one.

Get finds in your inbox → Search again →