app/models/record.rb in polysearch-0.1.1 vs app/models/record.rb in polysearch-0.2.0

- old
+ new

@@ -31,50 +31,72 @@ # validations ............................................................... # callbacks ................................................................. # scopes .................................................................... - scope :select_fts_rank, ->(value, *selects, rank_alias: nil) { - value = value.to_s.gsub(/\W/, " ").squeeze(" ").downcase.strip - value = Arel::Nodes::SqlLiteral.new(sanitize_sql_array(["?", value.to_s])) - plainto_tsquery = Arel::Nodes::NamedFunction.new("plainto_tsquery", [Arel::Nodes::SqlLiteral.new("'simple'"), value]) + scope :select_full_text_search_rank, ->(value, *selects) { + plainto_tsquery = Arel::Nodes::NamedFunction.new("plainto_tsquery", [Arel::Nodes::SqlLiteral.new("'simple'"), arel_search_value(value)]) ts_rank = Arel::Nodes::NamedFunction.new("ts_rank", [arel_table[:value], plainto_tsquery]) - - rank_alias ||= "fts_rank" selects << Arel.star if selects.blank? - selects << ts_rank.as(rank_alias) - select(*selects).order("#{rank_alias} desc") + selects << ts_rank.as("search_rank") + select(*selects).reorder("search_rank desc") } - scope :fts, ->(value) { - value = value.to_s.gsub(/\W/, " ").squeeze(" ").downcase.strip - value = Arel::Nodes::SqlLiteral.new(sanitize_sql_array(["?", value.to_s])) - plainto_tsquery = Arel::Nodes::NamedFunction.new("plainto_tsquery", [Arel::Nodes::SqlLiteral.new("'simple'"), value]) - where(Arel::Nodes::InfixOperation.new("@@", arel_table[:value], plainto_tsquery)) + scope :full_text_search, ->(value) { + if value.blank? + all + else + plainto_tsquery = Arel::Nodes::NamedFunction.new("plainto_tsquery", [Arel::Nodes::SqlLiteral.new("'simple'"), arel_search_value(value)]) + where(Arel::Nodes::InfixOperation.new("@@", arel_table[:value], plainto_tsquery)) + end } - scope :select_similarity_rank, ->(value, *selects, rank_alias: nil) { - value = value.to_s.gsub(/\W/, " ").squeeze(" ").downcase.strip - value = Arel::Nodes::SqlLiteral.new(sanitize_sql_array(["?", value.to_s])) - - rank_alias ||= "similarity_rank" + scope :select_similarity_rank, ->(value, *selects) { + similarity = Arel::Nodes::NamedFunction.new("similarity", [arel_table[:words], arel_search_value(value)]) selects << Arel.star if selects.blank? - selects << Arel::Nodes::NamedFunction.new("similarity", [arel_table[:words], value]).as(rank_alias) - select(*selects).order("#{rank_alias} desc") + selects << similarity.as("search_rank") + select(*selects).order("search_rank desc") } - scope :similar, ->(value, range: 0.01) { - value = value.to_s.gsub(/\W/, " ").squeeze(" ").downcase.strip - value = Arel::Nodes::SqlLiteral.new(sanitize_sql_array(["?", value.to_s])) - where Arel::Nodes::NamedFunction.new("similarity", [arel_table[:words], value]).gteq(range) + scope :similarity_search, ->(value) { + if value.blank? + all + else + where Arel::Nodes::NamedFunction.new("similarity", [arel_table[:words], arel_search_value(value)]).gt(0) + end } + scope :combined_search, ->(value) { + subquery = <<~SQL + ( + #{select_full_text_search_rank(value).full_text_search(value).except(:order).to_sql} + UNION ALL + #{select_similarity_rank(value).similarity_search(value).except(:order).to_sql} + ) AS #{table_name} + SQL + from(subquery).order("search_rank desc") + } + + scope :polysearch, ->(value) { + if value.blank? + all + else + full_text_search(value).exists? ? + select_full_text_search_rank(value).full_text_search(value) : + select_similarity_rank(value).similarity_search(value) + end + } + # additional config (i.e. accepts_nested_attribute_for etc...) .............. self.table_name = "polysearches" self.primary_key = :id # class methods ............................................................. class << self + def arel_search_value(value) + value = value.to_s.gsub(/\W/, " ").squeeze(" ").downcase.strip + Arel::Nodes::SqlLiteral.new(sanitize_sql_array(["?", value])) + end end # public instance methods ................................................... def update_value(tsvector_sql)