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 PostgreSQL’s 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!