pg_search picks one of three PostgreSQL search features per model and per scope
pg_search builds ActiveRecord named scopes that take advantage of PostgreSQL’s full text search
At a glance
- What is it?
- pg_search adds search scopes to Active Record models, with multi-search building one global index across classes through a pg_search_documents table. You choose full text search, double metaphone soundalikes or trigrams per scope, and combine features when one is not enough.
- Who is it for?
- Adopt pg_search if your search needs can be answered inside PostgreSQL and you want them expressed as model scopes instead of a separate search service. Do not adopt it expecting cross-field relevance tuning or a UI, because it is a query layer.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 22 days ago.
- What is it written in?
- Mainly Ruby, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.
Star history
- Casecommons/pg_search1,591 ★ · +2
| Repository | 2026-10-19 | 2026-11-07 |
|---|---|---|
| Casecommons/pg_search | 1.6k | 1.6k |
2 Stars in the last 19 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Two techniques, and picking the wrong one costs you the wrong index
pg_search supports two different techniques, and the README is clear that they are for different jobs.
Multi-search mixes records of many different Active Record classes into one global search index across the application. For a site with a generic search page covering several models, this is the technique the README recommends. It works through a dedicated table, and adding a model to it is a one-line declaration.
class EpicPoem < ActiveRecord::Base
include PgSearch::Model
multisearchable against: [:title, :author]
end
class Flower < ActiveRecord::Base
include PgSearch::Model
multisearchable against: :color
endSearch scopes are the other technique: advanced searching against a single Active Record class, which the README points at autocompleters and filtering a list of items in a faceted search.
So the decision is about where the results come from. If the answer needs rows from three tables, multi-search is the shape you want and a per-model scope will not give it to you. If the answer is always within one model and you want to keep composing with other scopes, search scopes fit.
Multi-search needs setup before use: a generated migration creates the pg_search_documents table, run with rails g pg_search:migration:multisearch followed by rake db:migrate. Models that already have records need a reindex to backfill, which is the step people forget on an established database.
The if: option is evaluated in an after_save hook, and that changes its meaning
A model can restrict which records enter the index by passing an if: option, either a method name or a Proc that is called on the record. The reason this needs care is documented at length, and the explanation is the most useful thing in the README.
The Proc or method name is called in an after_save hook. That has a concrete consequence: if the decision depends on a value that changes with time rather than on the save, the record's presence in the index is decided at save time and then frozen until the record is touched again.
class AntipatternExample
include PgSearch::Model
multisearchable against: [:contents], if: :published?
def published?
published_at < Time.now
end
endThe example walks through it. A record is created with published_at ten minutes in the future. published? is false, and a global search finds nothing. Twenty minutes later published? is true, and the search still finds nothing, because no save has happened since. The record only appears after another save, which re-fires the callback and re-evaluates the condition.
For the cases where that is what you want, the option is fine. `if::available_in_red?` on a Convertible and a lambda checking model_year greater than 1970 on a Jalopy are both conditions that are true or false when the record is written.
tsearch, dmetaphone and trigram are three different answers
The feature list is the substance of this gem, because each search feature is a different PostgreSQL mechanism answering a different question.
`:tsearch` is full text search, and it carries the most options. Weighting lets you say which column matters more. `:prefix` enables prefix matching, documented as PostgreSQL 8.4 and newer only. `:negation` excludes terms. `:dictionary` selects a text search configuration, and `:normalization` controls it. `:any_word` relaxes the requirement that all terms match. `:sort_only` restricts a scope to ranking without matching. `:highlight` marks matched terms in results.
`:dmetaphone` is double metaphone soundalike search, which is how a query for a name typed approximately still finds the record. `:trigram` is trigram search, similarity on character sequences rather than words, with a `:threshold` for how close a match must be and a `:word_similarity` mode.
The README documents a specific pattern for using more than one at a time: limiting fields when combining features. That section exists because combining, say, a trigram scope with a tsearch scope on the same query otherwise returns a cross product of the two conditions rather than a sensible union, and the field limiting is what keeps the combination meaningful.
Also covered: ignoring accent marks, using tsvector columns directly when you want the vector stored and combined yourself, and configuring ranking with `:ranked_by`, breaking ties with `:order_within_rank`, or reading a record's rank as a Float through PgSearch#pg_search_rank.
Requirements are current, and some features need extensions
The stated requirements are Ruby 3.3 or newer, Active Record 8.0 or newer, and PostgreSQL 9.2 or newer. PostgreSQL extensions are listed as required for certain features, with a wiki page on installing them.
That combination is worth pausing on. The maintenance record supports that reading: the last push to the repository was on 2026-10-14 and the project is being kept current rather than held stable.
Installation is a gem install or a Gemfile line.
gem 'pg_search'Outside Rails, you may want to load the pg_search rake tasks into your Rakefile with load "pg_search/tasks.rb". Rails applications do not need this because they pick the tasks up through a Railtie, which is the kind of small difference that costs an afternoon if you assume symmetry.
There are no GitHub releases for this repository, so versions come from RubyGems and the CHANGELOG.md at the root. The licence is MIT, and the project also carries a standard.yml for the standardrb linter and an.rspec file for RSpec.
Where pg_search is the wrong tool
The first boundary is relevance across heterogeneous content. Multi-search puts every model into one index with one set of weights, so a match in a title and a match in a paragraph are both just text in the same document. If your users need field-aware relevance across several models, this does not give it to you and no configuration in the documented list adds it.
The operational cost is the index. Multi-search means a table that grows with your data and a rebuild path when you change a model's against: list, so adding a field is a reindex, not a migration. The README documents a rebuild task for a given class, which tells you the operation is supported and also that it is expected to be run.
The alternative approaches differ in what you give up. Hand-written to_tsquery SQL through Active Record gives you full control with no dependency, and gives up the scopes, the weighting helpers and the document table management that this gem provides.
Editorial conclusion
Adopt pg_search if your search needs can be answered inside PostgreSQL and you want them expressed as model scopes instead of a separate search service. Do not adopt it expecting cross-field relevance tuning or a UI, because it is a query layer. Before starting, confirm PostgreSQL 9.2 or newer, Ruby 3.3 or newer and Active Record 8.0 or newer, and plan the multisearch migration and reindex step before you have data in the tables.
Frequently asked questions
How do I add search to an Active Record model with pg_search?
Include PgSearch::Model in the class. For a global index across models, also call multisearchable with the attributes to index, then generate and run the migration with rails g pg_search:migration:multisearch and rake db:migrate.
What is the difference between multi-search and search scopes?
Multi-search mixes records of many different Active Record classes into one global index through the pg_search_documents table, which suits a site-wide search page. Search scopes search a single Active Record class, which suits autocompleters and faceted filtering.
What does the if: option in multisearchable do?
It takes a method name or Proc called on the record to decide whether it belongs in the index. It is evaluated in an after_save hook, so a condition that depends on the current time does not re-evaluate until the record is saved again.
Which search features does pg_search support?
Full text search as :tsearch with weighting, prefix, negation, dictionary, normalization, any_word, sort_only and highlight; soundalike search as :dmetaphone using double metaphone; and :trigram with threshold and word_similarity. The README also documents how to limit fields when combining features.
What versions does pg_search need?
Ruby 3.3 or newer, Active Record 8.0 or newer and PostgreSQL 9.2 or newer, with PostgreSQL extensions required for some features. Outside Rails you also load the rake tasks with load "pg_search/tasks.rb".
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/casecommons-pg-search)Star history chart for your README
[](https://hysenlabs.com/projects/casecommons-pg-search)Was this useful?