Skip to content

Commit

Permalink
Refactor Speaker indexing into a Speaker::Index record.
Browse files Browse the repository at this point in the history
Follow-up to adrienpoly#540
  • Loading branch information
kaspth committed Jan 22, 2025
1 parent 8b46e6d commit 8f01dab
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 38 deletions.
27 changes: 27 additions & 0 deletions app/models/speaker/index.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class Speaker::Index < ApplicationRecord
self.table_name = :speakers_search_index

include ActiveRecord::SQLite::Index # Depends on `table_name` being assigned.
class_attribute :index_columns, default: {name: 0, github: 1}

belongs_to :speaker, foreign_key: :rowid

def self.search(query)
query = query&.gsub(/[^[:word:]]/, " ") || "" # remove non-word characters
query = query.split.map { |word| "#{word}*" }.join(" ") # wildcard search
where("#{table_name} match ?", query)
end

def self.snippets(**)
index_columns.each_key.reduce(all) { |relation, column| relation.snippet(column, **) }
end

def self.snippet(column, tag: "mark", omission: "…", limit: 32)
offset = index_columns.fetch(column)
select("snippet(#{table_name}, #{offset}, '<#{tag}>', '</#{tag}>', '#{omission}', #{limit}) AS #{column}_snippet")
end

def reindex
update! id: speaker.id, name: speaker.name, github: speaker.github
end
end
48 changes: 12 additions & 36 deletions app/models/speaker/searchable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,36 @@ module Speaker::Searchable
extend ActiveSupport::Concern

included do
scope :ft_search, ->(query) do
query = query&.gsub(/[^[:word:]]/, " ") || "" # remove non-word characters
query = query.split.map { |word| "#{word}*" }.join(" ") # wildcard search
joins("join speakers_search_index idx on speakers.id = idx.rowid")
.where("speakers_search_index match ?", query)
end
has_one :index, foreign_key: :rowid, dependent: :destroy

scope :ft_search, ->(query) { select("speakers.*").joins(:index).merge(Speaker::Index.search(query)) }

scope :with_snippets, ->(**options) do
select("speakers.*")
.select_snippet("name", 0, **options)
.select_snippet("github", 1, **options)
select("speakers.*").merge(Speaker::Index.snippets(**options))
end

scope :ranked, -> do
select("speakers.*,
bm25(speakers_search_index, 2, 1) AS combined_score")
.order(Arel.sql("combined_score ASC"))
.order(combined_score: :asc)
end

after_create_commit :create_in_index
after_update_commit :update_in_index
after_destroy_commit :remove_from_index
after_save_commit :reindex
end

class_methods do
def rebuild_search_index
connection.execute("DELETE FROM speakers_search_index")
Speaker.find_each(&:create_in_index)
end

def select_snippet(column, offset, tag: "mark", omission: "…", limit: 32)
select("snippet(speakers_search_index, #{offset}, '<#{tag}>', '</#{tag}>', '#{omission}', #{limit}) AS #{column}_snippet")
def reindex_all
Speaker::Index.delete_all
Speaker.find_each(&:reindex)
end
end

def name_with_snippet
try(:name_snippet) || name
end

def create_in_index
execute_sql_with_binds "insert into speakers_search_index(rowid, name, github) values (?, ?, ?)", id, name, github
end

def update_in_index
execute_sql_with_binds "update speakers_search_index set name = ?, github = ? where rowid = ?", name, github, id
end

def remove_from_index
execute_sql_with_binds "delete from speakers_search_index where rowid = ?", id
end

private

def execute_sql_with_binds(*statement)
self.class.connection.execute self.class.sanitize_sql(statement)
def index
super || build_index
end
delegate :reindex, to: :index
end
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ def up
"name", "github", "tokenize = porter"
]

Speaker.rebuild_search_index
Speaker.reindex_all
end

def down
Expand Down
2 changes: 1 addition & 1 deletion test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ActiveSupport::TestCase
# end

Talk.reindex_all
Speaker.rebuild_search_index
Speaker.reindex_all
end
# Run tests in parallel with specified workers
parallelize(workers: :number_of_processors)
Expand Down

0 comments on commit 8f01dab

Please sign in to comment.