Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ruby-on-rails-3/4.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_Mongoose - Fatal编程技术网

Node.js 如何在mongoose聚合中按子文档字段进行筛选?

Node.js 如何在mongoose聚合中按子文档字段进行筛选?,node.js,mongodb,mongoose,Node.js,Mongodb,Mongoose,我只想为付款单过滤特定用户的费用。以下是我的模式: var PaymentOrderSchema = new Schema({ name: {type: String, required: true}, description: {type: String}, amount: {type: Number}, charges: [{type: Schema.Types.ObjectId, ref: 'Charge'}], }, var ChargeSchema = new Sch

我只想为付款单过滤特定用户的费用。以下是我的模式:

var PaymentOrderSchema = new Schema({
  name: {type: String, required: true},
  description: {type: String},
  amount: {type: Number},
  charges: [{type: Schema.Types.ObjectId, ref: 'Charge'}],
},

var ChargeSchema = new Schema({
  amount: {type: Number},
  user: {type: Schema.Types.ObjectId, ref: 'User'},
  status: {type: String},
  description: {type: String},
  creation_date:{type: Date}
}
我正在尝试使用聚合,但无法过滤费用。当我使用$ne而不是$eq时,它确实会将费用返回给我,因此我知道我走的是正确的道路。这是我的实际代码:

const paymentOrder = await PaymentOrder.aggregate([
  {$match: {'_id': mongoose.Types.ObjectId(req.query.payment_order_id)}},
  {$project: {
    charges: {$filter: {
      input: '$charges',
      as: 'charge',
      cond: {$eq: ['$$charge.user._id', mongoose.Types.ObjectId(req.user._id)]}
    }}
  }}
]);

谢谢

如果我正确理解您的需求,那么我们可以将$lookup与管道一起使用

像这样的

db.paymentOrder.aggregate([
  {
    $match: {
      _id: ObjectId("5a934e000102030405000003") // replace this hard-coded objectId with mongoose.Types.ObjectId(req.query.payment_order_id)
    }
  },
  {
    $lookup: {
      from: "charge",
      let: {
        chargeIds: "$charges"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $eq: [
                "$user",
                "userId1" // replace this with mongoose.Types.ObjectId(req.user._id)
              ]
            }
          }
        }
      ],
      as: "charges"
    }
  }
])
检查这个


如果我正确理解您的需求,那么我们可以使用$lookup和pipeline

像这样的

db.paymentOrder.aggregate([
  {
    $match: {
      _id: ObjectId("5a934e000102030405000003") // replace this hard-coded objectId with mongoose.Types.ObjectId(req.query.payment_order_id)
    }
  },
  {
    $lookup: {
      from: "charge",
      let: {
        chargeIds: "$charges"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $eq: [
                "$user",
                "userId1" // replace this with mongoose.Types.ObjectId(req.user._id)
              ]
            }
          }
        }
      ],
      as: "charges"
    }
  }
])
检查这个


希望它有助于在数据库服务器上运行聚合,同时客户端的驱动程序会扩展引用,因此在管道中实际看到的值是

DBRef("User",ObjectId(...),"DatabaseName")
它可以像对象一样被访问

{"$ref":"User", "$id":ObjectId(...), "$db":"DatabaseName"}
但这又引发了一个问题:字段名不允许以
$
开头,因此这将引发一个错误:

{$eq:["$$charge.user.$id",mongoose.Types.ObjectId(req.user._id)]}
因此,您可以跳过聚合中的该步骤并将文档带回mongoose进行填充,或者如果您只需要在
$id
上进行匹配,则可以使用
$objectToArray
分解DBRef,以便匹配:

  {$match: {'_id': mongoose.Types.ObjectId(req.query.payment_order_id)}},
  {$unwind: "$charges"},
  {$addFields: {charges:{$objectToArray:"$charges"}}},
  {$match: {charges:{
         $elemMatch:{k:"$id",v:mongoose.Types.ObjectId(req.user._id}
  }}},
  {$group:{_id:"$_id", charges:{$push:{$arrayToObject("$charges"}}}}}
这将返回可由驱动程序扩展的DBRefs,但这与
费用
文档的
\u id
相匹配,这可能不是您想要的

但是,一旦您拥有收费凭证的
\u id
,您就可以使用
$lookup
并对其进行过滤,但是我们还必须处理
用户作为DBref的问题:

  {$match: {'_id': mongoose.Types.ObjectId(req.query.payment_order_id)}},
  {$unwind: "$charges"},
  {$addFields: {chargeId:{$filter:{
         input:$objectToArray:"$charges"
         cond:{$eq:["$$this.k","$id"]}
  }}}}, 
  {$lookup:{
       from:"charge",
       localField:"$chargeId.v",
       foreignField:"$_id",
       as: charges
  }},
  {$unwind:"$charges"},
  {$addField:{chargeUser:{$filter:{
        input:{$objectToArray:"$charges.user"},
        cond:{$eq:["$$this.k","$id"]}
  }}}},
  {$match: {"chargeUser.v":mongoose.Types.ObjectId(req.user._id)}},  
  {$project:{
        chargeUser:0, 
        chargeId:0
  },
  {$group:{
         _id:"$_id", 
         document:{$first:"$$ROOT"}, 
         charges:{$push:"$charges"}
  }},
  {$addFields:{"document.charges":"$charges"}},
  {$replaceRoot:{newRoot:"$document"}}

聚合在数据库服务器上运行,而引用由客户端的驱动程序展开,因此在管道中实际看到的值是

DBRef("User",ObjectId(...),"DatabaseName")
它可以像对象一样被访问

{"$ref":"User", "$id":ObjectId(...), "$db":"DatabaseName"}
但这又引发了一个问题:字段名不允许以
$
开头,因此这将引发一个错误:

{$eq:["$$charge.user.$id",mongoose.Types.ObjectId(req.user._id)]}
因此,您可以跳过聚合中的该步骤并将文档带回mongoose进行填充,或者如果您只需要在
$id
上进行匹配,则可以使用
$objectToArray
分解DBRef,以便匹配:

  {$match: {'_id': mongoose.Types.ObjectId(req.query.payment_order_id)}},
  {$unwind: "$charges"},
  {$addFields: {charges:{$objectToArray:"$charges"}}},
  {$match: {charges:{
         $elemMatch:{k:"$id",v:mongoose.Types.ObjectId(req.user._id}
  }}},
  {$group:{_id:"$_id", charges:{$push:{$arrayToObject("$charges"}}}}}
这将返回可由驱动程序扩展的DBRefs,但这与
费用
文档的
\u id
相匹配,这可能不是您想要的

但是,一旦您拥有收费凭证的
\u id
,您就可以使用
$lookup
并对其进行过滤,但是我们还必须处理
用户作为DBref的问题:

  {$match: {'_id': mongoose.Types.ObjectId(req.query.payment_order_id)}},
  {$unwind: "$charges"},
  {$addFields: {chargeId:{$filter:{
         input:$objectToArray:"$charges"
         cond:{$eq:["$$this.k","$id"]}
  }}}}, 
  {$lookup:{
       from:"charge",
       localField:"$chargeId.v",
       foreignField:"$_id",
       as: charges
  }},
  {$unwind:"$charges"},
  {$addField:{chargeUser:{$filter:{
        input:{$objectToArray:"$charges.user"},
        cond:{$eq:["$$this.k","$id"]}
  }}}},
  {$match: {"chargeUser.v":mongoose.Types.ObjectId(req.user._id)}},  
  {$project:{
        chargeUser:0, 
        chargeId:0
  },
  {$group:{
         _id:"$_id", 
         document:{$first:"$$ROOT"}, 
         charges:{$push:"$charges"}
  }},
  {$addFields:{"document.charges":"$charges"}},
  {$replaceRoot:{newRoot:"$document"}}

如果我当时理解正确的话,它的工作原理就不一样了,您需要使用mongoose的
.populate()
或MongoDB的
$lookup
首先将各自的
费用
文档放入
费用
数组,然后再次填充
用户
集合以获取用户文档,然后根据条件对其进行筛选或获取用户文档..如果我当时理解正确,它的工作原理与此不同,您需要使用mongoose的
.populate()
或MongoDB的
$lookup
首先将各自的
费用
文档放入
费用
数组,然后再次填充
用户
集合以获取用户文档,然后根据条件对其进行筛选或获取用户文档..谢谢,这正是我要找的。这避免了在每个模式中都必须有一个双重引用!谢谢,这正是我想要的。这避免了在每个模式中都必须有一个双重引用!