Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/40.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Node.js Mongoose中的子文档之和_Node.js_Mongodb_Express_Mongoose_Aggregation Framework - Fatal编程技术网

Node.js Mongoose中的子文档之和

Node.js Mongoose中的子文档之和,node.js,mongodb,express,mongoose,aggregation-framework,Node.js,Mongodb,Express,Mongoose,Aggregation Framework,目前我有这个模式 var cartSchema = new Schema({ userPurchased: {type: Schema.Types.ObjectId, ref: 'users'}, products: [ { product: {type: Schema.Types.ObjectId, ref: 'products'}, size: {type: String, required: true},

目前我有这个模式

var cartSchema = new Schema({
    userPurchased: {type: Schema.Types.ObjectId, ref: 'users'},
    products: [
        {
            product: {type: Schema.Types.ObjectId, ref: 'products'},
            size: {type: String, required: true},
            quantity: {type: Number, required: true},
            subTotal: {type: Number, required: true}
        }
    ],
    totalPrice: {type: Number, default: 0, required: true}
});
数据库记录示例

{
    "_id": {
        "$oid": "586f4be94d3e481378469a08"
    },
    "userPurchased": {
        "$oid": "586dca1f5f4a7810fc890f97"
    },
    "totalPrice": 0,
    "products": [
        {
            "product": {
                "$oid": "58466e8e734d1d2b0ceeae00"
            },
            "size": "m",
            "quantity": 5,
            "subTotal": 1995,
            "_id": {
                "$oid": "586f4be94d3e481378469a09"
            }
        },
        {
            "subTotal": 1197,
            "quantity": 3,
            "size": "m",
            "product": {
                "$oid": "58466e8e734d1d2b0ceeae01"
            },
            "_id": {
                "$oid": "586f4ef64d3e481378469a0a"
            }
        }
    ],
    "__v": 0
}
有没有办法将所有小计相加并将其放入总价字段?现在我正在考虑聚合函数,但我怀疑它在这里是否是正确的方法。我想我需要同时更新查询和求和方法。有人能帮我吗

使用功能,您可以运行以下管道,该管道使用运算符获得所需结果:

const results = await Cart.aggregate([
    { "$addFields": {
        "totalPrice": {
            "$sum": "$products.subTotal"
        }
    } },
]);

console.log(JSON.stringify(results, null, 4));
相应的更新操作如下所示:

db.carts.updateMany(
   { },
   [
        { "$set": {
            "totalPrice": {
                "$sum": "$products.subTotal"
            }
        } },
    ]
)
或者,如果使用MongoDB 3.2和早期版本(仅在$group阶段提供),您可以这样做

const pipeline = [
    { "$unwind": "$products" },
    {
        "$group": {
            "_id": "$_id",
            "products": { "$push": "$products" },
            "userPurchased": { "$first": "$userPurchased" },
            "totalPrice": { "$sum": "$products.subTotal" }
        }
    }
]

Cart.aggregate(pipeline)
    .exec(function(err, results){
        if (err) throw err;
        console.log(JSON.stringify(results, null, 4));
    })

在上述管道中,第一步是操作符

{ "$unwind": "$products" }
当数据存储为数组时,这非常方便。当对列表数据字段应用“展开”操作符时,它将为应用“展开”的列表数据字段的每个元素生成新记录。它基本上使数据平坦化

这是下一个管道阶段的必要操作,即步骤,在该步骤中,您可以通过
\u id
字段对展开的文档进行分组,从而有效地将非规范化文档重新组合回其原始模式

管道操作符类似于SQL的
GROUPBY
子句。在SQL中,除非使用任何聚合函数,否则不能使用
groupby
。同样,您也必须在MongoDB中使用聚合函数(称为累加器)。你可以阅读更多关于这本书的内容

在该操作中,计算
总价并返回原始字段的逻辑是通过。通过将每组的每个
小计
值与
相加,您可以得到
总价

"totalPrice": { "$sum": "$products.subTotal }
另一个表达

"userPurchased": { "$first": "$userPurchased" },
将使用
$first
从每个组的第一个文档返回一个
用户购买的
值。从而有效地在

这里需要注意的一件事是,在执行管道时,MongoDB会将操作符相互导入。这里的“管道”具有Linux的含义:操作符的输出成为以下操作符的输入。每个操作员的结果是一个新的文档集合。因此,Mongo执行上述管道,如下所示:

collection | $unwind | $group => result

作为旁注,为了帮助理解管道或在获得意外结果时对其进行调试,请仅使用第一个管道操作符运行聚合。例如,在mongo shell中以以下方式运行聚合:

db.cart.aggregate([
    { "$unwind": "$products" }
])
检查结果以查看
产品
数组是否正确解构。如果得到预期结果,则添加下一个:

db.cart.aggregate([
    { "$unwind": "$products" },
    {
        "$group": {
            "_id": "$_id",
            "products": { "$push": "$products" },
            "userPurchased": { "$first": "$userPurchased" },
            "totalPrice": { "$sum": "$products.subTotal" }
        }
    }
])
重复这些步骤,直到进入最后一个管道步骤


如果要更新字段,则可以添加管道阶段作为最后一步。这将把聚合管道的结果文档写入同一个集合,从而从技术上更新集合

var pipeline = [
    { "$unwind": "$products" },
    {
        "$group": {
            "_id": "$_id",
            "products": { "$push": "$products" },
            "userPurchased": { "$first": "$userPurchased" },
            "totalPrice": { "$sum": "$products.subTotal" }
        }
    },
    { "$out": "cart" } // write the results to the same underlying mongo collection
]

更新

要同时执行更新和查询,您可以在聚合回调中发出
find()
调用以获取更新的json,即

Cart.aggregate(pipeline)
    .exec(function(err, results){
        if (err) throw err;
        Cart.find().exec(function(err, docs){
            if (err) return handleError(err);
            console.log(JSON.stringify(docs, null, 4));
        })
    })
    
使用承诺,您可以按照以下方式交替执行此操作:

Cart.aggregate(pipeline).exec().then(function(res)
    return Cart.find().exec();
).then(function(docs){  
    console.log(JSON.stringify(docs, null, 4));
});

我真的不能说这种方法是否比聚合更好,但如果您想使用virtuals:

cartSchema.virtual('totalPrice').get(function () {
     return this.products.map(p => p.subTotal).reduce((a, b) => a + b);
});
但请注意:

如果使用toJSON()或toObject()(或在mongoose文档上使用JSON.stringify()),默认情况下,mongoose将不包括虚拟对象。将{virtuals:true}传递给toObject()或toJSON()


如果你只是把所有产品的价格加起来,虚拟财产怎么样?我不认为有必要存储总价。我认为聚合函数或映射减少函数是最好的解决方案。MongoDB有一个网格聚合和映射redece框架。看看这个例子,你能给我举个例子吗?我觉得这是个好主意。谢谢@chridam的精彩解释。我已经在我的终端上试过了,但是totalPrice的输出是0。无法找出原因。让我们调试管道以找出原因。当您使用
$unwind
作为
db.cart.aggregate([{“$unwind”:“$products”}])
运行管道时,您是否得到了预期的结果?您的
产品
子文档中是否有一些字段没有
小计
的数值?可能是
小计
是一个字符串吗?展开的结果符合要求。GROUPBY id按预期返回两个objectId。然后我跳过了关于产品和用户购买的部分,因为我有点困惑。我包括了总价格,然后它也按预期返回。当我早些时候尝试按顺序排列时,totalPrice返回0。我认为产品和用户购买的产品之间存在冲突。我应该先计算totalPrice,然后再重新填充模式吗?你认为这有影响吗?我想我只是输入了一些错误的代码,导致它行为不端。现在它在我的终端上工作。我将尝试在我的代码中实现这一点。非常感谢您给出这个非常直观的答案!没问题:)很高兴我能帮上忙。