【问题标题】:how to use projections for nested arrays in mongo如何在 mongo 中使用嵌套数组的投影
【发布时间】:2015-07-22 17:16:08
【问题描述】:

我的架构如下所示:

new Schema (
users:[userSchema]
}

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

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

我希望能够找到一个我知道其 ID 的特定部门,然后只返回该部门。我无法弄清楚如何为此使用投影。我尝试了以下几种变体:

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

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

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

【问题讨论】:

    标签: mongodb mongoose mongodb-query aggregation-framework


    【解决方案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) {
    
      }
    );
    

    这将“剥离”任何不匹配的元素和任何生成的“空”数组,因为其中没有匹配的元素。只要包含的元素在它们的组合属性中都是“唯一的”,它通常是安全的。

    它也非常快,由于仅包含 $match$project 阶段,因此与标准 .find() 操作一样快。这基本上就是.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 的所有使用对于性能来说都是很糟糕的,您最好像现在一样简单地删除应用程序代码中不需要的项目。

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

    【讨论】:

    • 谢谢 - 这很有教育意义!我将开始阅读聚合框架
    【解决方案2】:

    我有这个收藏

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

    【讨论】:

    • 我不认为这是类似的 - 你必须为数组使用 $itemMatch
    猜你喜欢
    • 2015-05-12
    • 1970-01-01
    • 1970-01-01
    • 2021-12-16
    • 1970-01-01
    • 1970-01-01
    • 2017-01-01
    • 1970-01-01
    • 2016-02-14
    相关资源
    最近更新 更多