How to union two different Mongoid Criteria How to union two different Mongoid Criteria mongodb mongodb

How to union two different Mongoid Criteria


You can try to compose your criteria using Mongoid's query methods and dereferencing into the criteria's selector, but I wouldn't necessarily recommend this -- see below for an example. I second the recommendation to craft your third scope. Remember that these scopes correspond to db queries that you want to be efficient, so it is probably worth your time to examine and understand the resulting and underlying MongoDB queries that are generated.

Model

class Episode  include Mongoid::Document  field :name, type: String  field :start_time, type: Time  field :end_time, type: Time  scope :upcoming, -> { where(:start_time.gt => Time.now).asc(:start_time) }  scope :in_progress, -> {     now = Time.now     where(:start_time.lte => now).where(:end_time.gte => now).asc(:start_time)  }  scope :current, -> { any_of([upcoming.selector, in_progress.selector]) }  scope :current_simpler, -> { where(:end_time.gte => Time.now) }end

Test

require 'test_helper'class EpisodeTest < ActiveSupport::TestCase  def setup    Episode.delete_all  end  test "scope composition" do    #p Episode.in_progress    #p Episode.upcoming    #p Episode.current    #p Episode.current_simpler    in_progress_name = 'In Progress'    upcoming_name = 'Upcoming'    Episode.create(:name => in_progress_name, :start_time => Time.now, :end_time => 1.hour.from_now)    Episode.create(:name => upcoming_name, :start_time => 1.hour.from_now, :end_time => 2.hours.from_now)    assert_equal([in_progress_name], Episode.in_progress.to_a.map(&:name))    assert_equal([upcoming_name], Episode.upcoming.to_a.map(&:name))    assert_equal([in_progress_name, upcoming_name], Episode.current.to_a.map(&:name))    assert_equal([in_progress_name, upcoming_name], Episode.current_simpler.to_a.map(&:name))  endend


You have to map your Array back to a Mongoid::Criteria.Any array of yours can be translated to a criteria with any_in:

scope :has_data, -> { any_in(:_id => all.select{ |record| record.data.size > 0 }.map{ |r| r.id }) }

So, something like this should do the trick: (untested)

scope :current, -> { any_in(:_id => (self.in_progress + self.upcoming).map{ |r| r.id }) }

I hope there exists better solutions, but this solves the equation at least.