module EvilQuery class Base def fetch_ids!(relation, id_column) #the relation should be ordered and limited by here @class.connection.select_values(id_sql(relation, id_column)) end def id_sql(relation, id_column) relation.select(id_column).to_sql end end class Participation < Base def initialize(user) @user = user @class = Post end def posts Post.joins(:participations).where(:participations => {:author_id => @user.person.id}).order("posts.interacted_at DESC") end end class LikedPosts < Base def initialize(user) @user = user end def posts Post.liked_by(@user.person) end end class CommentedPosts < Base def initialize(user) @user = user end def posts Post.commented_by(@user.person) end end class MultiStream < Base def initialize(user, order, max_time, include_spotlight) @user = user @class = Post @order = order @max_time = max_time @include_spotlight = include_spotlight end def make_relation! Rails.logger.debug("[EVIL-QUERY] make_relation!") post_ids = aspects_post_ids! + ids!(followed_tags_posts!) + ids!(mentioned_posts) post_ids += ids!(community_spotlight_posts!) if @include_spotlight Post.where(:id => post_ids) end def aspects_post_ids! Rails.logger.debug("[EVIL-QUERY] aspect_post_ids!") @user.visible_shareable_ids(Post, :limit => 15, :order => "#{@order} DESC", :max_time => @max_time, :all_aspects? => true, :by_members_of => @user.aspect_ids) end def followed_tags_posts! Rails.logger.debug("[EVIL-QUERY] followed_tags_posts!") StatusMessage.public_tag_stream(@user.followed_tag_ids) end def mentioned_posts Rails.logger.debug("[EVIL-QUERY] mentioned_posts") StatusMessage.where_person_is_mentioned(@user.person) end def community_spotlight_posts! Post.all_public.where(:author_id => fetch_ids!(Person.community_spotlight, 'people.id')) end def ids!(query) fetch_ids!(query.for_a_stream(@max_time, @order), 'posts.id') end end class VisibleShareableById < Base def initialize(user, klass, key, id, conditions={}) @querent = user @class = klass @key = key @id = id @conditions = conditions end def post! #small optimization - is this optimal order?? querent_is_contact.first || querent_is_author.first || public_post.first end protected def querent_is_contact @class.where(@key => @id).joins(:contacts).where(:contacts => {:user_id => @querent.id}).where(@conditions).select(@class.table_name+".*") end def querent_is_author @class.where(@key => @id, :author_id => @querent.person.id).where(@conditions) end def public_post @class.where(@key => @id, :public => true).where(@conditions) end end class ShareablesFromPerson < Base def initialize(querent, klass, person) @querent = querent @class = klass @person = person end def make_relation! return querents_posts if @person == @querent.person # persons_private_visibilities and persons_public_posts have no limit which is making shareable_ids gigantic. # perhaps they should the arrays should be merged and sorted # then the query at the bottom of this method can be paginated or something? shareable_ids = contact.present? ? fetch_ids!(persons_private_visibilities, "share_visibilities.shareable_id") : [] shareable_ids += fetch_ids!(persons_public_posts, table_name + ".id") @class.where(:id => shareable_ids, :pending => false). select('DISTINCT '+table_name+'.*'). order(table_name+".created_at DESC") end protected def table_name @class.table_name end def contact @contact ||= @querent.contact_for(@person) end def querents_posts @querent.person.send(table_name).where(:pending => false).order("#{table_name}.created_at DESC") end def persons_private_visibilities contact.share_visibilities.where(:hidden => false, :shareable_type => @class.to_s) end def persons_public_posts @person.send(table_name).where(:public => true).select(table_name+'.id') end end end