2012-04-21 12 views
8

Ho il seguente ambiti definiti nel mio modello:Come unione di due diversi criteri di Mongoid

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

voglio creare un altro ambito che combina i risultati di entrambi questi ambiti di corrente chiamata. Ho provato qualcosa di simile:

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

Ma questo appena finisce trattarli entrambi gli array come e concatenando. Il problema di questo è che quando provo a chiamare il mio scopo con Model.current, ricevo il seguente messaggio di errore:

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

Questo perché convertito i criteri Mongoid oggetto ad un array, ma non lo faccio voglio quello. Voglio che l'oggetto rimanga come un oggetto Criteria Mongoide.

Quello che voglio veramente è l'unione del set in_progress e del set imminente.

Qualche idea? Grazie.

+1

Se si desidera l'unione dei due set di risultati, è preferibile scrivere da zero il terzo ambito utilizzando una query ': $ o'. –

risposta

6

Puoi provare a comporre i tuoi criteri usando i metodi di query di Mongoid e il dereferenziamento nel selettore dei criteri, ma non lo consiglierei necessariamente - vedi sotto per un esempio. Seguo la raccomandazione per creare il terzo ambito. Ricorda che questi ambiti corrispondono alle query db che desideri essere efficienti, quindi è probabilmente il tuo tempo di esaminare e comprendere le query MongoDB sottostanti che vengono generate.

Modello

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 

prova

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

L'uso di 'any_of' ha funzionato per me, grazie! – josal

2

Dovete mappare l'Array torna a un Mongoid :: Criteri. Qualsiasi serie di vostro può essere tradotto in un criterio con any_in:

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

Quindi, qualcosa di simile dovrebbe fare il trucco: (non testato)

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

Spero che esiste soluzioni migliori, ma questo risolve l'equazione almeno.