Skip to content

Instantly share code, notes, and snippets.

@bhserna
Last active May 26, 2025 16:48
Show Gist options
  • Save bhserna/be69f7d688bda8bc02d92fbc5defe509 to your computer and use it in GitHub Desktop.
Save bhserna/be69f7d688bda8bc02d92fbc5defe509 to your computer and use it in GitHub Desktop.
A concern to easily add search to a rails model using LIKE (only tested on sqlite)
# You can search on columns of the record or on associations
# Example of a search on the record's column
class Brand < ApplicationRecord
include Searchable
# All this are columns on the Brand record
search_on :id, :name, :description
end
class BrandsController < ApplicationController
def index
# maybe_search will execute the search only if the query is present
@brands = Brand.maybe_search(params[:query])
end
end
# Example of a search on the record's column and associations
class Quote < ApplicationRecord
include Searchable
belongs_to :client
belongs_to :requester, class_name: "Contact"
has_many :quote_items, dependent: :destroy
has_many :notes, as: :notable, dependent: :destroy
search_on :id, :sequence_id, :external_id, :custom_id, :general_conditions,
client: [:name],
requester: [:full_name, :current_company],
quote_items: [:product_name, :part_number, :description],
notes: [:content]
end
class QuotesController < ApplicationController
def index
@quotes = Quote.maybe_search(params[:query])
end
end
module Searchable
extend ActiveSupport::Concern
included do
cattr_accessor :searchable_fields, :searchable_associations_fields
end
class_methods do
def search_on(*searchable_fields, **searchable_associations_fields)
self.searchable_fields = searchable_fields
self.searchable_associations_fields = searchable_associations_fields
scope :search, ->(query) do
left_outer_joins(searchable_joins).where(searchable_where_clause, query: "%#{query}%").distinct
end
scope :maybe_search, ->(query) do
search(query) if query.present?
end
end
def searchable_joins
searchable_associations_fields.keys
end
def searchable_where_clause
all_normalized_searchable_fields.map { |field| "#{field} LIKE :query" }.join(" OR ")
end
def all_normalized_searchable_fields
normalized_searchable_fields + normalized_searchable_associations_fields
end
def normalized_searchable_fields
searchable_fields.map { |field| [table_name, field].join(".") }
end
def normalized_searchable_associations_fields
searchable_associations_fields.flat_map do |association, fields|
table_name = reflect_on_association(association).klass.table_name
Array.wrap(fields).map { |field| [table_name, field].join(".") }
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment