简体   繁体   中英

Querying embedded objects in Mongoid/rails 3 (“Lower than”, Min operators and sorting)

I am using rails 3 with mongoid. I have a collection of Stocks with an embedded collection of Prices :

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

I would like to get the stocks whose the minimum price since a given date is lower than a given price p, and then be able to sort the prices for each stock.

But it looks like Mongodb does not allow to do it. Because this will not work:

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

Also, it seems that mongoDB can not sort embedded objects.

So, is there an alternative in order to accomplish this task ?

Maybe i should put everything in one collection so that i could easily run the following query:

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

But i really want to get results grouped by stock names after my query (distinct stocks with an array of ordered prices for example). I have heard about map/reduce with the group function but i am not sure how to use it correctly with Mongoid.

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

The equivalent in SQL would be something like this:

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

Thanks for your help.

MongoDB / Mongoid do allow you to do this. Your example will work, the syntax is just incorrect.

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

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

And, it's still chainable so you can order by name as well:

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

Hope this helps.

I've had a similar problem... here's what I suggest:

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

Place this scope in the parent model. This will enable you to make queries like:

Stock.price_min(1000).count

Note that my scope only works when you actually insert some data there. This is very handy if you're building complex queries with Mongoid.

Good luck!

Very best, Ruy

MongoDB does allow querying of embedded documents, http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-ValueinanEmbeddedObject

What you're missing is a scope on the Price model, something like this:

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

This will let you pass in any value you want and return a Mongoid collection of prices with the value greater than what you passed in. It'll be an unsorted collection, so you'll have to sort it in Ruby.

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

Mongoid does have a map_reduce method to which you pass two string variables containing the Javascript functions to execute map/reduce, and this would probably be the best way of doing what you need, but the code above will work for now.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM