开发者

Aggregate functions with MongoDB/Mongoid and calculated fields

I'm migrating an existing Rails app to use MongoDB (with Mongoid), and I'm having some trouble figuring out how to do aggregations like you can do with MySQL.

Previously I had something like SELECT DATE(created_at) AS day, SUM(amount) AS amount GROUP BY day, Which would return a collection tha开发者_运维问答t you can loop through in the template like this:

:day => '2011-03-01', :amount => 55.00 
:day => '2011-03-02', :amount => 45.00
etc...

Does anyone know how to do that in Mongoid? The model is pretty straightforward:

class Conversion
  include Mongoid::Document
  include Mongoid::Timestamps

  field :amount,      :type => Float, :default => 0.0
  ...
  # created_at generated automatically, standard Rails...
end

Thanks!

-Avishai


Unfortunately, it's a bit harder than you would think.

My understanding is that Mongoid itself does not support grouping and addition in one query, to do so - you'll need to do a map/reduce directly with the MongoDB driver. I ran this example with a similar data structure to what you have, but there may be a couple of errors during the translation, if you have any trouble, let me know and i'll try and amend it.

First you need to define your map and reduce functions. These have to be stored as JavaScript strings.

map = "function(){ emit(new Date(this.created_at.getYear(), this.created_at.getMonth(), this.created_at.getDate()), {amount: this.amount}); };"
reduce = "function(key, values){ var sum = 0; values.forEach(function(doc){ sum += doc.amount; }); return {amount: sum};};"

Then you call map_reduce directly on the collection, in this case:

Conversion.collection.map_reduce(map, reduce).find.to_a

Which will return an array of hashes like the following:

[{"_id"=>Sat Dec 13 13:00:00 UTC 0110, "value"=>{"amount"=>55.0}}, {"_id"=>Sat Jan 17 13:00:00 UTC 0111, "value"=>{"amount"=>45.0}}] 


Mongoid also has a handful of shortcuts for simple calculations:

You can add the following to any scope:

.max(:age)
.min(:quantity)
.sum(:total)

Note this will not be as fast as m/r for it loops through the cursor in the application vs in mongo. But if your result set is not that large, its a good option

Here is the source: https://github.com/mongoid/mongoid/blob/2.4.0-stable/lib/mongoid/contexts/enumerable.rb

Also stay tuned for the forthcoming aggregation framework: http://www.mongodb.org/display/DOCS/Aggregation+Framework

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜