如何在mongo中使用嵌套数组的投影

时间:2015-07-22 08:24:16

标签: mongodb mongoose mongodb-query aggregation-framework

我的架构如下所示:

new Schema (
users:[userSchema]
}

userSchema = new Schema ({
email: String,
companies: [companySchema]
})

companySchema = new Schema ({
dept:{
id: String,
desc: String
},
rooms: {
name: String,
location: String
});

我希望能够找到一个我知道其身份的特定部门并返回部门。我无法弄清楚如何使用投影。我尝试了以下几种变体:

Model.findOne({“users.companies.dept.id”: “10},{users:{$elemMatch:{dept:{$elemMatch:{id:”10”}}}}});

所有这些选择整个用户,而不仅仅是部门。

我的解决方法是使用findOne()查找用户并使用某些节点代码获取部门。任何见解都表示赞赏

2 个答案:

答案 0 :(得分:1)

MongoDB中的投影只能在匹配数组时在“顶层”数组级别工作。要在“服务器上”执行更多操作,您需要使用“聚合框架”,这比标准.find()查询能够执行此操作更擅长:

Model.aggregate(
  [
    // Match the document(s) that contain this match
    { "$match": { "users.companies.dept.id": "10" } },

    { "$project": {
      "users": {
        "$setDiffernce": [
          { "$map": {
            "input": "$users",
            "as": "user",
            "in": {
              "$setDifference": [
                { "$map": {
                  "input": "$$user.companies",
                  "as": "comp",
                  "in": {
                    "$cond": [
                      { "$eq": [ "$$comp.dept.id", "10" ] },
                      "$comp",
                      false
                    ]
                  }
                }},
                [false]
              ]
            }
          }},
          [[]]
        ]
      }    
    }}

  ],
  function(err,results) {

  }
);

由于其中没有匹配的元素,这将“剥离”任何不匹配的元素和任何产生的“空”数组。只要所包含的元素在它们的组合属性中都是“独特的”,它通常是安全的。

它也非常快,并且与标准.find()操作一样快,因为只包含$match$project阶段。这基本上是.find()所做的。因此,除了“小”额外的超额外,没有区别。当然,每场比赛从服务器返回的流量较少。

如果你的MongoDB服务器版本低于2.6而没有那些运算符,或者你的“dept.id”值在内部数组中不是唯一的,你也可以这样做。

Model.aggregate(
    [
        // Match the document(s) that contain this match
        { "$match": { "users.companies.dept.id": "10" } },

        // Unwind arrays
        { "$unwind": "$users" },
        { "$unwind": "$users.companies" },

        // Match to "filter" the array
        { "$match": { "users.companies.dept.id": "10" } },

        // Group back to company
        { "$group": {
            "_id": {
               "_id": "$_id",
               "user_id": "$user._id",
               "userEmail": "$user.email"
           },
           "companies": { "$push": "$users.companies" }
        }},

        // Now push "users" as an array
        { "$group": {
            "_id": "$_id._id",
            "users": { "$push": {
                "_id": "$_id.userId",
                "email": "$_id.userEmail",
                "companies": "$companies"
            }}
        }}
    ],
    function(err,results) {

    }
);

但是$unwind的所有使用对于性能来说都很糟糕,而且您最好只是像现在一样在应用程序代码中删除不需要的项目。

因此,如果您的服务器支持它,那么使用第一个选项来减轻您的应用和网络传输的负担。否则坚持你正在做的事情,因为它可能更快。

答案 1 :(得分:0)

我有这个集合

> db.depts.find()
{ "_id" : ObjectId("55af5cefa894779dc40208e7"), "dept" : { "id" : 2, "desc" : "test" }, "rooms" : { "name" : "room", "location" : "test2" } }

以下查询仅返回dept

> db.depts.find({'dept.id':2},{'dept':1})
{ "_id" : ObjectId("55af5cefa894779dc40208e7"), "dept" : { "id" : 2, "desc" : "test" } }

所以在moongose中,它应该是

Model.findOne({“users.companies.dept.id”: 10},{“users.companies.dept”:1, “_id”: 0})