Searching made easy with pg_search gem in Ruby on Rails

This article will discuss how to make search functionality easier and faster in Ruby on Rails through a gem called pg_search. According to the official documentation:

pg_search builds ActiveRecord named scopes that take advantage of PostgreSQLs full-text search.

This gem supports two types of searches: multi-search and search scopes. Before looking more closely at each, first create an application and define the ActiveRecord classes.

1
2
3
4
5
6
rails new search-project
cd search-project

rails generate model User name
rails generate model Post title body:text
rails db:create db:migrate

Now add pg_search gem in Gemfile.

1
gem 'pg_search'

Run bundle install.

Multi-search

In the multi-search technique, one global index is created for all the active record classes. To implement it, first run a migration for multi-search to work.

1
2
rails g pg_search:migration:multisearch
rake db:migrate

Make sure your models look like this:

1
2
3
4
5
6
# app/models/user.rb

class User < ApplicationRecord
 include PgSearch::Model
 multisearchable against: :name
end
1
2
3
4
5
6
# app/models/post.rb

class Post < ApplicationRecord
 include PgSearch::Model
 multisearchable against: [:title, :body]
end

Now add some records to the database to test functionality.

1
2
3
4
5
6
7
8
9
rails console
User.create(name: "Johny Bravo")
Post.create(title: "Cartoon Characters", body: "This is a post about cartoon characters.")

PgSearch.multisearch("Johny")
=> #<ActiveRecord::Relation [#<PgSearch::Document id: 1, content: "Johny Bravo", searchable_type: "User", searchable_id: 1, created_at: "2020-05-08 21:39:51", updated_at: "2020-05-08 21:39:51">]> 

PgSearch.multisearch("cartoon")
=> #<ActiveRecord::Relation [#<PgSearch::Document id: 2, content: "Cartoon Characters This is a post about cartoon ch...", searchable_type: "Post", searchable_id: 1, created_at: "2020-05-08 21:40:39", updated_at: "2020-05-08 21:40:39">]> 

That demonstrates multi-search, now onto the next technique.

Search Scopes

This is an advanced search technique used for a single ActiveRecord class and can be used for autocomplete and filtering results. To implement it, first make a few changes to the models.

1
2
3
4
5
6
7
# app/models/user.rb

class User < ApplicationRecord
 include PgSearch::Model

 pg_search_scope :search_by_name, against: :name
end
1
2
3
4
5
6
7
8
# app/models/post.rb

class Post < ApplicationRecord
 include PgSearch::Model

 pg_search_scope :search_by_title, against: :title
 pg_search_scope :search_by_body, against: :body
end

Now test functionality with records already created.

1
2
3
4
5
6
7
8
User.search_by_name("Johny")
=> #<ActiveRecord::Relation [#<User id: 1, name: "Johny Bravo", created_at: "2020-05-08 21:39:50", updated_at: "2020-05-08 21:39:50">]>

Post.search_by_title("Cartoon")
=> #<ActiveRecord::Relation [#<Post id: 1, title: "Cartoon Characters", body: "This is a post about cartoon characters.", created_at: "2020-05-08 21:40:39", updated_at: "2020-05-08 21:40:39">]> 

Post.search_by_body("characters")
=> #<ActiveRecord::Relation [#<Post id: 1, title: "Cartoon Characters", body: "This is a post about cartoon characters.", created_at: "2020-05-08 21:40:39", updated_at: "2020-05-08 21:40:39">]> 

Now you know the basics of pg_search!