Skip to content

Commit 3180b93

Browse files
committed
Search performance optimization
* Add basic trigram indexes * Change search query to use unions for better index use * Remove duplicate query execution in search result display
1 parent e2db193 commit 3180b93

5 files changed

Lines changed: 35 additions & 4 deletions

File tree

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ gem "rails", "~> 8.0.2"
66
gem "propshaft"
77
# Use postgresql as the database for Active Record
88
gem "pg", "~> 1.1"
9+
# PostgreSQL-specific ActiveRecord extensions (UNION, CTE, etc.)
10+
gem "active_record_extended"
911
# Use the Puma web server [https://github.com/puma/puma]
1012
gem "puma", ">= 5.0"
1113
# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]

Gemfile.lock

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ GEM
4444
erubi (~> 1.11)
4545
rails-dom-testing (~> 2.2)
4646
rails-html-sanitizer (~> 1.6)
47+
active_record_extended (3.4.0)
48+
activerecord (>= 5.2, < 8.1)
49+
pg (< 3.0)
4750
activejob (8.0.4)
4851
activesupport (= 8.0.4)
4952
globalid (>= 0.3.6)
@@ -472,6 +475,7 @@ PLATFORMS
472475
x86_64-linux-musl
473476

474477
DEPENDENCIES
478+
active_record_extended
475479
bcrypt (~> 3.1)
476480
bootsnap
477481
brakeman

app/controllers/topics_controller.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,13 @@ def search
8282
@search_query = params[:q].to_s.strip
8383

8484
base_query = if @search_query.present?
85-
Topic.joins(:messages)
86-
.where("topics.title ILIKE ? OR messages.body ILIKE ?", "%#{@search_query}%", "%#{@search_query}%")
87-
.distinct
85+
search_pattern = "%#{ActiveRecord::Base.sanitize_sql_like(@search_query)}%"
86+
87+
title_sql = Topic.select(:id).where("title ILIKE ?", search_pattern).to_sql
88+
message_sql = Message.select(:topic_id).where("body ILIKE ?", search_pattern).to_sql
89+
union_sql = "(#{title_sql}) UNION (#{message_sql})"
90+
91+
Topic.where("topics.id IN (#{union_sql})")
8892
.includes(:creator)
8993
else
9094
Topic.none
@@ -237,6 +241,7 @@ def apply_cursor_pagination(base_query)
237241

238242
@topics = @topics.order('MAX(messages.created_at) DESC, topics.id DESC')
239243
.limit(25)
244+
.load
240245

241246
@new_topics_count = base_query.joins(:messages)
242247
.group('topics.id')
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class AddTrigramIndexesForSearch < ActiveRecord::Migration[8.0]
2+
def up
3+
# Enable pg_trgm extension for trigram-based text search
4+
enable_extension 'pg_trgm'
5+
6+
# Add GIN trigram indexes for efficient ILIKE searches
7+
add_index :topics, :title, using: :gin, opclass: :gin_trgm_ops, name: 'index_topics_on_title_trgm'
8+
add_index :messages, :body, using: :gin, opclass: :gin_trgm_ops, name: 'index_messages_on_body_trgm'
9+
end
10+
11+
def down
12+
remove_index :topics, name: 'index_topics_on_title_trgm'
13+
remove_index :messages, name: 'index_messages_on_body_trgm'
14+
# Note: Not disabling pg_trgm extension as it might be used elsewhere
15+
end
16+
end

db/schema.rb

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)