2012-04-21 12 views
8

Ich habe folgende Bereiche in meinem Modell definiert:Wie Vereinigung zwei verschiedene Mongoid Kriterien

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) 
} 

ich einen anderen Bereich erstellen möchten, die die Ergebnisse von diesen beiden Bereiche genannt Strom kombiniert. Ich habe versucht, so etwas wie dieses:

scope :current, -> { self.in_progress | self.upcoming } 

Aber dies nur landet sie beide wie Arrays Behandlung und verketten sie. Das Problem dabei ist, dass wenn ich versuche, mit Model.current meinen Rahmen zu nennen, ich die folgende Fehlermeldung erhalten:

NoMethodError: undefined method `as_conditions' for #<Array:0xaceb008> 

Dies ist, weil es die Mongoid Criteria-Objekt in ein Array umgewandelt, aber ich weiß nicht will das. Ich möchte, dass das Objekt als Mongoid-Kriterium-Objekt bleibt.

Was ich wirklich will, ist die Vereinigung des in_progress Sets und des kommenden Sets.

Irgendwelche Ideen? Vielen Dank.

+1

Wenn Sie die Vereinigung der beiden Ergebnismengen wollen, dann ist es wahrscheinlich besser, den dritten Bereich von Grund auf mit einer ': $ oder' Abfrage zu schreiben. –

Antwort

6

Sie können versuchen, Ihre Kriterien mit Mongoids Abfragemethoden und Dereferenzierung in den Selektor der Kriterien zu erstellen, aber ich würde dies nicht unbedingt empfehlen - siehe unten für ein Beispiel. Ich stimme der Empfehlung zu, deinen dritten Bereich zu erstellen. Denken Sie daran, dass diese Bereiche db-Abfragen entsprechen, die effizient sein sollen. Daher lohnt es sich wahrscheinlich, die resultierenden und zugrunde liegenden MongoDB-Abfragen zu untersuchen und zu verstehen, die generiert werden.

Modell

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)) 
    end 
end 
+0

Die Verwendung von 'any_of' hat für mich funktioniert, danke! – josal

2

Sie haben Ihre Array zurück auf eine Mongoid :: Kriterien abzubilden. Jedes Array von Ihnen kann mit ANY_IN ein Kriterium übersetzt werden:

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

Also, so etwas wie dies sollte es tun: (ungetestet)

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

Ich hoffe, dass es bessere Lösungen gibt, aber diese löst die Gleichung zumindest.