我的数据:
db.customers:
{ "_id" : "email@address.de", "since" : ISODate("2010-12-08T09:26:33Z") }
db.orders
{ "_id" : "201234224", "order_date" : ISODate("2010-12-08T09:26:33Z"), "net_revenue" : "26.8400", "customer_id" : "email@address.de" }
{ "_id" : "201223245", "order_date" : ISODate("2011-04-16T16:09:17Z"), "net_revenue" : "26.8400", "customer_id" : "email@address.de" }
现在我想总结每月回归客户的net_revenue
,这意味着我必须排除等于since
日期的初始订单。我带来了以下声明
db.orders.aggregate( [
{
$project:
{
_id:
{
$cond: { if: { $eq: [ db.customers.find({_id:"$customer_id"},{ _id:0,since:1 }), "$order_date" ] }, then: 0, else: 1 }
}
}
},
{ $group : {
_id: {
year : { $year: "$order_date" },
month : { $month: "$order_date" }
},
count: { $sum: 1 },
net_revenue: { $sum: 1 }
}}
]
);
我不确定如何对net_revenue
进行求和,但我的$cond
已失败
errmsg" : "exception: the $cond operator requires an array of 3 operands",
我需要其他的吗??
答案 0 :(得分:0)
首先,正如已经评论过的那样:您的net_revenue
是一个字符串,您无法创建字符串总和。首先,我们需要将所有net_revenue
字段转换为浮点数,以便能够处理它们:
> var bulk = db.orders.initializeUnorderedBulkOp()
> db.orders.find().forEach(function(order){
order.net_revenue = parseFloat(order.net_revenue);
bulk.find({_id:order._id}).updateOne(order);
})
> bulk.execute()
您应该确保将net_revenue
保存为浮点数,而不是字符串。
现在,您需要的实际数据。如果你改写它,你想要
拥有多个订单的所有客户的所有
net_revenues
的总数
这转换为:
db.orders.aggregate(
{$group: { _id:"$customer_id", orders:{$sum:1}, total_revenue:{$sum:"$net_revenue"} } },
{ $match: {orders:{ $gte:2 } } }
)
首先,我们按$customer_id
对所有订单进行分组,创建新的字段顺序,对于处理的每个文档,该字段顺序增加1
。此外,我们创建新字段total_revenue
,其增加了每个处理文档的net_revenue
值。最后但同样重要的是,我们只希望那些已经下了多个订单的客户。上述聚合的输出符合预期:
{ "_id" : "email@address.de", "orders" : 2, "total_revenue" : 53.68 }
根据禁区,第一个值无法计算。由于我们不能在聚合框架中跳过组内,我们需要使用map reduce和一种丑陋的黑客。
db.orders.mapReduce(
// Map
function(){
// We need to have all values
emit(this.customer_id,this.net_revenue);
},
// Reduce
function(customer,values){
var recurring_revenue = 0;
// We build the sum for all key which have multiple values
for(var idx = 1; idx < values.length; idx++) {
recurring_revenue += values[idx];
}
// here is the hack:
// Since the reduce phase is only run if a key has multiple values
// We need to make sure that the recurring revenues are can be queried in our
// output collection
var reduced = {
"recurring":recurring_revenue,
"orders":values.length
};
return reduced
},
// Options for map/reduce
{
// Since we want to skip the first order by date
// we need to make sure the original documents are fed into the map
// function in order of date
sort:{"order_date":1},
out:"recurring_revenues"
})
现在,为了让所有具有经常性收入的客户,我们需要查询我们的out
集合
db.recurring_revenues.find({"value.orders":{$gt:1}})
应返回
{ "_id" : "email@address.de", "value" : { "recurring" : 26.84, "orders" : 2 } }
根据给出的示例数据。如果有多个客户有多个订单,那么他们将被退回。
在2.4中,您没有批量操作。转换可以使用
完成db.order.find().forEach(function(order) {
var rev = parseFloat(order.net_revenue);
db.order.update({_id:order._id},{$set:{"net_revenue":rev}});
}
在大型系列中会相当慢。