Rails Search with pg_search

A search bar is a special feature as a web application grows. There are several ways to implement this feature in a Rails application. This article will explore one of these by searching a Postgres database with the pg_search gem.

Install pg_search

In a smaller application, you can query the database using ActiveRecord, a simple way to prototype search and filtering. It allows you to quickly find related records for databases with fewer than 1000 records, but as your database grows, ActiveRecord queries can become overly complex and slow your app.

Install the gem as usual. Add gem pg_search to your Gemfile and bundle install.

There are two basic search configurations:pg_search a Single Model search scope or a multi-model configuration. In my case, I am only using the Single Model configuration, but you can read more about multi-search in the documentation.

I am using my Rails Your Congress app as an example and setting up a search bar on the Senator's search page.

Model

To start using pg_search, you need to include PgSearch in your model and set up the pg_search_scope:

class Senator < ApplicationRecord
include PgSearch

scope :sorted, ->{ order(last_name: :asc) }

pg_search_scope :global_search,
against: [:first_name, :last_name, :state],
using: { tsearch: { prefix: true } }
end

In this example, I am searching from the Senator resource, by first_name, last_name, and state.

Controller

Since I am providing a search on the index page, the index method in the Senators controller:

class SenatorsController < ApplicationController

def index
if params[:query].present?
@senator_search = Senator.global_search(params[:query])
@senators = @senator_search.paginate(page: params[:page], per_page: 20)
else
@senators = Senator.paginate(page: params[:page], per_page: 20)
end

respond_to do |format|
format.html
format.json { render json: { senators: @senators } }
end
end

In this method, we use a conditional statement to determine whether we are searching. If we are searching, we are setting two instance variables. The variable @senators is used in the views, and I had to set up two variables to get will_paginate them to play nicely with the pg_search results.

Improvements

With each search, the entire page re-renders. So next week, we will look at how I set up StimulusJS to make the Senator container reactive.