Javascript 如何通过计算任何数组项在mongodb聚合中添加新字段
我正在努力找出如何在基于项目数组计算的聚合中添加一个新的Javascript 如何通过计算任何数组项在mongodb聚合中添加新字段,javascript,mongodb,mongodb-query,aggregation-framework,aggregate,Javascript,Mongodb,Mongodb Query,Aggregation Framework,Aggregate,我正在努力找出如何在基于项目数组计算的聚合中添加一个新的status字段 目前,我在Angular前端通过询问集合和使用迭代每个元素来实现这一点。但是我想把计算转移到后端,我被MongoDB聚合卡住了 输入:每个订阅(每个用户一个)都有许多合同(每个月一个),我想从合同中计算订阅的状态 [ { "_id": "5b4d9a2fde57780a2e175agh", "user": "5a709da7c2ffc105hu47b254", "contracts": [
status
字段
目前,我在Angular前端通过询问集合和使用迭代每个元素来实现这一点。但是我想把计算转移到后端,我被MongoDB聚合卡住了
输入:每个订阅(每个用户一个)都有许多合同(每个月一个),我想从合同中计算订阅的状态
[
{
"_id": "5b4d9a2fde57780a2e175agh",
"user": "5a709da7c2ffc105hu47b254",
"contracts": [
{
"_id": "5b4d9a2fde57780a2e175agh",
"date": "2018-07-15T13:00:00.000Z",
"totalPrice": 200,
"totalPaid": 67,
"isCanceled": false,
"paymentFailed": false
},
{
"_id": "5b4d9a2fde57780a2e175agh",
"date": "2018-08-15T13:00:00.000Z",
"totalPrice": 200,
"totalPaid": 0,
"isCanceled": false,
"paymentFailed": false
},
{
"_id": "5b4d9a2fde57780a2e175agh",
"date": "2018-09-15T13:00:00.000Z",
"totalPrice": 200,
"totalPaid": 0,
"isCanceled": false,
"paymentFailed": false
}
]
}
]
输出:在这种情况下,取过去的合同,检查用户是否按照totalPrice所说的支付了(以及是否没有任何支付错误)。如果不是,则订阅的付款“待定”:
{
"_id": "5b4d9a2fde57780a2e175agh",
"user": "5a709da7c2ffc105hu47b254",
"status": "PAYMENT_PENDING" // or "PAYMENT_ERROR" or "SUCCESS"…
}
但我无法按每个数组项进行计算:如果我尝试使用“$contracts.$.totalPaid”
(“'FieldPath字段名不能以'$'开头”),它会给出一个错误
这是我的聚合步骤(仅测试两个状态
条件):
我已经成功地从订阅的字段中计算了状态
,但没有从它的合同数组中计算出来
[
{
"_id": "5b4d9a2fde57780a2e175agh",
"user": "5a709da7c2ffc105hu47b254",
"contracts": [
{
"_id": "5b4d9a2fde57780a2e175agh",
"date": "2018-07-15T13:00:00.000Z",
"totalPrice": 200,
"totalPaid": 67,
"isCanceled": false,
"paymentFailed": false
},
{
"_id": "5b4d9a2fde57780a2e175agh",
"date": "2018-08-15T13:00:00.000Z",
"totalPrice": 200,
"totalPaid": 0,
"isCanceled": false,
"paymentFailed": false
},
{
"_id": "5b4d9a2fde57780a2e175agh",
"date": "2018-09-15T13:00:00.000Z",
"totalPrice": 200,
"totalPaid": 0,
"isCanceled": false,
"paymentFailed": false
}
]
}
]
如果有人能为我指出聚合框架的正确方向,我将不胜感激,就像我发现的其他示例/问题一样,$sum
/calculate,但不要添加新字段
非常感谢。我找到了一种方法:我不再直接在$addFields
步骤中计算,而是执行更多步骤
请随意建议对聚合的改进,因为这是我的第一个大agrgegate:)
第一步:$match
我感兴趣的订阅条件。(使用你自己的)
步骤2:$lookup
加入每个订阅及其所有合同:
$lookup: {
// Join with subscriptioncontracts collection
"from" : "subscriptioncontracts",
"localField" : "_id",
"foreignField" : "subscription",
"as" : "contracts"
}
第三步:放松
每个订阅合同制作一份文档:
$unwind: {
// Make one document per subscription contract
path : "$contracts",
preserveNullAndEmptyArrays : false // optional
}
步骤4:$sort
(我需要使用上一份/最新合同的一些特殊内容,因此我需要对它们进行排序)
第五步:$group
神奇之处在于:使用所有订阅合同(现在每个“合同”都在自己的文档中,而不是在一个数组中)在计算中添加新字段
我需要添加“订阅”
,因为我需要将其作为响应进行投影
$group: {
// Calculate status from contracts (group by the same subscription _id)
"_id": "$_id",
"subscription": { "$first": "$$CURRENT" },
"_lastContract": { $last: "$contracts" },
"_statusPaymentPending": {
$sum: { $cond: [
{ $and: [
{ $lt: [ "$contracts.paidAmount", "$contracts.checkout.totalPrice" ] },
{ $lt: [ "$contracts.subscriptionDate", new Date() ] },
{ $eq: [ "$contracts.paymentFailed", false ] },
{ $eq: [ "$contracts.isCanceled", false ] }
]}, 1, 0
] }
},
"_statusPaymentFailed": {
$sum: { $cond: [
{ $and: [
{ $lt: [ "$contracts.paidAmount", "$contracts.checkout.totalPrice" ] },
{ $lt: [ "$contracts.subscriptionDate", new Date() ] },
{ $eq: [ "$contracts.paymentFailed", true ] },
{ $eq: [ "$contracts.isCanceled", false ] }
]}, 1, 0
] }
}
}
步骤6:$项目
在这里,我根据订阅数据(而不是合同)计算其他状态
第7步:项目
最后,我将所有状态合并到一个“status”
字段中:
$project: {
"subscription": 1,
// Condense all statuses into one Status field
"status": {
$cond: [
"$_statusCanceled",
'CANCELED',
{ $cond: [
"$_statusPaymentFailed",
'PAYMENT_ERROR',
{ $cond: [
"$_statusPaymentPending",
'PAYMENT_PENDING',
{ $cond: [
"$_statusUnsubscribed",
'UNSUBSCRIBED',
{ $cond: [
"$_statusExtensionPending",
'PENDING_EXTEND_CONTRACT',
{ $cond: [
"$_statusFutureStart",
'FUTURE_START',
{ $cond: [
"$_statusFinished",
'FINISHED',
'OK'
]}
]}
]}
]}
]}
]}
]
}
}
待办事项
也许你可以改进我的流程:
- 是否可以将
subscription
对象中的所有数据移动到根目录中(并伴有计算的状态
字段),而不是使用订阅
和状态
最终对象
- 您是否看到了其他更好的方法来计算这个最终的
状态
字段,而不是有一个$group
和两个$project
感谢您提出的任何改进建议 是否检查付款中的所有或任何数组元素?如果所有数组元素都有不同的值,那么状态是什么?Hi@Veeram我检查的数组元素都是过去的合同,也就是说,{$lte:[“$contracts.date”,ISODate(“2018-08-24T18:32:50.958+0000”)}
(今天)。如果未满足$cond
中的任何条件,只需输出状态:“OK”
(为简洁起见,在本例中为“其他内容”)
$project: {
// Calculate other statuses
"_id": "$_id",
"subscription": "$subscription",
"_statusCanceled": { $cond: [ "$subscription.isCanceled", true, false ] },
"_statusFutureStart": { $cond: [ { $gte: [ "$subscription.subscriptionStartDate", new Date() ] }, true, false ] },
"_statusUnsubscribed": { $cond: [ { $gte: [ "$subscription.subscriptionEndDate", new Date() ] }, true, false ] },
"_statusFinished": {
$cond: [
{ $and: [
{ $ne: [ "$subscription.subscriptionEndDate", undefined ] },
{ $lte: [ "$subscription.subscriptionEndDate", new Date() ] }
]},
true,
false
]
},
"_statusPaymentPending": "$_statusPaymentPending",
"_statusPaymentFailed": "$_statusPaymentFailed",
"_statusExtensionPending": { $cond: [ { $lte: [ "$_lastContract.expirationDate", new Date() ] }, true, false ] }
}
$project: {
"subscription": 1,
// Condense all statuses into one Status field
"status": {
$cond: [
"$_statusCanceled",
'CANCELED',
{ $cond: [
"$_statusPaymentFailed",
'PAYMENT_ERROR',
{ $cond: [
"$_statusPaymentPending",
'PAYMENT_PENDING',
{ $cond: [
"$_statusUnsubscribed",
'UNSUBSCRIBED',
{ $cond: [
"$_statusExtensionPending",
'PENDING_EXTEND_CONTRACT',
{ $cond: [
"$_statusFutureStart",
'FUTURE_START',
{ $cond: [
"$_statusFinished",
'FINISHED',
'OK'
]}
]}
]}
]}
]}
]}
]
}
}