【问题标题】:Ignore null values for $last in MongoDB aggregation在 MongoDB 聚合中忽略 $last 的空值
【发布时间】:2016-10-20 03:57:54
【问题描述】:

“帐户”集合中有文档。某些字段可能未声明。看看这个。

{ _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。 我想,我应该在$group之前使用$project,如果当前字段为空,我如何保留最后一个文档的值?

【问题讨论】:

  • 我认为这是一项需要将​​数据读取为您选择的语言的任务。任何数据框库(如 python 中的pandas)都可以轻松处理这个问题

标签: mongodb aggregation-framework mongodb-aggregation


【解决方案1】:

解决方案
以下管道应为您提供所需的结果

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 阶段和第 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
    }
}

在第四阶段,字段的最后一个值和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
}

最后阶段只是聚合每个产品的这些文档。

【讨论】:

    【解决方案2】:

    我目前的解决方案包括三个查询。

    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()
    })
    

    【讨论】:

      猜你喜欢
      • 2019-04-30
      • 1970-01-01
      • 2021-05-28
      • 2021-11-20
      • 2015-12-10
      • 1970-01-01
      • 2022-10-04
      • 2011-10-26
      • 2019-09-23
      相关资源
      最近更新 更多