2014-08-23 09:45:32 +09:00
|
|
|
module Post::SqlMethods
|
2010-04-20 23:05:11 +00:00
|
|
|
module ClassMethods
|
2010-10-15 01:09:19 +00:00
|
|
|
def find_by_tag_join(tag, options = {})
|
|
|
|
tag = tag.downcase.tr(" ", "_")
|
|
|
|
find(:all, :conditions => ["tags.name = ?", tag], :select => "posts.*", :joins => "JOIN posts_tags ON posts_tags.post_id = posts.id JOIN tags ON tags.id = posts_tags.tag_id", :limit => options[:limit], :offset => options[:offset], :order => (options[:order] || "posts.id DESC"))
|
|
|
|
end
|
2012-06-04 17:13:54 +07:00
|
|
|
|
2014-11-21 22:17:58 +09:00
|
|
|
def sql_range_for_where(parsed_query, field)
|
|
|
|
conds = []
|
|
|
|
params = []
|
|
|
|
generate_sql_range_helper(parsed_query, field, conds, params)
|
|
|
|
[*conds, *params]
|
|
|
|
end
|
|
|
|
|
2010-04-20 23:05:11 +00:00
|
|
|
def generate_sql_range_helper(arr, field, c, p)
|
|
|
|
case arr[0]
|
|
|
|
when :eq
|
|
|
|
c << "#{field} = ?"
|
|
|
|
p << arr[1]
|
|
|
|
|
|
|
|
when :gt
|
|
|
|
c << "#{field} > ?"
|
|
|
|
p << arr[1]
|
2012-06-04 17:13:54 +07:00
|
|
|
|
2010-04-20 23:05:11 +00:00
|
|
|
when :gte
|
|
|
|
c << "#{field} >= ?"
|
|
|
|
p << arr[1]
|
|
|
|
|
|
|
|
when :lt
|
|
|
|
c << "#{field} < ?"
|
|
|
|
p << arr[1]
|
|
|
|
|
|
|
|
when :lte
|
|
|
|
c << "#{field} <= ?"
|
|
|
|
p << arr[1]
|
|
|
|
|
|
|
|
when :between
|
|
|
|
c << "#{field} BETWEEN ? AND ?"
|
|
|
|
p << arr[1]
|
|
|
|
p << arr[2]
|
|
|
|
|
2010-11-18 20:47:53 +00:00
|
|
|
when :in
|
|
|
|
items = ["?"] * arr[1].length
|
|
|
|
c << "#{field} IN (#{items.join(", ")})"
|
|
|
|
p.concat(arr[1])
|
|
|
|
|
2010-04-20 23:05:11 +00:00
|
|
|
else
|
|
|
|
# do nothing
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def generate_sql(q, options = {})
|
|
|
|
if q.is_a?(Hash)
|
|
|
|
original_query = options[:original_query]
|
|
|
|
else
|
|
|
|
original_query = q
|
|
|
|
q = Tag.parse_query(q)
|
|
|
|
end
|
|
|
|
|
|
|
|
conds = ["true"]
|
|
|
|
joins = ["posts p"]
|
|
|
|
join_params = []
|
|
|
|
cond_params = []
|
|
|
|
|
2014-08-23 16:30:16 +09:00
|
|
|
if q.key?(:error)
|
2010-04-20 23:05:11 +00:00
|
|
|
conds << "FALSE"
|
|
|
|
end
|
|
|
|
|
|
|
|
generate_sql_range_helper(q[:post_id], "p.id", conds, cond_params)
|
|
|
|
generate_sql_range_helper(q[:mpixels], "p.width*p.height/1000000.0", conds, cond_params)
|
|
|
|
generate_sql_range_helper(q[:width], "p.width", conds, cond_params)
|
|
|
|
generate_sql_range_helper(q[:height], "p.height", conds, cond_params)
|
|
|
|
generate_sql_range_helper(q[:score], "p.score", conds, cond_params)
|
|
|
|
generate_sql_range_helper(q[:date], "p.created_at::date", conds, cond_params)
|
|
|
|
generate_sql_range_helper(q[:change], "p.change_seq", conds, cond_params)
|
|
|
|
|
|
|
|
if q[:md5].is_a?(String)
|
|
|
|
conds << "p.md5 IN (?)"
|
|
|
|
cond_params << q[:md5].split(/,/)
|
|
|
|
end
|
2012-06-04 17:13:54 +07:00
|
|
|
|
2010-05-02 23:02:19 +00:00
|
|
|
if q[:ext].is_a?(String)
|
|
|
|
conds << "p.file_ext IN (?)"
|
|
|
|
cond_params << q[:ext].downcase.split(/,/)
|
|
|
|
end
|
2012-06-04 17:13:54 +07:00
|
|
|
|
2014-08-23 16:30:16 +09:00
|
|
|
if q.key?(:show_deleted_only)
|
2010-11-18 20:19:17 +00:00
|
|
|
if q[:show_deleted_only]
|
|
|
|
conds << "p.status = 'deleted'"
|
|
|
|
end
|
2010-11-30 05:20:29 +00:00
|
|
|
elsif q[:post_id].empty?
|
2010-11-30 02:51:28 +00:00
|
|
|
# If a specific post_id isn't specified, default to filtering deleted posts.
|
|
|
|
conds << "p.status <> 'deleted'"
|
2010-04-20 23:05:11 +00:00
|
|
|
end
|
|
|
|
|
2014-08-23 16:30:16 +09:00
|
|
|
if q.key?(:parent_id) && q[:parent_id].is_a?(Integer)
|
2010-04-20 23:05:11 +00:00
|
|
|
conds << "(p.parent_id = ? or p.id = ?)"
|
|
|
|
cond_params << q[:parent_id]
|
|
|
|
cond_params << q[:parent_id]
|
2014-08-23 16:30:16 +09:00
|
|
|
elsif q.key?(:parent_id) && q[:parent_id] == false
|
2010-04-20 23:05:11 +00:00
|
|
|
conds << "p.parent_id is null"
|
|
|
|
end
|
|
|
|
|
|
|
|
if q[:source].is_a?(String)
|
2010-10-27 21:35:42 +00:00
|
|
|
conds << "lower(p.source) LIKE lower(?) ESCAPE E'\\\\'"
|
2010-04-20 23:05:11 +00:00
|
|
|
cond_params << q[:source]
|
|
|
|
end
|
|
|
|
|
2010-09-07 03:25:28 +00:00
|
|
|
if q[:subscriptions].is_a?(String)
|
2014-08-24 09:19:28 +09:00
|
|
|
/^(?<username>.+?):(?<subscription_name>.+)$/ =~ q[:subscriptions]
|
|
|
|
username ||= q[:subscriptions]
|
2010-09-07 02:54:04 +00:00
|
|
|
user = User.find_by_name(username)
|
2010-04-20 23:05:11 +00:00
|
|
|
|
2010-09-07 02:56:32 +00:00
|
|
|
if user
|
2010-09-07 03:25:28 +00:00
|
|
|
post_ids = TagSubscription.find_post_ids(user.id, subscription_name)
|
2010-04-20 23:05:11 +00:00
|
|
|
conds << "p.id IN (?)"
|
|
|
|
cond_params << post_ids
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if q[:fav].is_a?(String)
|
|
|
|
joins << "JOIN favorites f ON f.post_id = p.id JOIN users fu ON f.user_id = fu.id"
|
|
|
|
conds << "lower(fu.name) = lower(?)"
|
|
|
|
cond_params << q[:fav]
|
|
|
|
end
|
|
|
|
|
2014-08-23 16:30:16 +09:00
|
|
|
if q.key?(:vote_negated)
|
2010-04-20 23:05:11 +00:00
|
|
|
joins << "LEFT JOIN post_votes v ON p.id = v.post_id AND v.user_id = ?"
|
|
|
|
join_params << q[:vote_negated]
|
|
|
|
conds << "v.score IS NULL"
|
|
|
|
end
|
|
|
|
|
2014-08-23 16:30:16 +09:00
|
|
|
if q.key?(:vote)
|
2010-04-20 23:05:11 +00:00
|
|
|
joins << "JOIN post_votes v ON p.id = v.post_id"
|
|
|
|
conds << "v.user_id = ?"
|
|
|
|
cond_params << q[:vote][1]
|
|
|
|
|
|
|
|
generate_sql_range_helper(q[:vote][0], "v.score", conds, cond_params)
|
|
|
|
end
|
|
|
|
|
|
|
|
if q[:user].is_a?(String)
|
|
|
|
joins << "JOIN users u ON p.user_id = u.id"
|
|
|
|
conds << "lower(u.name) = lower(?)"
|
|
|
|
cond_params << q[:user]
|
|
|
|
end
|
|
|
|
|
2014-08-23 16:30:16 +09:00
|
|
|
if q.key?(:exclude_pools)
|
2010-04-20 23:05:11 +00:00
|
|
|
q[:exclude_pools].each_index do |i|
|
|
|
|
if q[:exclude_pools][i].is_a?(Integer)
|
|
|
|
joins << "LEFT JOIN pools_posts ep#{i} ON (ep#{i}.post_id = p.id AND ep#{i}.pool_id = ?)"
|
|
|
|
join_params << q[:exclude_pools][i]
|
|
|
|
conds << "ep#{i} IS NULL"
|
|
|
|
end
|
|
|
|
|
|
|
|
if q[:exclude_pools][i].is_a?(String)
|
|
|
|
joins << "LEFT JOIN pools_posts ep#{i} ON ep#{i}.post_id = p.id LEFT JOIN pools epp#{i} ON (ep#{i}.pool_id = epp#{i}.id AND epp#{i}.name ILIKE ? ESCAPE E'\\\\')"
|
|
|
|
join_params << ("%" + q[:exclude_pools][i].to_escaped_for_sql_like + "%")
|
|
|
|
conds << "ep#{i} IS NULL"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-08-23 16:30:16 +09:00
|
|
|
if q.key?(:pool)
|
2010-09-03 21:45:33 +00:00
|
|
|
conds << "pools_posts.active = true"
|
2010-04-20 23:05:11 +00:00
|
|
|
|
2014-08-23 18:28:59 +09:00
|
|
|
unless q.key?(:order)
|
2010-04-20 23:05:11 +00:00
|
|
|
pool_ordering = " ORDER BY pools_posts.pool_id ASC, nat_sort(pools_posts.sequence), pools_posts.post_id"
|
|
|
|
end
|
|
|
|
|
|
|
|
if q[:pool].is_a?(Integer)
|
|
|
|
joins << "JOIN pools_posts ON pools_posts.post_id = p.id JOIN pools ON pools_posts.pool_id = pools.id"
|
|
|
|
conds << "pools.id = ?"
|
|
|
|
cond_params << q[:pool]
|
|
|
|
end
|
|
|
|
|
|
|
|
if q[:pool].is_a?(String)
|
2014-11-08 22:57:37 +09:00
|
|
|
if q[:pool] == "*"
|
2010-08-26 21:19:46 +00:00
|
|
|
joins << "JOIN pools_posts ON pools_posts.post_id = p.id JOIN pools ON pools_posts.pool_id = pools.id"
|
|
|
|
else
|
|
|
|
joins << "JOIN pools_posts ON pools_posts.post_id = p.id JOIN pools ON pools_posts.pool_id = pools.id"
|
|
|
|
conds << "pools.name ILIKE ? ESCAPE E'\\\\'"
|
|
|
|
cond_params << ("%" + q[:pool].to_escaped_for_sql_like + "%")
|
|
|
|
end
|
2010-04-20 23:05:11 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-09-06 05:35:59 +00:00
|
|
|
tags_index_query = []
|
|
|
|
|
|
|
|
if q[:include].any?
|
2014-09-16 16:36:05 +09:00
|
|
|
tags_index_query << "(" + Array(q[:include]).map(&:to_escaped_for_tsquery).join(" | ") + ")"
|
2010-09-06 05:35:59 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
if q[:related].any?
|
2014-08-23 16:16:09 +09:00
|
|
|
raise "You cannot search for more than #{CONFIG["tag_query_limit"]} tags at a time" if q[:exclude].size > CONFIG["tag_query_limit"]
|
2014-09-16 16:36:05 +09:00
|
|
|
tags_index_query << "(" + Array(q[:related]).map(&:to_escaped_for_tsquery).join(" & ") + ")"
|
2010-04-20 23:05:11 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
if q[:exclude].any?
|
2014-08-23 16:16:09 +09:00
|
|
|
raise "You cannot search for more than #{CONFIG["tag_query_limit"]} tags at a time" if q[:exclude].size > CONFIG["tag_query_limit"]
|
2014-09-16 16:36:05 +09:00
|
|
|
tags_index_query << "!(" + Array(q[:exclude]).map(&:to_escaped_for_tsquery).join(" | ") + ")"
|
2010-09-06 05:35:59 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
if tags_index_query.any?
|
2012-10-14 16:45:51 +07:00
|
|
|
conds << "tags_index @@ to_tsquery('danbooru', ?)"
|
2014-08-23 16:16:09 +09:00
|
|
|
cond_params << tags_index_query.join(" & ")
|
2010-04-20 23:05:11 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
if q[:rating].is_a?(String)
|
|
|
|
case q[:rating][0, 1].downcase
|
|
|
|
when "s"
|
|
|
|
conds << "p.rating = 's'"
|
|
|
|
|
|
|
|
when "q"
|
|
|
|
conds << "p.rating = 'q'"
|
|
|
|
|
|
|
|
when "e"
|
|
|
|
conds << "p.rating = 'e'"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if q[:rating_negated].is_a?(String)
|
|
|
|
case q[:rating_negated][0, 1].downcase
|
|
|
|
when "s"
|
|
|
|
conds << "p.rating <> 's'"
|
|
|
|
|
|
|
|
when "q"
|
|
|
|
conds << "p.rating <> 'q'"
|
|
|
|
|
|
|
|
when "e"
|
|
|
|
conds << "p.rating <> 'e'"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if q[:unlocked_rating] == true
|
|
|
|
conds << "p.is_rating_locked = FALSE"
|
|
|
|
end
|
|
|
|
|
|
|
|
if options[:pending]
|
|
|
|
conds << "p.status = 'pending'"
|
|
|
|
end
|
2012-06-04 17:13:54 +07:00
|
|
|
|
2010-04-20 23:05:11 +00:00
|
|
|
if options[:flagged]
|
|
|
|
conds << "p.status = 'flagged'"
|
|
|
|
end
|
|
|
|
|
2014-08-23 16:30:16 +09:00
|
|
|
if q.key?(:show_holds)
|
2010-12-02 06:54:24 +00:00
|
|
|
if q[:show_holds] == :only
|
2010-04-20 23:05:11 +00:00
|
|
|
conds << "p.is_held"
|
2010-12-02 06:54:24 +00:00
|
|
|
elsif q[:show_holds] == :hide
|
|
|
|
conds << "NOT p.is_held"
|
|
|
|
elsif q[:show_holds] == :yes
|
2010-04-20 23:05:11 +00:00
|
|
|
end
|
|
|
|
else
|
2012-06-04 18:45:53 +07:00
|
|
|
# Hide held posts by default only when not using the API.
|
2014-11-08 22:57:37 +09:00
|
|
|
unless options[:from_api]
|
2012-06-04 18:45:53 +07:00
|
|
|
conds << "NOT p.is_held"
|
|
|
|
end
|
2010-04-20 23:05:11 +00:00
|
|
|
end
|
|
|
|
|
2014-08-23 16:30:16 +09:00
|
|
|
if q.key?(:show_pending)
|
2011-02-23 02:11:09 +00:00
|
|
|
if q[:show_pending] == :only
|
|
|
|
conds << "p.status = 'pending'"
|
|
|
|
elsif q[:show_pending] == :hide
|
|
|
|
conds << "p.status <> 'pending'"
|
|
|
|
elsif q[:show_pending] == :yes
|
|
|
|
end
|
|
|
|
else
|
2012-06-04 18:45:53 +07:00
|
|
|
# Hide pending posts by default only when not using the API.
|
2014-11-08 22:57:37 +09:00
|
|
|
if CONFIG["hide_pending_posts"] && !options[:from_api]
|
2011-02-23 02:11:09 +00:00
|
|
|
conds << "p.status <> 'pending'"
|
2012-06-04 18:45:53 +07:00
|
|
|
end
|
2011-02-23 02:11:09 +00:00
|
|
|
end
|
|
|
|
|
2014-08-23 16:30:16 +09:00
|
|
|
if q.key?(:shown_in_index)
|
2010-04-20 23:05:11 +00:00
|
|
|
if q[:shown_in_index]
|
|
|
|
conds << "p.is_shown_in_index"
|
|
|
|
else
|
|
|
|
conds << "NOT p.is_shown_in_index"
|
|
|
|
end
|
2014-08-23 18:06:02 +09:00
|
|
|
elsif original_query.blank? && !options[:from_api]
|
2012-06-04 18:45:53 +07:00
|
|
|
# Hide not shown posts by default only when not using the API.
|
2010-04-20 23:05:11 +00:00
|
|
|
conds << "p.is_shown_in_index"
|
|
|
|
end
|
|
|
|
|
|
|
|
sql = "SELECT "
|
|
|
|
|
|
|
|
if options[:count]
|
|
|
|
sql << "COUNT(*)"
|
|
|
|
elsif options[:select]
|
|
|
|
sql << options[:select]
|
|
|
|
else
|
|
|
|
sql << "p.*"
|
|
|
|
end
|
|
|
|
|
|
|
|
sql << " FROM " + joins.join(" ")
|
|
|
|
sql << " WHERE " + conds.join(" AND ")
|
|
|
|
|
2014-08-23 16:30:16 +09:00
|
|
|
if q.key?(:order) && !options[:count]
|
2010-04-20 23:05:11 +00:00
|
|
|
case q[:order]
|
|
|
|
when "id"
|
|
|
|
sql << " ORDER BY p.id"
|
2012-06-04 17:13:54 +07:00
|
|
|
|
2010-04-20 23:05:11 +00:00
|
|
|
when "id_desc"
|
|
|
|
sql << " ORDER BY p.id DESC"
|
2012-06-04 17:13:54 +07:00
|
|
|
|
2010-04-20 23:05:11 +00:00
|
|
|
when "score"
|
|
|
|
sql << " ORDER BY p.score DESC"
|
2012-06-04 17:13:54 +07:00
|
|
|
|
2010-04-20 23:05:11 +00:00
|
|
|
when "score_asc"
|
|
|
|
sql << " ORDER BY p.score"
|
2012-06-04 17:13:54 +07:00
|
|
|
|
2010-04-20 23:05:11 +00:00
|
|
|
when "mpixels"
|
|
|
|
# Use "w*h/1000000", even though "w*h" would give the same result, so this can use
|
|
|
|
# the posts_mpixels index.
|
|
|
|
sql << " ORDER BY width*height/1000000.0 DESC"
|
|
|
|
|
|
|
|
when "mpixels_asc"
|
|
|
|
sql << " ORDER BY width*height/1000000.0"
|
|
|
|
|
|
|
|
when "portrait"
|
|
|
|
sql << " ORDER BY 1.0*width/GREATEST(1, height)"
|
|
|
|
|
|
|
|
when "landscape"
|
|
|
|
sql << " ORDER BY 1.0*width/GREATEST(1, height) DESC"
|
|
|
|
|
2010-11-18 20:22:08 +00:00
|
|
|
when "portrait_pool"
|
|
|
|
# We can only do this if we're searching for a pool.
|
2014-11-08 22:57:37 +09:00
|
|
|
if q.key?(:pool)
|
2010-11-18 20:22:08 +00:00
|
|
|
sql << " ORDER BY 1.0*width / GREATEST(1, height), nat_sort(pools_posts.sequence), pools_posts.post_id"
|
|
|
|
end
|
|
|
|
|
2010-04-20 23:05:11 +00:00
|
|
|
when "change", "change_asc"
|
|
|
|
sql << " ORDER BY change_seq"
|
|
|
|
|
|
|
|
when "change_desc"
|
|
|
|
sql << " ORDER BY change_seq DESC"
|
|
|
|
|
|
|
|
when "vote"
|
2014-08-23 16:30:16 +09:00
|
|
|
if q.key?(:vote)
|
2010-04-20 23:05:11 +00:00
|
|
|
sql << " ORDER BY v.updated_at DESC"
|
|
|
|
end
|
|
|
|
|
|
|
|
when "fav"
|
|
|
|
if q[:fav].is_a?(String)
|
|
|
|
sql << " ORDER BY f.id DESC"
|
|
|
|
end
|
|
|
|
|
|
|
|
when "random"
|
2012-08-29 05:14:27 -07:00
|
|
|
sql << " ORDER BY random()"
|
2010-04-20 23:05:11 +00:00
|
|
|
|
|
|
|
else
|
2010-11-18 20:10:19 +00:00
|
|
|
use_default_order = true
|
|
|
|
end
|
|
|
|
else
|
|
|
|
use_default_order = true
|
|
|
|
end
|
|
|
|
|
2014-11-08 22:57:37 +09:00
|
|
|
if use_default_order && !options[:count]
|
2010-11-18 20:10:19 +00:00
|
|
|
if pool_ordering
|
|
|
|
sql << pool_ordering
|
|
|
|
else
|
2014-11-08 22:57:37 +09:00
|
|
|
if options[:from_api]
|
2010-11-18 20:10:19 +00:00
|
|
|
# When using the API, default to sorting by ID.
|
|
|
|
sql << " ORDER BY p.id DESC"
|
2010-04-20 23:05:11 +00:00
|
|
|
else
|
2010-11-18 20:10:19 +00:00
|
|
|
sql << " ORDER BY p.index_timestamp DESC"
|
2010-04-20 23:05:11 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if options[:limit]
|
|
|
|
sql << " LIMIT " + options[:limit].to_s
|
|
|
|
end
|
|
|
|
|
|
|
|
if options[:offset]
|
|
|
|
sql << " OFFSET " + options[:offset].to_s
|
|
|
|
end
|
|
|
|
|
|
|
|
params = join_params + cond_params
|
2010-09-06 05:35:59 +00:00
|
|
|
|
2014-08-23 16:56:00 +09:00
|
|
|
Post.sanitize_sql_array([sql, *params])
|
2010-04-20 23:05:11 +00:00
|
|
|
end
|
|
|
|
end
|
2012-06-04 17:13:54 +07:00
|
|
|
|
2010-04-20 23:05:11 +00:00
|
|
|
def self.included(m)
|
|
|
|
m.extend(ClassMethods)
|
|
|
|
end
|
|
|
|
end
|