Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
208: Full-Text Search with pg_search
Look, we've all been there. You're building a feature, and the requirement is simple: "The user needs to be able to search for something." Your first instinct is probably to throw a WHERE title LIKE '%query%' at the database and call it a day. For a few hundred records, that's fine. But as soon as you're dealing with thousands of rows or multiple columns, that approach falls apart. It's slow, it doesn't handle stemming (searching for "run" won't find "running"), and it's frankly a blunt instrument.
I've got a project here—a recipe manager—and I want to be able to search for recipes by their title, their description, and the ingredients list. Let's see what happens when I try to do this the "naive" way first.
The frustration of the manual query
# In my Recipe controller
def index
@recipes = Recipe.where("title ILIKE ? OR description ILIKE ?", "%#{params[:q]}%", "%#{params[:q]}%")
end
It works, technically. But it's clunky. If I add a third column, the query gets longer. If I want to prioritize results where the keyword is in the title over the description, I'm basically out of luck without writing some nightmare of a SQL CASE statement. Plus, PostgreSQL is just sitting there with powerful full-text search (FTS) capabilities that I'm completely ignoring.
Enter pg_search. It's a wrapper that makes Postgres's FTS features feel like native Ruby. Let's pull it in and see if we can clean this up.
Setting up the basic scope
After adding the gem and running the bundle, I'll move the search logic out of the controller and into the model where it belongs. I'll use pg_search_scope because it gives me a reusable scope I can chain with other queries.
class Recipe < ApplicationRecord
include PgSearch::Model
pg_search_scope :search_by_text,
against: [:title, :description]
end
Now, in my controller, I can just do Recipe.search_by_text(params[:q]). This is already cleaner, but I noticed something during testing. If I search for "Chocolate Cake," and I have a recipe titled "Chocolate Cake" and another description that mentions "chocolate" and "cake" in two different sentences, they're both showing up. But they aren't being sorted by relevance. The "perfect match" isn't necessarily at the top.
Tuning the relevance with weights
This is where I usually start scratching my head until I remember that pg_search allows for weighting. I want a match in the title to be significantly more important than a match in the description. In Postgres, we use weights (A, B, C, D), where A is the highest.
Let's adjust the model to tell the database that the title is the priority:
class Recipe < ApplicationRecord
include PgSearch::Model
pg_search_scope :search_by_text,
against: {
title: 'A',
description: 'B'
}
end
Now, when I run a search for "Sourdough," the recipe actually titled "Sourdough Bread" jumps to the top, while the "Daily Bread" recipe that just mentions sourdough in the description drops down. That feels much more like a professional search experience.
Handling the 'No Results' awkwardness
I tried searching for "Zucchini" and got exactly what I expected: nothing. But as I keep playing with the search bar, I realize that the default behavior is very strict. If I typo "Zuccini", I get zero results. In a real app, users typo things constantly.
I'll add using: { tsearch: { prefix: true } } to the scope. This allows for prefix matching, so searching "Zucc" will find "Zucchini". It's a small tweak, but it makes the search feel "softer" and more forgiving.
pg_search_scope :search_by_text,
against: {
title: 'A',
description: 'B'
},
using: {
tsearch: { prefix: true }
}
end
One final thought: keep in mind that pg_search is leaning heavily on PostgreSQL. If you're using SQLite for development and Postgres for production, this code will blow up in your dev environment. I always make sure my local environment mirrors production exactly when I'm touching FTS features, otherwise, you'll spend an hour debugging a "method not found" error that only exists on your laptop.
📋 Practical Task
Building a Weighted Book Catalog Search
You are tasked with adding a search feature to a Book model. The Book model has three attributes: title, author, and summary.
- Install and integrate the
pg_searchgem. - Create a
pg_search_scopenamedsearch_books. - Configure the weights so that a match in the
titleis the most important (Weight A), theauthoris second (Weight B), and thesummaryis the least important (Weight C). - Enable
prefixmatching so that searching for "Hem" returns "Hemingway". - Test your scope in the Rails console by searching for a term that appears in both a title and a summary to verify the title result ranks higher.
There are no comments for now.