MongoDB高级查询 - 根据对象数组获取数据

时间:2014-09-28 22:51:56

标签: mongodb mongodb-query aggregation-framework

On Mongo 2.4.6

Collection of Users
{
    "_id" : User1,
     "orgRoles" : [ 
        {"_id" : 1, "app" : "ANGRYBIRDS", "orgId" : "CODOE"}, 
        {"_id" : 2, "app" : "ANGRYBIRDS", "orgId" : "MSDN"}
    ],
},
{
    "_id" : User2,
     "orgRoles" : [ 
        {"_id" : 1, "app" : "ANGRYBIRDS", "orgId" : "CODOE"}, 
        {"_id" : 2, "app" : "HUNGRYPIGS", "orgId" : "MSDN"}
    ],
},
{
    "_id" : User2,
     "orgRoles" : [ 
        {"_id" : 1, "app" : "ANGRYBIRDS", "orgId" : "YAHOO"}, 
        {"_id" : 2, "app" : "HUNGRYPIGS", "orgId" : "MSDN"}
    ],
}

使用如上所示的数据,我正在尝试编写一个查询来获取:

只有一个ANGRYBIRDS应用程序和ANGRYBIRDS应用程序的用户的所有ID都在CODOE组织中。

因此它会返回User2,因为它们有1个ANGRYBIRDS并且在ORG“CODOE”中而不是User1,因为它们有两个ANGRYBIRDS或User3,因为它们在“CODOE”组织中没有ANGRYBIRDS应用程序。我对mongo查询相当新,所以任何帮助都表示赞赏。

2 个答案:

答案 0 :(得分:1)

要做一些标准操作员不能立即提供的更详细的条件,那么最好的方法是使用聚合框架。这允许您进行一些处理以处理我们的条件,例如匹配的数量:

db.collection.aggregate([
     // Filter the documents that are possible matches
     { "$match": { 
         "orgRoles": { 
             "$elemMatch": { 
                 "app": "ANGRYBIRDS", "orgId": "CODOE"
             }
         }
     }},

     // De-normalize the array content
     { "$unwind": "$orgRoles" },

     // Group and count the matches
     { "$group": {
          "_id": "$_id",
          "orgRoles": { "$push": "$orgRoles" },
          "matched": { 
              "$sum": {
                  "$cond": [
                      { "$eq": ["$orgRoles.app", "ANGRYBIRDS"] },
                      1,
                      0
                  ]
              }
          }
     }},

     // Filter where matched is more that 1
     { "$match": { 
         "orgRoles": { 
             "$elemMatch": { 
                 "app": "ANGRYBIRDS", "orgId": "CODOE"
             }
         },
         "matched": 1
     }},

     // Optionally project to just keep the original fields
     { "$project": { "orgRoles": 1 } }
])

这里的主要事情发生在处理初始$match之后才返回那些至少有一个数组元素与主条件匹配的文档,然后用$unwind处理数组元素之后它们可以单独检查。

技巧是使用$sum运算符的条件$cond操作,这是一个“三元”运算符。这会将数组中的“howMany”匹配评估为“ANGRYBIRDS”字符串。在此之后,您再次 $match ,以便“过滤”匹配计数超过一个的任何文档。仍然留下其他条件,但这确实没有必要。

仅仅为了记录,使用$where子句的JavaScript评估也可以做到这一点,但由于它可能在处理时效率不高:

db.collection.find({
    "orgRoles": {
        "$elemMatch": {
            "app": "ANGRYBIRDS", "orgId": "CODOE"
        }
    },
    "$where": function() {
        var orgs = this.orgRoles.filter(function(el) {
            return el.app == "ANGRYBIRDS";
        });
        return ( orgs.length == 1 );
    }
})

答案 1 :(得分:0)

使用aggregation pipeline执行此操作的一种方法是:

db.users.aggregate([

// Match the documents with app being "ANGRYBIRDS" and orgID being "CODE"
// Note that this step filters out most of the documents and is good to have
// at the start of the pipeline, moreover it can make use of indexes, if
// used at the beginning of the aggregation pipeline.
{
    $match : {
        "orgRoles.app" : "ANGRYBIRDS",
        "orgRoles.orgId" : "CODOE"
    }
},

// unwind the elements in the orgRoles array
{
    $unwind : "$orgRoles"
},

// group by userid and app
{
    $group : {
        "_id" : {
            "id" : "$_id",
            "app" : "$orgRoles.app"
        },
        // take the id and app of the first document in each group, since all
        // the
        // other documents in the group will have the same values.
        "id" : {
            $first : "$_id"
        },
        "app" : {
            $first : "$orgRoles.app"
        },
        // orgId can be different, so form an array for each group.
        "orgId" : {
            $push : {
                "id" : "$orgRoles.orgId"
            }
        },
        // count the number of documents in each group.
        "count" : {
            $sum : 1
        }
    }
},
// find the matching group
{
    $match : {
        "count" : 1,
        "app" : "ANGRYBIRDS",
        "orgId" : {
            $elemMatch : {
                "id" : "CODOE"
            }
        }
    }
},

// project only the userid
{
    $project : {
        "id" : 1,
        "_id" : 0
    }
} ]);

编辑:已删除映射聚合结果,因为问题需要v2.4.6中的解决方案,并且根据文档。

  

在2.6版中更改:db.collection.aggregate()方法返回一个游标,可以返回任何大小的结果集。之前的版本   将所有结果返回到单个文档中,结果集为   受限制为16兆字节。