Model#search: refactor searching for attributes.

This commit is contained in:
evazion
2019-08-29 20:24:52 -05:00
parent 6fc4b63fa8
commit 7b8584e3b0
26 changed files with 90 additions and 212 deletions

View File

@@ -35,36 +35,23 @@ class ApplicationRecord < ActiveRecord::Base
where.not("#{qualified_column_for(attr)} ~ ?", "(?e)" + value)
end
def attribute_matches(attribute, value, **options)
return all if value.nil?
def search_boolean_attribute(attribute, params)
return all unless params[attribute]
column = column_for_attribute(attribute)
case column.sql_type_metadata.type
when :boolean
boolean_attribute_matches(attribute, value, **options)
when :integer, :datetime
numeric_attribute_matches(attribute, value, **options)
when :string, :text
text_attribute_matches(attribute, value, **options)
else
raise ArgumentError, "unhandled attribute type"
end
end
def boolean_attribute_matches(attribute, value)
if value.to_s.truthy?
value = true
elsif value.to_s.falsy?
value = false
value = params[attribute].to_s
if value.truthy?
where(attribute => true)
elsif value.falsy?
where(attribute => false)
else
raise ArgumentError, "value must be truthy or falsy"
end
where(attribute => value)
end
# range: "5", ">5", "<5", ">=5", "<=5", "5..10", "5,6,7"
def numeric_attribute_matches(attribute, range)
return all unless range.present?
column = column_for_attribute(attribute)
qualified_column = "#{table_name}.#{column.name}"
parsed_range = Tag.parse_helper(range, column.type)
@@ -73,6 +60,8 @@ class ApplicationRecord < ActiveRecord::Base
end
def text_attribute_matches(attribute, value, index_column: nil, ts_config: "english")
return all unless value.present?
column = column_for_attribute(attribute)
qualified_column = "#{table_name}.#{column.name}"
@@ -85,6 +74,31 @@ class ApplicationRecord < ActiveRecord::Base
end
end
def search_attributes(params, *attributes)
attributes.reduce(self) do |relation, attribute|
relation.search_attribute(attribute, params)
end
end
def search_attribute(name, params)
type = type_for_attribute(name).type || reflect_on_association(name)&.class_name
case type
when "User"
search_user_attribute(name, params)
when "Post"
search_post_id_attribute(params)
when :string, :text
search_text_attribute(name, params)
when :boolean
search_boolean_attribute(name, params)
when :integer, :datetime
numeric_attribute_matches(name, params[name])
else
raise NotImplementedError, "unhandled attribute type"
end
end
def search_text_attribute(attr, params, **options)
if params[attr].present?
where(attr => params[attr])
@@ -111,7 +125,7 @@ class ApplicationRecord < ActiveRecord::Base
def search_user_attribute(attr, params)
if params["#{attr}_id"]
numeric_attribute_matches("#{attr}_id", params["#{attr}_id"])
search_attribute("#{attr}_id", params)
elsif params["#{attr}_name"]
where(attr => User.search(name_matches: params["#{attr}_name"]).reorder(nil))
elsif params[attr]
@@ -125,7 +139,7 @@ class ApplicationRecord < ActiveRecord::Base
relation = self
if params[:post_id].present?
relation = relation.attribute_matches(:post_id, params[:post_id])
relation = relation.search_attribute(:post_id, params)
end
if params[:post_tags_match].present?
@@ -161,9 +175,9 @@ class ApplicationRecord < ActiveRecord::Base
params ||= {}
q = all
q = q.attribute_matches(:id, params[:id])
q = q.attribute_matches(:created_at, params[:created_at]) if attribute_names.include?("created_at")
q = q.attribute_matches(:updated_at, params[:updated_at]) if attribute_names.include?("updated_at")
q = search_attribute(:id, params)
q = search_attribute(:created_at, params) if attribute_names.include?("created_at")
q = search_attribute(:updated_at, params) if attribute_names.include?("updated_at")
q
end

View File

@@ -490,8 +490,7 @@ class Artist < ApplicationRecord
def search(params)
q = super
q = q.search_text_attribute(:name, params)
q = q.search_text_attribute(:group_name, params)
q = q.search_attributes(params, :is_active, :is_banned, :creator, :name, :group_name)
if params[:any_other_name_like]
q = q.any_other_name_like(params[:any_other_name_like])
@@ -509,10 +508,6 @@ class Artist < ApplicationRecord
q = q.url_matches(params[:url_matches])
end
q = q.attribute_matches(:is_active, params[:is_active])
q = q.attribute_matches(:is_banned, params[:is_banned])
q = q.search_user_attribute(:creator, params)
if params[:has_tag].to_s.truthy?
q = q.joins(:tag).where("tags.post_count > 0")
elsif params[:has_tag].to_s.falsy?

View File

@@ -49,10 +49,7 @@ class ArtistUrl < ApplicationRecord
def self.search(params = {})
q = super
q = q.attribute_matches(:artist_id, params[:artist_id])
q = q.attribute_matches(:is_active, params[:is_active])
q = q.search_text_attribute(:url, params)
q = q.search_text_attribute(:normalized_url, params)
q = q.search_attributes(params, :url, :normalized_url, :artist_id, :is_active)
q = q.artist_matches(params[:artist])
q = q.url_matches(params[:url_matches])

View File

@@ -14,14 +14,7 @@ class ArtistVersion < ApplicationRecord
q = q.where("name like ? escape E'\\\\'", params[:name].to_escaped_for_sql_like)
end
q = q.search_user_attribute(:updater, params)
if params[:artist_id].present?
q = q.where(artist_id: params[:artist_id].split(",").map(&:to_i))
end
q = q.attribute_matches(:is_active, params[:is_active])
q = q.attribute_matches(:is_banned, params[:is_banned])
q = q.search_attributes(params, :updater, :is_active, :is_banned, :artist_id)
params[:order] ||= params.delete(:sort)
if params[:order] == "name"

View File

@@ -28,9 +28,8 @@ class Ban < ApplicationRecord
def self.search(params)
q = super
q = q.search_user_attribute(:banner, params)
q = q.search_user_attribute(:user, params)
q = q.attribute_matches(:reason, params[:reason_matches])
q = q.search_attributes(params, :banner, :user)
q = q.text_attribute_matches(:reason, params[:reason_matches])
q = q.expired if params[:expired].to_s.truthy?
q = q.unexpired if params[:expired].to_s.falsy?

View File

@@ -30,24 +30,14 @@ class BulkUpdateRequest < ApplicationRecord
def search(params = {})
q = super
q = q.search_user_attribute(:user, params)
q = q.search_user_attribute(:approver, params)
if params[:forum_topic_id].present?
q = q.where(forum_topic_id: params[:forum_topic_id].split(",").map(&:to_i))
end
if params[:forum_post_id].present?
q = q.where(forum_post_id: params[:forum_post_id].split(",").map(&:to_i))
end
q = q.search_attributes(params, :user, :approver, :forum_topic_id, :forum_post_id)
q = q.text_attribute_matches(:title, params[:title_matches])
q = q.text_attribute_matches(:script, params[:script_matches])
if params[:status].present?
q = q.where(status: params[:status].split(","))
end
q = q.attribute_matches(:title, params[:title_matches])
q = q.attribute_matches(:script, params[:script_matches])
params[:order] ||= "status_desc"
case params[:order]
when "id_desc"

View File

@@ -34,12 +34,8 @@ class Comment < ApplicationRecord
def search(params)
q = super
q = q.attribute_matches(:body, params[:body_matches], index_column: :body_index)
q = q.search_post_id_attribute(params)
q = q.search_user_attribute(:creator, params)
q = q.attribute_matches(:is_deleted, params[:is_deleted])
q = q.attribute_matches(:is_sticky, params[:is_sticky])
q = q.attribute_matches(:do_not_bump_post, params[:do_not_bump_post])
q = q.search_attributes(params, :post, :creator, :is_deleted, :is_sticky, :do_not_bump_post)
q = q.text_attribute_matches(:body, params[:body_matches], index_column: :body_index)
case params[:order]
when "post_id", "post_id_desc"

View File

@@ -141,15 +141,11 @@ class Dmail < ApplicationRecord
def search(params)
q = super
q = q.attribute_matches(:title, params[:title_matches])
q = q.attribute_matches(:body, params[:message_matches], index_column: :message_index)
q = q.search_user_attribute(:to, params)
q = q.search_user_attribute(:from, params)
q = q.search_attributes(params, :to, :from, :is_spam, :is_read, :is_deleted)
q = q.text_attribute_matches(:title, params[:title_matches])
q = q.text_attribute_matches(:body, params[:message_matches], index_column: :message_index)
params[:is_spam] = false unless params[:is_spam].present?
q = q.attribute_matches(:is_spam, params[:is_spam])
q = q.attribute_matches(:is_read, params[:is_read])
q = q.attribute_matches(:is_deleted, params[:is_deleted])
q = q.read if params[:read].to_s.truthy?
q = q.unread if params[:read].to_s.falsy?

View File

@@ -47,6 +47,7 @@ class FavoriteGroup < ApplicationRecord
def search(params)
q = super
q = q.search_attributes(params, :is_public)
if params[:creator_id].present?
user = User.find(params[:creator_id])
@@ -65,8 +66,6 @@ class FavoriteGroup < ApplicationRecord
q = q.name_matches(params[:name_matches])
end
q = q.attribute_matches(:is_public, params[:is_public])
q.apply_default_order(params)
end
end

View File

@@ -47,25 +47,17 @@ class ForumPost < ApplicationRecord
def search(params)
q = super
q = q.permitted
q = q.search_user_attribute(:creator, params)
if params[:topic_id].present?
q = q.where("forum_posts.topic_id = ?", params[:topic_id].to_i)
end
q = q.search_attributes(params, :creator, :topic_id, :is_deleted)
q = q.text_attribute_matches(:body, params[:body_matches], index_column: :text_index)
if params[:topic_title_matches].present?
q = q.topic_title_matches(params[:topic_title_matches])
end
q = q.attribute_matches(:body, params[:body_matches], index_column: :text_index)
if params[:topic_category_id].present?
q = q.joins(:topic).where("forum_topics.category_id = ?", params[:topic_category_id].to_i)
end
q = q.attribute_matches(:is_deleted, params[:is_deleted])
q.apply_default_order(params)
end
end

View File

@@ -39,10 +39,6 @@ class ForumTopic < ApplicationRecord
def reverse_category_mapping
@reverse_category_mapping ||= CATEGORIES.invert
end
def for_category_id(cid)
where(:category_id => cid)
end
end
def category_name
@@ -70,25 +66,13 @@ class ForumTopic < ApplicationRecord
def search(params)
q = super
q = q.permitted
q = q.search_attributes(params, :is_sticky, :is_locked, :is_deleted, :category_id, :title)
q = q.text_attribute_matches(:title, params[:title_matches], index_column: :text_index)
if params[:mod_only].present?
q = q.where("min_level >= ?", MIN_LEVELS[:Moderator])
end
q = q.attribute_matches(:title, params[:title_matches], index_column: :text_index)
if params[:category_id].present?
q = q.for_category_id(params[:category_id])
end
if params[:title].present?
q = q.where("title = ?", params[:title])
end
q = q.attribute_matches(:is_sticky, params[:is_sticky])
q = q.attribute_matches(:is_locked, params[:is_locked])
q = q.attribute_matches(:is_deleted, params[:is_deleted])
case params[:order]
when "sticky"
q = q.sticky_first

View File

@@ -55,12 +55,8 @@ class ModAction < ApplicationRecord
def self.search(params)
q = super
q = q.attribute_matches(:description, params[:description_matches])
q = q.search_user_attribute(:creator, params)
if params[:category].present?
q = q.attribute_matches(:category, params[:category])
end
q = q.search_attributes(params, :creator, :category)
q = q.text_attribute_matches(:description, params[:description_matches])
q.apply_default_order(params)
end

View File

@@ -19,10 +19,8 @@ class Note < ApplicationRecord
def search(params)
q = super
q = q.attribute_matches(:body, params[:body_matches], index_column: :body_index)
q = q.attribute_matches(:is_active, params[:is_active])
q = q.search_user_attribute(:creator, params)
q = q.search_post_id_attribute(params)
q = q.search_attributes(params, :creator, :post, :is_active)
q = q.text_attribute_matches(:body, params[:body_matches], index_column: :body_index)
q.apply_default_order(params)
end

View File

@@ -1,20 +1,13 @@
class NoteVersion < ApplicationRecord
belongs_to :post
belongs_to :note
belongs_to_updater :counter_cache => "note_update_count"
def self.search(params)
q = super
if params[:post_id]
q = q.where(post_id: params[:post_id].split(",").map(&:to_i))
end
if params[:note_id]
q = q.where(note_id: params[:note_id].split(",").map(&:to_i))
end
q = q.attribute_matches(:is_active, params[:is_active])
q = q.attribute_matches(:body, params[:body_matches])
q = q.search_user_attribute(:updater, params)
q = q.search_attributes(params, :updater, :is_active, :post, :note_id)
q = q.text_attribute_matches(:body, params[:body_matches])
q.apply_default_order(params)
end

View File

@@ -57,22 +57,19 @@ class Pool < ApplicationRecord
def search(params)
q = super
q = q.search_attributes(params, :creator, :is_active, :is_deleted)
q = q.text_attribute_matches(:description, params[:description_matches])
if params[:name_matches].present?
q = q.name_matches(params[:name_matches])
end
q = q.attribute_matches(:description, params[:description_matches])
q = q.search_user_attribute(:creator, params)
if params[:category] == "series"
q = q.series
elsif params[:category] == "collection"
q = q.collection
end
q = q.attribute_matches(:is_active, params[:is_active])
q = q.attribute_matches(:is_deleted, params[:is_deleted])
params[:order] ||= params.delete(:sort)
case params[:order]
when "name"

View File

@@ -24,10 +24,9 @@ class PostAppeal < ApplicationRecord
def search(params)
q = super
q = q.search_attributes(params, :creator, :post)
q = q.text_attribute_matches(:reason, params[:reason_matches])
q = q.attribute_matches(:reason, params[:reason_matches])
q = q.search_user_attribute(:creator, params)
q = q.search_post_id_attribute(params)
q = q.resolved if params[:is_resolved].to_s.truthy?
q = q.unresolved if params[:is_resolved].to_s.falsy?

View File

@@ -47,20 +47,11 @@ class PostDisapproval < ApplicationRecord
concerning :SearchMethods do
class_methods do
def post_tags_match(query)
where(post_id: PostQueryBuilder.new(query).build.reorder(""))
end
def search(params)
q = super
q = q.attribute_matches(:post_id, params[:post_id])
q = q.attribute_matches(:message, params[:message_matches])
q = q.search_user_attribute(:user, params)
q = q.search_text_attribute(:message, params)
q = q.post_tags_match(params[:post_tags_match]) if params[:post_tags_match].present?
q = q.where(reason: params[:reason]) if params[:reason].present?
q = q.search_attributes(params, :post, :user, :message, :reason)
q = q.text_attribute_matches(:message, params[:message_matches])
q = q.with_message if params[:has_message].to_s.truthy?
q = q.without_message if params[:has_message].to_s.falsy?

View File

@@ -51,7 +51,8 @@ class PostFlag < ApplicationRecord
def search(params)
q = super
q = q.attribute_matches(:reason, params[:reason_matches])
q = q.search_attributes(params, :post, :is_resolved)
q = q.text_attribute_matches(:reason, params[:reason_matches])
# XXX
if params[:creator_id].present?
@@ -74,9 +75,6 @@ class PostFlag < ApplicationRecord
end
end
q = q.search_post_id_attribute(params)
q = q.attribute_matches(:is_resolved, params[:is_resolved])
case params[:category]
when "normal"
q = q.where("reason NOT IN (?) AND reason NOT LIKE ?", [Reasons::UNAPPROVED, Reasons::BANNED], Reasons::REJECTED)

View File

@@ -22,15 +22,7 @@ class PostReplacement < ApplicationRecord
class_methods do
def search(params = {})
q = super
q = q.attribute_matches(:replacement_url, params[:replacement_url])
q = q.attribute_matches(:original_url, params[:original_url])
q = q.attribute_matches(:file_ext_was, params[:file_ext_was])
q = q.attribute_matches(:file_ext, params[:file_ext])
q = q.attribute_matches(:md5_was, params[:md5_was])
q = q.attribute_matches(:md5, params[:md5])
q = q.search_user_attribute(:creator, params)
q = q.search_post_id_attribute(params)
q = q.search_attributes(params, :post, :creator, :md5, :md5_was, :file_ext, :file_ext_was, :original_url, :replacement_url)
q.apply_default_order(params)
end
end

View File

@@ -931,6 +931,8 @@ class Tag < ApplicationRecord
def search(params)
q = super
q = q.search_attributes(params, :is_locked, :category)
if params[:fuzzy_name_matches].present?
q = q.fuzzy_name_matches(params[:fuzzy_name_matches])
end
@@ -943,10 +945,6 @@ class Tag < ApplicationRecord
q = q.where("tags.name": normalize_name(params[:name]).split(","))
end
if params[:category].present?
q = q.where("category = ?", params[:category])
end
if params[:hide_empty].blank? || params[:hide_empty].to_s.truthy?
q = q.where("post_count > 0")
end
@@ -963,8 +961,6 @@ class Tag < ApplicationRecord
q = q.joins("LEFT JOIN artists ON tags.name = artists.name").where("artists.name IS NULL OR artists.is_active = false")
end
q = q.attribute_matches(:is_locked, params[:is_locked])
params[:order] ||= params.delete(:sort)
case params[:order]
when "name"

View File

@@ -177,25 +177,12 @@ class Upload < ApplicationRecord
def search(params)
q = super
q = q.search_user_attribute(:uploader, params)
q = q.search_post_id_attribute(params)
if params[:source].present?
q = q.where(source: params[:source])
end
q = q.search_attributes(params, :uploader, :post, :source, :rating, :parent_id, :server)
if params[:source_matches].present?
q = q.where("uploads.source LIKE ? ESCAPE E'\\\\'", params[:source_matches].to_escaped_for_sql_like)
end
if params[:rating].present?
q = q.where(rating: params[:rating])
end
if params[:parent_id].present?
q = q.attribute_matches(:rating, params[:parent_id])
end
if params[:has_post].to_s.truthy?
q = q.where.not(post_id: nil)
elsif params[:has_post].to_s.falsy?
@@ -214,10 +201,6 @@ class Upload < ApplicationRecord
q = q.where("uploads.tag_string LIKE ? ESCAPE E'\\\\'", params[:tag_string].to_escaped_for_sql_like)
end
if params[:server].present?
q = q.where(server: params[:server])
end
q.apply_default_order(params)
end
end

View File

@@ -708,13 +708,7 @@ class User < ApplicationRecord
params = params.dup
params[:name_matches] = params.delete(:name) if params[:name].present?
q = q.search_text_attribute(:name, params)
q = q.attribute_matches(:level, params[:level])
q = q.attribute_matches(:post_upload_count, params[:post_upload_count])
q = q.attribute_matches(:post_update_count, params[:post_update_count])
q = q.attribute_matches(:note_update_count, params[:note_update_count])
q = q.attribute_matches(:favorite_count, params[:favorite_count])
q = q.search_user_attribute(:inviter, params)
q = q.search_attributes(params, :name, :level, :inviter, :post_upload_count, :post_update_count, :note_update_count, :favorite_count)
if params[:name_matches].present?
q = q.where_ilike(:name, normalize_name(params[:name_matches]))

View File

@@ -44,13 +44,8 @@ class UserFeedback < ApplicationRecord
def search(params)
q = super
q = q.attribute_matches(:body, params[:body_matches])
q = q.search_user_attribute(:user, params)
q = q.search_user_attribute(:creator, params)
if params[:category].present?
q = q.where("category = ?", params[:category])
end
q = q.search_attributes(params, :user, :creator, :category)
q = q.text_attribute_matches(:body, params[:body_matches])
q.apply_default_order(params)
end

View File

@@ -57,8 +57,8 @@ class WikiPage < ApplicationRecord
q = q.where("title LIKE ? ESCAPE E'\\\\'", params[:title].mb_chars.downcase.strip.tr(" ", "_").to_escaped_for_sql_like)
end
q = q.search_user_attribute(:creator, params)
q = q.attribute_matches(:body, params[:body_matches], index_column: :body_index, ts_config: "danbooru")
q = q.search_attributes(params, :creator, :is_locked, :is_deleted)
q = q.text_attribute_matches(:body, params[:body_matches], index_column: :body_index, ts_config: "danbooru")
if params[:other_names_match].present?
q = q.other_names_match(params[:other_names_match])
@@ -74,9 +74,6 @@ class WikiPage < ApplicationRecord
q = q.where("other_names is null or other_names = '{}'")
end
q = q.attribute_matches(:is_locked, params[:is_locked])
q = q.attribute_matches(:is_deleted, params[:is_deleted])
params[:order] ||= params.delete(:sort)
case params[:order]
when "title"

View File

@@ -9,15 +9,9 @@ class WikiPageVersion < ApplicationRecord
def search(params)
q = super
if params[:wiki_page_id].present?
q = q.where("wiki_page_id = ?", params[:wiki_page_id].to_i)
end
q = q.attribute_matches(:title, params[:title])
q = q.attribute_matches(:body, params[:body])
q = q.attribute_matches(:is_locked, params[:is_locked])
q = q.attribute_matches(:is_deleted, params[:is_deleted])
q = q.search_user_attribute(:updater, params)
q = q.search_attributes(params, :updater, :is_locked, :is_deleted, :wiki_page_id)
q = q.text_attribute_matches(:title, params[:title])
q = q.text_attribute_matches(:body, params[:body])
q.apply_default_order(params)
end