我想知道在一段时间内每个品牌销售了多少独特产品。单个文档如下所示:
{
brand_id: 1,
product_id: 2,
date: ISODate("2014-12-12")
}
在SQL中,这将是:SELECT brand_id, count(distinct(product_id)) FROM orders WHERE date ... GROUP BY brand_id
;
我无法通过Mongo的聚合框架(组等)来实现这一目标。这是我现在的map-reduce:
db.orders.mapReduce(
function() {
emit(this.brand_id, this.product_id);
},
function(key, values) {
return values.filter(function (value, index, self) {return self.indexOf(value) === index;}).length;
},
{
query: {date: {$gte: new Date('2014-11-20')}},
out: "example"
}
)
这看起来很不错。但是,我遇到的问题是reduce函数没有立即接收所有“值”,而是批量接收101个元素。因此,任何查找唯一值的尝试都会失败,而我得到的只是调用reduce函数的最后时间内唯一元素的数量。我无法想象如何在这里使用“finalize”属性来获得我想要的东西。
非常感谢任何想法。
我在Mongo 2.4和2.6上试过这个,只是为了确保它不是版本问题。
答案 0 :(得分:2)
我无法想办法通过Mongo的聚合框架(组等)
您可以轻松汇总结果,而不是选择map-reduce解决方案:
Match
日期大于等于的记录
指定日期。
Group
基于brand_id
字段。
使用$addToSet运算符维护products
唯一列表
每组product_id
。
Project
每个密钥中count
数组的products
。
代码:
db.collection.aggregate([
{$match:{"date":{$gte:new Date('2014-11-20')}}},
{$group:{"_id":"$brand_id","products":{$addToSet:"$product_id"}}},
{$project:{"_id":0,"brand_id":"$_id","distinct_prod":{$size:"$products"}}}
])
来到你的map-reduce解决方案,
但是,我遇到的问题是reduce函数没有立即接收所有“值”,而是批量接收101 元素
这是mongodb可以为每个组调用reduce函数的一种方式。来自docs:
MongoDB可以多次调用reduce函数 键。在这种情况下,从reduce函数的前一个输出 该键将成为下一个reduce的输入值之一 该键的函数调用。
您需要对map
,reduce
个函数进行一些修改并添加新的finalize
函数:
mongodb
调用reduce
时,您需要记住
函数对于同一个键不止一次,结果是前一个
调用作为reduce函数的输入传递,同时传递
下次调用reduce函数时的其他值。reduce
函数
为每个键累积不同的product_ids
并写一个
finalize
函数,用于计算这些唯一值的计数。代码:
db.collection.mapReduce(
function() {
// emitting the same structure returned by the reduce function.
emit(this.brand_id, {"prod_id":[this.product_id]});
},
function(key, values) {
// the return value would be a list of unique product_ids.
var res = {"prod_id":[]};
for(var i=0;i<values.length;i++)
{
for(var j=0;j<values[i].prod_id.length;j++){
if(res.prod_id.indexOf(values[i].prod_id[j]) == -1){
res.prod_id.push(values[i].prod_id[j]);
}
}}
return res;
},
{
query: {date: {$gte: new Date('2014-11-20')}},
out: "example",
finalize: function(key, reducedValue){
// it returns just the count
return reducedValue.prod_id.length;
}
}
)