'帐户'采集。某些字段可能未声明。看看这个。
{ _id: ObjectId(1), price: 5400, product: ObjectId(2), count: 1 }
{ _id: ObjectId(1), product: ObjectId(2), count: 0, sale: 0.2 }
{ _id: ObjectId(1), product: ObjectId(2), count: 1 }
使用聚合框架,我想要一个产品的最后价格,最后销售和计数。 目前我使用此查询
db.collection('account').aggregate([
{
$group: {
_id: '$product',
count: { $sum: '$count' },
price: { $last: '$price' },
sale: { $last: '$sale' }
}
}
], { allowDiskUse: true }).toArray()
但sale
和price
字段变为null
。
我想,我应该在$project
之前使用$group
,如果当前字段为空,如何保留最后一个文档的值?
答案 0 :(得分:2)
<强>解决方案强>
以下管道应该为您提供所需的结果
db.getCollection('account').aggregate(
[
{
$project: {
_id: '$product',
fields: [
{ name: { $literal: 'price' }, value: '$price', count: { $literal: 0 } },
{ name: { $literal: 'sale' }, value: '$sale', count: { $literal: 0 } },
{ name: { $literal: 'count' }, value: { $literal: 0 }, count: '$count' }
]
}
},
{
$unwind: {
path: '$fields'
}
},
{
$match: {
'fields.value': {
$exists: true
}
}
},
{
$group: {
_id: {
product: '$_id',
field: '$fields.name'
},
value: {
$last: '$fields.value'
},
count: {
$sum: '$fields.count'
}
}
},
{
$project: {
_id: '$_id.product',
price: {
$cond: { if: { $eq: [ '$_id.field', 'price' ] }, then: '$value', else: null }
},
sale: {
$cond: { if: { $eq: [ '$_id.field', 'sale' ] }, then: '$value', else: null }
},
count: {
$cond: { if: { $eq: [ '$_id.field', 'count' ] }, then: '$count', else: 0 }
}
}
},
{
$group: {
_id: '$_id',
price: {
$max: '$price'
},
sale: {
$max: '$sale'
},
count: {
$sum: '$count'
}
}
}
])
<强>解释强>
它首先创建一个包含每个字段元素的新数组,其中包含字段名称,字段值和计数值。
请注意,字段count
被视为特殊字段,因为它应该被累积而不是获取它的最后一个值。
所以在第一阶段文件看起来像这样:
/* 1 */
{
"_id" : "2",
"fields" : [
{
"name" : "price",
"value" : 5400,
"count" : 0.0
},
{
"name" : "sale",
"count" : 0.0
},
{
"name" : "count",
"value" : 0.0,
"count" : 1
}
]
}
/* 2 */
{
"_id" : "2",
"fields" : [
{
"name" : "price",
"count" : 0.0
},
{
"name" : "sale",
"value" : 0.2,
"count" : 0.0
},
{
"name" : "count",
"value" : 0.0,
"count" : 0
}
]
}
/* 3 */
{
"_id" : "2",
"fields" : [
{
"name" : "price",
"count" : 0.0
},
{
"name" : "sale",
"count" : 0.0
},
{
"name" : "count",
"value" : 0.0,
"count" : 1
}
]
}
然后展开数组并对其进行过滤以消除空值,因此在第2阶段&amp; 3份文件如下:
/* 1 */
{
"_id" : "2",
"fields" : {
"name" : "price",
"value" : 5400,
"count" : 0.0
}
}
/* 2 */
{
"_id" : "2",
"fields" : {
"name" : "count",
"value" : 0.0,
"count" : 1
}
}
/* 3 */
{
"_id" : "2",
"fields" : {
"name" : "sale",
"value" : 0.2,
"count" : 0.0
}
}
/* 4 */
{
"_id" : "2",
"fields" : {
"name" : "count",
"value" : 0.0,
"count" : 0
}
}
/* 5 */
{
"_id" : "2",
"fields" : {
"name" : "count",
"value" : 0.0,
"count" : 1
}
}
在第四阶段,领域&#39;建立了count
的最后一个值和总和。结果如下所示:
/* 1 */
{
"_id" : {
"product" : "2",
"field" : "sale"
},
"value" : 0.2,
"count" : 0.0
}
/* 2 */
{
"_id" : {
"product" : "2",
"field" : "count"
},
"value" : 0.0,
"count" : 2
}
/* 3 */
{
"_id" : {
"product" : "2",
"field" : "price"
},
"value" : 5400,
"count" : 0.0
}
由于这些值现在位于不同形状的单独文档中,我们需要将它们重新投射到我们最终可以分组的内容中。所以在第五阶段文件之后是这样的:
/* 1 */
{
"_id" : "2",
"count" : 0.0,
"price" : null,
"sale" : 0.2
}
/* 2 */
{
"_id" : "2",
"count" : 2,
"price" : null,
"sale" : null
}
/* 3 */
{
"_id" : "2",
"count" : 0.0,
"price" : 5400,
"sale" : null
}
最后阶段就是按照产品汇总这些文件。
答案 1 :(得分:0)
我目前的解决方案在三个查询中完成。
1. - Group by `product` and sum `count` field
- Remove any entries, where `count` is 0 and lower
- Hold `_ids` array as `product_ids`
- Hold result array as `firstArray`
2. - Match any entries, where
- `sale` is 0 and greater
- `_id` is in `product_ids`
- Group by `product` and get last `sale` field
- Hold result array as `secondArray`
3. - Match any entries, where
- `price` is 0 and greater
- `_id` is in `product_ids`
- Group by `product` and get last `price` field
- Enumerate `firstArray` and add `count` field to entries
- Enumerate `secondArray` and add `sale` field to entries
代码
let firstArray
let secondArray
let product_ids
Promise.resolve().then(function () {
return db.collection(ACCOUNT).aggregate([
{
$group: {
_id: '$product',
count: { $sum: '$count' }
}
}, {
$match: {
count: { $gt: 0 }
}
}
], { allowDiskUse: true }).toArray()
}).then(function (array) {
firstArray = array
product_ids = array.map(function (item) {
return item._id
})
return db.collection(ACCOUNT).aggregate([
{
$match: {
product: { $in: product_ids },
sale: { $gte: 0 }
}
}, {
$group: {
_id: '$product',
sale: { $last: '$sale' }
}
}
], { allowDiskUse: true }).toArray()
}).then(function (array) {
secondArray = array
return db.collection(ACCOUNT).aggregate([
{
$match: {
product: { $in: product_ids },
price: { $gte: 0 }
}
}, {
$group: {
_id: '$product',
price: { $last: '$price' }
}
}
], { allowDiskUse: true }).toArray()
}).then(function (array) {
req.data = array.map(function (item) {
let count = 0
let sale = 0
firstArray.some(function (_item) {
if (item._id.toHexString() == _item._id.toHexString()) {
count = _item.count
return true
}
return false
})
secondArray.some(function (_item) {
if (item._id.toHexString() == _item._id.toHexString()) {
sale = _item.sale
return
}
return false
})
item.count = count
item.sale = sale
return item
})
next()
})