Created

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist

Class that handles the PostgreSQL full text search for railscasts.com

View episode_search.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
class EpisodeSearch
attr_reader :ability, :options
delegate :sanitize, to: Episode
 
def initialize(ability, options = {})
@ability = ability
@options = options.dup
end
 
def tag
@tag ||= Tag.find_by_id(options[:tag_id]) if options[:tag_id].present?
end
 
def episodes(paginate = true)
episodes = (tag ? tag.episodes : Episode).accessible_by(ability).where(type_conditions)
episodes = episodes.paginate(page: page, per_page: per_page) if paginate
if options[:search]
episodes = episodes.where(search_conditions(options[:search])).order(order_clause)
else
episodes = episodes.recent
end
episodes
end
 
def similar_episodes(episode)
episodes(false).limit(5).where(search_conditions(episode.name, "OR")).where("episodes.id != ?", episode.id)
end
 
def type_conditions
case options[:type]
when "free" then {pro: false, revised: false}
when "pro" then {pro: true, revised: false}
when "revised" then {revised: true}
else {}
end
end
 
def search_conditions(query, join = "AND")
query.split(/\s+/).map do |word|
'(' + word_search_conditions(word).join(' OR ') + ')'
end.join(" #{join} ")
end
 
def word_search_conditions(word)
%w[name description notes].map do |col|
"to_tsvector('english', episodes.#{col}) @@ plainto_tsquery(#{sanitize(word)})"
end + ["episodes.position::varchar = #{sanitize(word)}"]
end
 
def order_clause
(search_order_clauses + extra_order_clauses).join(" + ") + " desc"
end
 
def search_order_clauses
{
name: 5,
description: 3,
notes: 1
}.map do |col, weight|
"(ts_rank(to_tsvector('english', episodes.#{col}), " +
"plainto_tsquery(#{sanitize(options[:search])})) * #{weight})"
end
end
 
def extra_order_clauses
[
"((date(episodes.published_at) - date '2007-01-01') * 0.0003)",
"(episodes.comments_count * 0.001)",
"(CASE episodes.revised WHEN TRUE THEN 0.02 ELSE 0 END)",
]
end
 
def per_page
if options[:per_page]
options[:per_page].to_i
else
case options[:view]
when "list" then 40
when "grid" then 24
else 10
end
end
end
 
def page
options[:page].to_i > 0 ? options[:page].to_i : 1
end
 
def applied_filters
filters = []
filters << [options[:search], options.merge(search: nil, page: nil)] if options[:search].present?
filters << ["#{options[:type].titleize} Episodes", options.merge(type: nil, page: nil)] if options[:type].present?
filters << [tag.display_name, options.merge(tag_id: nil, page: nil)] if tag
filters
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.