Imagine that you are building a great Rails application which requires a search function. However, you don’t want to use a LIKE query since it’s too basic to support your requirements. You also decide against using Solr or ElasticSearch since they are too complicated to get started. Well, great news for you: if you use Postgresql, you will have a free built-in full-text search feature! However, the downside of using this full-text search feature is its strange syntax. Luckily, there’s a Ruby gem to resolve it: pg_search. In this blog post, we will explore that gem.
In order to follow this post, you will need to be at least familiar with Rails. The examples and commands provided here are also based on MacOS. If you use Linux, please adapt them accordingly. Your machine should have Git, Ruby, Rails and Postgresql as well as a functional terminal.
1. The sample project
We are looking to build a library system which allows users to search for books. The query string needs to be matched with the book’s title, description and authors’ name. Below are the entities of the project:
In order to highlight the feature of pg_search, we won’t be implementing any controllers or UI. We will instead focus on the models only. You can check the result via the `rails console`.
The initial code of this project can be found here. Please clone the project, checkout the branch “initial” and run:
bundle install
You might want to open config/database.yml to edit the database settings:
default: &default adapter: postgresql pool: 5 timeout: 5000 username: << YOUR POSTGRESQL USERNAME HERE >> password: << YOUR POSTGRESQL PASSWORD HERE >>
After that, reset the database:
rake db:reset
We are now good to go.
2. Our star: pg_search
First of all, add pg_search to your project by simply add this line to your Gemfile:
gem 'pg_search'
After running “bundle install” to install it, open the model which you need to add your search function to, in this case: Book. Next, add the following lines to include PgSearch module and search scope to the model:
class Book > ApplicationRecord include PgSearch pg_search_scope :search, against: [:title, :description] # The rest is omitted end
Here, we use pg_search_scope to define a search scope, followed by the scope name and the column list for search under the key :against. You can add as many search scopes as you want within a model and name the scope arbitrarily.
With the above code, we can then search by book title and description in a succinct way. Let’s try searching with “rails console” and check the result with awesome_print:
ap Book.search('Harry')
Great, we have found 3 books using ‘Harry’ keyword. Now, what if we want to search for the author’s name? We can do that by easily modifying the search scope as below:
pg_search_scope :search, against: [:title, :description], associated_against: { author: [ :full_name, :nationality ] }
Here, we add the key associated_against, and then declare the associated models with desired search columns under the key. Let’s test again and don’t forget to run ‘reload!’ to update the changes made to the model in your rails console:
ap Book.search('Rowling')
Great, we have found 3 results too! Don’t we feel like we are the best developer in the world now? Wait a moment, how can I search with an incomplete word, for example: ‘Rowl’ or ‘Har’? Well, it’s a piece of cake:
pg_search_scope :search, against: [:title, :description], associated_against: { author: [ :full_name, :nationality ] }, using: { tsearch: { prefix: true } }
Try verifying again and you will be surprised with the same result:
ap Book.search('Rowl')
In the above modification, we added a new option “using” which enables tsearch with prefix. You might want to explore more options for the search scope on the Github repo of pg_search.. Besides tsearch, there are 2 more search methods called trigram and dmetaphone which require some extension to Postgresql which you can further explore.
This blog post is just a “get started with pg_search” guide. The gem itself has a lot of other advanced features. The full-text search feature of Postgresql is amazingly powerful and totally deserves further research.
Happy coding, guys!
3. References
- pg_search repository on Github: https://github.com/Casecommons/pg_search
- Postgresql full-text search in depth: https://www.compose.com/articles/mastering-postgresql-tools-full-text-search-and-phrase-search/
Brought to you by RobustTechHouse