我刚刚开始使用MongoDB和mongoid。 我遇到的最大问题是理解map / reduce功能,以便能够进行一些非常基本的分组等。
假设我有这样的模型:
class Person
include Mongoid::Document
field :age, type: Integer
field :name
field :sdate
end
该模型会产生如下对象:
#<Person _id: 9xzy0, age: 22, name: "Lucas", sdate: "2013-10-07">
#<Person _id: 9xzy2, age: 32, name: "Paul", sdate: "2013-10-07">
#<Person _id: 9xzy3, age: 23, name: "Tom", sdate: "2013-10-08">
#<Person _id: 9xzy4, age: 11, name: "Joe", sdate: "2013-10-08">
有人可以展示如何使用mongoid map reduce来获取按照sdate字段分组的那些对象的集合吗?并获得那些共享相同sdate字段的年龄总和?
我知道这一点:http://mongoid.org/en/mongoid/docs/querying.html#map_reduce 但不知何故,这将有助于看到应用于一个真实的例子。代码在哪里,我猜想在模型中是需要的范围等等。
我可以使用mongoid进行简单的搜索,获取数组并手动构建我需要的任何内容,但我猜地图reduce就是这里的方式。我想象在mongoid页面上提到的这些js函数被提供给在内部进行这些操作的DB。从积极的记录来看,这些新概念有点奇怪。
我在Heroku(mongolab)上使用Rails 4.0,Ruby 1.9.3,Mongoid 4.0.0,MongoDB 2.4.6,虽然我有本地2.0,我应该更新。
感谢。
答案 0 :(得分:21)
从http://mongoid.org/en/mongoid/docs/querying.html#map_reduce获取示例并根据您的情况调整它们并添加注释以进行解释。
map = %Q{
function() {
emit(this.sdate, { age: this.age, name : this. name });
// here "this" is the record that map
// is going to be executed on
}
}
reduce = %Q{
function(key, values) {
// this will be executed for every group that
// has the same sdate value
var result = { avg_of_ages: 0 };
var sum = 0; // sum of all ages
var totalnum = 0 // total number of people
values.forEach(function(value) {
sum += value.age;
});
result.avg_of_ages = sum/total // finding the average
return result;
}
}
results = Person.map_reduce(map, reduce) //You can access this as an array of maps
first_average = results[0].avg_of_ages
results.each do |result|
// do whatever you want with result
end
虽然我建议你使用Aggregation而不是map reduce来进行这么简单的操作。这样做的方法如下:
results = Person.collection.aggregate([{"$group" => { "_id" => {"sdate" => "$sdate"},
"avg_of_ages"=> {"$avg" : "$age"}}}])
并且结果与减少的地图几乎完全相同,您可以编写更少的代码。