2012-04-21 16 views
8

I tienen los siguientes ámbitos definidos en mi modelo:Cómo unión de dos criterios diferentes 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) 
} 

que quieren crear otro ámbito que combina los resultados de estos dos ámbitos llamados actual. He intentado algo como esto:

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

Pero esto sólo termina tratándolos como ambas matrices y la concatenación de ellas. El problema con esto es que cuando trato de llamar a mi alcance con Model.current, me sale el siguiente mensaje de error:

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

Esto se debe a que convierte los Criterios MongoId se oponen a una matriz, pero no lo hacen quiero eso. Quiero que el objeto permanezca como un objeto Criterio Mongoide.

Lo que realmente quiero es la unión del conjunto in_progress y el próximo conjunto.

¿Alguna idea? Gracias.

+1

Si desea la unión de los dos conjuntos de resultados, entonces probablemente sea mejor escribir el tercer ámbito desde cero con una consulta ': $ o'. –

Respuesta

6

Puede intentar redactar sus criterios utilizando los métodos de consulta de Mongoid y la desreferenciación en el selector de criterios, pero no necesariamente lo recomendaría, vea a continuación un ejemplo. En segundo lugar la recomendación para diseñar su tercer alcance. Recuerde que estos ámbitos corresponden a las consultas de DB que desea que sean eficientes, por lo que probablemente valga la pena examinar y comprender las consultas de MongoDB resultantes y subyacentes que se generan.

Modelo

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 

prueba

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

El uso de 'any_of' funcionó para mí, ¡gracias! – josal

2

Usted tiene que asignar su arsenal de nuevo a un MongoId :: Criterios. Cualquier conjunto de la suya puede ser traducido a un criterio con ANY_IN:

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

Por lo tanto, algo como esto debe hacer el truco: (no probado)

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

espero existe mejores soluciones, pero esto resuelve la ecuación al menos.

Cuestiones relacionadas