Moongoose 聚合 $match 与 id 不匹配

问题描述 投票:0回答:6

我想按 ID(

56e641d4864e5b780bb992c6
56e65504a323ee0812e511f2
)显示产品,并显示减去折扣后的价格(如果有)。

我可以使用聚合来计算最终价格,但这会返回集合中的所有文档,如何使其仅返回匹配的ID

"_id" : ObjectId("56e641d4864e5b780bb992c6"), 
"title" : "Keyboard", 
"discount" : NumberInt(10),
"price" : NumberInt(1000)

"_id" : ObjectId("56e65504a323ee0812e511f2"), 
"title" : "Mouse", 
"discount" : NumberInt(0),
"price" : NumberInt(1000)

"_id" : ObjectId("56d90714a48d2eb40cc601a5"), 
"title" : "Speaker", 
"discount" : NumberInt(10),
"price" : NumberInt(1000)

这是我的询问

productModel.aggregate([
        {
            $project: {
                title   : 1,
                price: {
                    $cond: {
                        if: {$gt: ["$discount", 0]}, then: {$subtract: ["$price", {$divide: [{$multiply: ["$price", "$discount"]}, 100]}]}, else: "$price"
                    }

                }
            }
        }
    ], function(err, docs){
        if (err){
            console.log(err)
        }else{
            console.log(docs)
        }
    })

如果我添加这个

$in
查询,它会返回空数组

productModel.aggregate([
            {
                $match: {_id: {$in: ids}}
            },
            {
                $project: {
                    title   : 1,
                    price: {
                        $cond: {
                            if: {$gt: ["$discount", 0]}, then: {$subtract: ["$price", {$divide: [{$multiply: ["$price", "$discount"]}, 100]}]}, else: "$price"
                    }

                }
            }
        }
    ], function(err, docs){
        if (err){
            console.log(err)
        }else{
            console.log(docs)
        }
    })
mongodb mongoose mongodb-query aggregation-framework
6个回答
120
投票

您的

ids
变量将由“字符串”构成,而不是
ObjectId
值。

Mongoose 在常规查询中将

ObjectId
的字符串值“自动转换”为正确的类型,但这不会在聚合管道中发生,如问题 #1399 中所述。

相反,您必须进行正确的转换才能手动键入:

ids = ids.map(function(el) { return mongoose.Types.ObjectId(el) })

然后您可以在管道阶段使用它们:

{ "$match": { "_id": { "$in": ids } } }

原因是因为聚合管道“通常”会改变文档结构,因此 mongoose 不会假设“模式”适用于任何给定管道阶段的文档。

有争议的是,“第一个”管道阶段当它是

$match
阶段时应该执行此操作,因为实际上文档没有更改。但现在情况并非如此。

任何可能是“字符串”或至少不是正确的 BSON 类型的值都需要手动转换才能匹配。


18
投票
  1. 在 mongoose 中,它与 find({_id:'606c1ceb362b366a841171dc'}) 配合得很好

  2. 但是在使用聚合函数时,我们必须使用 mongoose 对象将 _id 转换为对象,例如。

$match: { "_id": mongoose.Types.ObjectId("606c1ceb362b366a841171dc") }

这会很好用。


9
投票

您只需将您的 id 转换为

 let id = mongoose.Types.ObjectId(req.query.id);

然后匹配

 { $match: { _id: id } },

4
投票

而不是:

$match: { _id: "6230415bf48824667a417d56" }

用途:

$match: { _id: ObjectId("6230415bf48824667a417d56") }

0
投票

用这个

$match: { $in : [ {_id: mongoose.Types.ObjectId("56e641d4864e5b780bb992c6 ")}, {_id: mongoose.Types.ObjectId("56e65504a323ee0812e511f2")}] }

因为 Mongoose 在常规查询中将 ObjectId 的字符串值自动转换为正确的类型,但在聚合管道中不会发生这种情况。所以我们需要在管道查询中定义 ObjectId 强制转换。


0
投票

使用 mongoose 8+ 和 typescript,我使用

let newId = new mongoose.mongo.ObjectId(id);

然后比赛阶段是这样的

"$match" : {
    "_id" : newId
}
© www.soinside.com 2019 - 2024. All rights reserved.