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 = { } )
2014-11-20 18:12:44 +09:00
joins ( :_tags ) . where ( :tags = > { :name = > tag . downcase . tr ( " " , " _ " ) } )
. limit ( options [ :limit ] )
. offset ( options [ :offset ] )
. order ( options [ :order ] || { :id = > :desc } )
2010-10-15 01:09:19 +00:00
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
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 )
2022-02-09 02:29:25 +09:00
joins << " LEFT JOIN pools_posts ep #{ i } ON (ep #{ i } .active AND ep #{ i } .post_id = p.id AND ep #{ i } .pool_id = ?) "
2010-04-20 23:05:11 +00:00
join_params << q [ :exclude_pools ] [ i ]
conds << " ep #{ i } IS NULL "
end
if q [ :exclude_pools ] [ i ] . is_a? ( String )
2022-02-09 02:29:25 +09:00
joins << " LEFT JOIN pools_posts ep #{ i } ON (ep #{ i } .active AND 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' \\ \\ ') "
2010-04-20 23:05:11 +00:00
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
if q [ :include ] . any?
2016-03-29 17:28:18 +09:00
conds << " tags_array && ARRAY[?]::varchar[] "
cond_params << Array ( q [ :include ] )
2010-09-06 05:35:59 +00:00
end
if q [ :related ] . any?
2016-03-29 17:28:18 +09:00
raise " You cannot search for more than #{ CONFIG [ " tag_query_limit " ] } tags at a time " if q [ :related ] . size > CONFIG [ " tag_query_limit " ]
conds << " tags_array @> ARRAY[?]::varchar[] "
cond_params << Array ( q [ :related ] )
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 " ]
2016-03-29 17:28:18 +09:00
conds << " NOT tags_array && ARRAY[?]::varchar[] "
cond_params << Array ( q [ :exclude ] )
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.
2015-11-23 13:41:17 +09:00
if CONFIG [ " hide_pending_posts " ] && ! options [ :from_api ] && ! options [ :pending ]
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 "
2022-01-25 04:32:49 +09:00
options [ :offset ] = nil
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