Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine two scopes with OR

I have a tag feed and a friend feed. I want to combine these two and build the ultimate "all" feed.

For friend feed:

class Post < ActiveRecord::Base
  scope :friendfeed, lambda{|x| followed_by}

  def self.followed_by(user)
    where("user_id IN (?) OR user_id = ?", user.watched_ids, user.id)
  end
end

For tag feed:

class Post < ActiveRecord::Base
  scope :tagfeed, lambda{|x| infatuated_with}

  def self.infatuated_with(user)
    joins(:attachments).where("attachments.tag_id IN (?)", user.tags).select("DISTINCT pages.*")
  end
end

And I would call something like this from the controller (I'm using Kaminari gem for pagination):

@tag_feed = Post.tagfeed(current_user).page(params[:page]).per(21)
@friend_feed = Post.friendfeed(current_user).page(params[:page]).per(21)

Now I want to have a universal feed, but I'm lost. Scopes are meant for narrowing down, but in this case I'm trying to do an OR operation. Doing stuff like

@mother_of_all_feed = @tag_feed + @friend_feed

would be redundant, and I wouldn't be able to control the number of posts appearing on a single page. How can I go about doing this? Thanks!

By the way, for tags I have association set up like this:

class Post < ActiveRecord::Base
  has_many :attachments
  has_many :tags, :through => :attachments
end

class Tag < ActiveRecord::Base
  has_many :attachments
  has_many :posts, :through => :attachments
end

class Attachment < ActiveRecord::Base
  belongs_to :tag
  belongs_to :post
end
like image 629
Vlad Avatar asked May 24 '12 07:05

Vlad


3 Answers

There's a rails pull request for this feature (https://github.com/rails/rails/pull/9052), but in the meantime, some one has created a monkey patch that you can include in your initializers that will allow you to or scopes and where clauses in one query and still give you an ActiveRecord::Relation:

https://gist.github.com/j-mcnally/250eaaceef234dd8971b

With that, you'd be able to OR your scopes like this

Post.tagfeed(current_user).or.friendfeed(current_user)

or write a new scope

scope :mother_of_all_feed, lambda{|user| tagfeed(user).or.friendfeed(user)}
like image 190
keithepley Avatar answered Nov 19 '22 08:11

keithepley


Answering my own question. I think I figured out a way.

where("pages.id IN (?) OR pages.id IN (?)",
  Page.where(
      "user_id IN (?) OR user_id = ?",
      user.watched_ids, user.id
  ),
  Page
    .joins(:attachments)
    .where("attachments.tag_id IN (?)", user.tags)
    .select("DISTINCT pages.*")
)

It seems to be working so far, hope this is it!

like image 40
Vlad Avatar answered Nov 19 '22 08:11

Vlad


Here's an example of how I combined two scopes.

scope :reconcilable, -> do
  scopes = [
    with_matching_insurance_payment_total,
    with_zero_insurance_payments_and_zero_amount
  ]

  where('id in (?)', scopes.flatten.map(&:id))
end
like image 3
Jason Swett Avatar answered Nov 19 '22 08:11

Jason Swett