需要使用mongodb聚合查询对从另一个集合连接的多个字段进行明确计数

时间:2017-10-05 16:55:25

标签: mongodb mongodb-query aggregation-framework distinct

我正在尝试使用mongodb聚合查询来连接($ lookup)两个集合,然后对连接数组中的所有唯一值进行非常计数。

所以我的两个系列看起来像这样: 事件 -

{
    "_id" : "1",
    "name" : "event1",
    "objectsIds" : [ "1", "2", "3" ],
}

物件

{
    "_id" : "1",
    "name" : "object1",
    "metaDataMap" : { 
                         "SOURCE" : ["ABC", "DEF"],
                         "DESTINATION" : ["XYZ", "PDQ"],
                         "TYPE" : []
                    }
},
{
    "_id" : "2",
    "name" : "object2",
    "metaDataMap" : { 
                         "SOURCE" : ["RST", "LNE"],
                         "TYPE" : ["text"]
                    }
},
{
    "_id" : "3",
    "name" : "object3",
    "metaDataMap" : { 
                         "SOURCE" : ["NOP"],
                         "DESTINATION" : ["PHI", "NYC"],
                         "TYPE" : ["video"]
                    }
}

我想要出来的是当我在事件_id = 1上进行$ match时我想加入metaDataMap然后将所有键重新计算如下: 计数事件_id = 1

SOURCE : 5
DESTINATION: 4
TYPE: 2

到目前为止我所拥有的是:

db.events.aggregate([
 {$match: {"_id" : id}}
,{$lookup: {"from" : "objects",
            "localField" : "objectsIds",
            "foreignField" : "_id",
            "as" : "objectResults"}}
,{$project: {x: {$objectToArray: "$objectResults.metaDataMap"}}}
,{$unwind: "$x"}
,{$match: {"x.k": {$ne: "_id"}}}
,{$group: {_id: "$x.k", y: {$addToSet: "$x.v"}}}
,{$addFields: {size: {"$size":"$y"}} }
]);

这会失败,因为$ objectResults.metaDataMap不是一个对象,而是一个数组。关于如何解决这个或不同的方式来做我想做的任何建议? 此外,我不一定知道metaDataMap数组中的字段(键)。而且我不想计算或包含地图中可能存在或可能不存在的字段。

2 个答案:

答案 0 :(得分:1)

这应该可以解决问题。我在你的输入集上对它进行了测试,并故意添加了一些重叠值,如NYC,显示在多个DESTINATION中,以确保它被删除(即要求的明显计数)。 为了好玩,请注释掉所有阶段,然后自上而下将其注释掉以查看管道每个阶段的效果。

var id = "1";

c=db.foo.aggregate([
// Find a thing:
{$match: {"_id" : id}}

// Do the lookup into the objects collection:
,{$lookup: {"from" : "foo2",
            "localField" : "objectsIds",
            "foreignField" : "_id",
            "as" : "objectResults"}}

// OK, so we've got a bunch of extra material now.  Let's
// get down to just the metaDataMap:
,{$project: {x: "$objectResults.metaDataMap"}}
,{$unwind: "$x"}
,{$project: {"_id":0}}

// Use $objectToArray to get all the field names dynamically:
// Replace the old x with new x (don't need the old one):
,{$project: {x: {$objectToArray: "$x"}}}
,{$unwind: "$x"}

// Collect unique field names.  Interesting note: the values
// here are ARRAYS, not scalars, so $push is creating an
// array of arrays:
,{$group: {_id: "$x.k", tmp: {$push: "$x.v"}}}

// Almost there!  We have to turn the array of array (of string)
// into a single array which we'll subsequently dedupe.  We will
// overwrite the old tmp with a new one, too:
,{$addFields: {tmp: {$reduce:{
    input: "$tmp",
    initialValue:[],
    in:{$concatArrays: [ "$$value", "$$this"]}
        }}
    }}

// Now just unwind and regroup using the addToSet operator
// to dedupe the list:
,{$unwind: "$tmp"}
,{$group: {_id: "$_id", uniqueVals: {$addToSet: "$tmp"}}}

// Add size for good measure:
,{$addFields: {size: {"$size":"$uniqueVals"}} }
          ]);

答案 1 :(得分:0)

我能够使用以下查询生成所需的结果。

db.events.aggregate(
   [  
      {$match: {"_id" : id}} ,
      {$lookup: {
          "from" : "objects",
          "localField" : "objectsIds",
          "foreignField" : "_id",
          "as" : "objectResults"
      }},
      {$unwind: "$objectResults"},
      {$project:{"A":"$objectResults.metaDataMap"}},
      {$unwind: {path: "$A.SOURCE", preserveNullAndEmptyArrays: true}},
      {$unwind:{ path: "$A.DESTINATION", preserveNullAndEmptyArrays: true}},
      {$unwind:{ path: "$A.TYPE", preserveNullAndEmptyArrays: true}},
      {$group:{"_id":"$_id","SOURCE":{$addToSet:"$A.SOURCE"},"DESTINATION":{$addToSet:"$A.DESTINATION"},"TYPE":{$addToSet:"$A.TYPE"}}},
      {$addFields: {"SOURCE":{$size:"$SOURCE"},"DESTINATION":{$size:"$DESTINATION"},"TYPE":{$size:"$TYPE"}}},
      {$project:{"_id":0}}]
).pretty()

更新了动态字段的查询。

db.events.aggregate([  
{
$match: {"_id" : id}} ,
{$lookup: {"from" : "objects","localField" : "objectsIds","foreignField" : "_id","as" : "objectResults"}},
{$unwind: "$objectResults"},
{$project:{"A":"$objectResults.metaDataMap"}},
{$project: {x: {$objectToArray: "$A"}}},
{$unwind: "$x"},
{$match: {"x.k": {$ne: "_id"}}},
{$unwind:"$x.v"},
{$group: {_id: "$x.k", y: {$addToSet: "$x.v"}}},
{$project:{"size":{$size:"$y"}}}]
).pretty()