2011-03-10 8 views
15

Estoy usando rails 3 con mongoid. Tengo un conjunto de acciones con una colección incrustada de precios:Consultar objetos incrustados en Mongoid/rails 3 ("Inferior a", min operadores y clasificación)

class Stock 
    include Mongoid::Document 
    field :name, :type => String 
    field :code, :type => Integer 
    embeds_many :prices 

class Price 
    include Mongoid::Document 
    field :date, :type => DateTime 
    field :value, :type => Float 
    embedded_in :stock, :inverse_of => :prices 

quisiera obtener las acciones cuyo precio mínimo desde una fecha determinada es inferior a un precio p dado, y luego ser capaz de ordenar los precios de cada acción.

Pero parece que Mongodb no permite hacerlo. Debido a esto no funcionará:

@stocks = Stock.Where(:prices.value.lt => p) 

Además, parece que no puede mongodb objetos incrustados tipo.

Entonces, ¿hay alguna alternativa para llevar a cabo esta tarea?

Tal vez debería poner todo en una colección para que yo podría fácilmente ejecutar la consulta siguiente:

@stocks = Stock.Where(:prices.lt => p) 

Pero realmente quiero obtener resultados agrupados por nombres de archivo después de mi consulta (acciones distintas con una gran variedad de precios ordenados, por ejemplo). He oído hablar de map/reduce con la función de grupo, pero no estoy seguro de cómo usarlo correctamente con Mongoid.

http://www.mongodb.org/display/DOCS/Aggregation

El equivalente en SQL sería algo como esto:

SELECT name, code, min(price) from Stock WHERE price<p GROUP BY name, code 

Gracias por su ayuda.

Respuesta

20

MongoDB/Mongoid no le permite hacer esto. Tu ejemplo funcionará, la sintaxis es simplemente incorrecta.

@stocks = Stock.Where(:prices.value.lt => p) #does not work 

@stocks = Stock.where('prices.value' => {'$lt' => p}) #this should work 

Y, todavía encadenables así que puedes pedir por su nombre así:

@stocks = Stock.where('prices.value' => {'$lt' => p}).asc(:name) 

Espero que esto ayude.

+0

Tenga en cuenta que todavía se pueden realizar consultas más complejas, como esta: 'all_of ({: 'books.name' => 'name'}, {: 'books.author' =>/joe/i})' –

1

MongoDB hace permiten la consulta de documentos incrustados, http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-ValueinanEmbeddedObject

lo que se pierde es un ámbito en el modelo de precio, algo como esto:

scope :greater_than, lambda {|value| { :where => {:value.gt => value} } } 

Esto permitirá que se pasa en cualquier valor que desea y devuelva una colección Mongoid de precios con un valor superior al que ingresó. Será una colección sin clasificar, por lo que tendrá que ordenarla en Ruby.

prices.sort {|a,b| a.value <=> b.value}.each {|price| puts price.value} 

Mongoid tiene un map_reduce método al que se pasan dos variables de cadena que contienen las funciones Javascript para ejecutar map/reduce, y esto sería probablemente la mejor manera de hacer lo que necesita, pero el código anterior funcionará por ahora.

+0

Gracias por su respuesta. Pero no estoy seguro de cómo usar esta funcionalidad de alcance. Puse la función de alcance en mi modelo de Precio, y consultando @Stocks = Stock.where (: prices.greater_than => 200), me da un error: "método indefinido' greater_than 'para: precios: Símbolo " – mathieurip

+0

El alcance debería ser invocado en una sola acción, no en la colección. Entonces, tu código no funcionará, de hecho. Debido a que está buscando filtrar así, es mejor que tenga una llamada de MapReduce. Veré si puedo publicar un ejemplo en alguna parte. –

+0

Sí, esto es lo que acabo de descubrir. Sin embargo, encontré una solución tonta y lenta que funciona para obtener una lista de acciones cuyos precios están entre 100 y 200 por ejemplo, pero no estoy satisfecho con ella: 'i = 100 mientras que <200 results = Stock.where ("prices.value" => i) si (@stocks == nil) @stocks = resultados otro @stocks resultados + = extremo i + = 1 end' – mathieurip

2

He tenido un problema similar ... esto es lo que sugiero:

scope :price_min, lambda { |price_min| price_min.nil? ? {} : where("price.value" => { '$lte' => price_min.to_f }) } 

Coloque este ámbito en el modelo padre. Esto le permitirá realizar consultas como:

Stock.price_min(1000).count 

Tenga en cuenta que mi alcance solo funciona cuando realmente inserta algunos datos allí. Esto es muy útil si está creando consultas complejas con Mongoid.

¡Buena suerte!

lo mejor, Ruy

Cuestiones relacionadas