Rails Search with pg_search

A search bar is an especial 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 of fewer than 1000 records, but, as your database grows, ActiveRecord queries can get overly complex and make your app lag.

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

There are two basic search configurations with 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 as an example, and I am 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 set a conditional to determine if we are searching or not. 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 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.

Footnote

This has been fun. Leave a comment or send me a DM on Twitter.

Shameless Plug: If you work at a great company, and you are in the market for a Software Developer with a varied skill set and life experiences, send me a message on Twitter and check out my LinkedIn.