Я нашел способ: вместо прямого вычисления на шаге $addFields
я делаю еще несколько шагов.
Пожалуйста, не стесняйтесь предлагать улучшения для агрегата, так как это мой первый большой агргейт:)
Шаг 1: $ match
Условия подписки, которые меня интересуют. (Используйте свои собственные)
Шаг 2: $ lookup
Присоединяйтесь к каждой подписке со всеми контрактами:
$lookup: {
// Join with subscriptioncontracts collection
"from" : "subscriptioncontracts",
"localField" : "_id",
"foreignField" : "subscription",
"as" : "contracts"
}
Шаг 3: $ раскрутить
Создайте один документ на каждый подписной контракт:
$unwind: {
// Make one document per subscription contract
path : "$contracts",
preserveNullAndEmptyArrays : false // optional
}
Шаг 4: $ sort
(мне нужно что-то особенное, используя последний / самый современный контракт, поэтому мне нужно их отсортировать)
$sort: {
// Assure the last contract if the most modern
"_id": 1,
"contracts.subscriptionDate": 1
}
Шаг 5: $ группа
Вот волшебство: добавьте новые поля с расчетом, используя все контракты на подписку (теперь каждый «контракт» находится в своем собственном документе, а не в массиве)
Мне нужно добавить “subscription”
, потому что мне нужно спроецировать его как ответ.
$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: $ project
Здесь я рассчитываю другие статусы из данных подписки (не контрактов)
$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 ] }
}
Шаг 7: $ project
И, наконец, я объединяю все статусы в одном “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'
]}
]}
]}
]}
]}
]}
]
}
}
TODO
Может быть, вы можете улучшить мой поток:
- Вместо конечного объекта
subscription
и status
можно ли переместить все данные из объекта subscription
в корень (сопровождаемый вычисленным полем status
)?
- Видите ли вы другой лучший способ вычисления этого окончательного поля
status
, вместо $group
и двух $project
?
Спасибо за любые улучшения, которые вы можете предложить!