如何在mongoDB中展平双数组?

时间:2015-01-01 22:43:49

标签: ruby arrays mongodb

我的mongoDB文档中的某些字段如下所示:

{
...
Countries: [["Spain", "France"]]
...
}

或者这个:

{
...
Countries: [["Spain"],["Russia", "Egypt"]]
...
}

我想要做的是将[["Spain", "France"]]变为["Spain", "France"],将[["Spain"],["Russia", "Egypt"]]变为["Spain", "Russia", "Egypt"],类似于在Ruby中使用flatten方法。

有没有办法在mongoDB中展平数组?我需要在整个集合中的所有文档中展平数组,而不仅仅是单个文档,如果这很重要,那么数组中的值及其数量会因文档而异。

我也使用Ruby作为mongo的驱动程序,因此使用Ruby驱动程序的方法对我也很有用。

4 个答案:

答案 0 :(得分:3)

试试这个:

db.test2.aggregate([
   {"$unwind" : "$Countries"},
   {"$unwind" : "$Countries"},
   {$group : { _id : '$_id', Countries: { $addToSet: "$Countries" }}},
]).result

答案 1 :(得分:3)

您需要执行具有两个unwind阶段和一个group阶段的聚合操作。基本规则是你放松巢深度的次数。这里的嵌套水平是2,所以我们放松两次。

 collection.aggregate([
 {$unwind => "$Countries"},
 {$unwind => "$Countries"},
 {$group => {"_id":"$_id","Countries":{$push => "$Countries"}}}
 ])

第一个$unwind阶段产生结果:

{
        "_id" : ObjectId("54a32e0fc2eaf05fc77a5ea4"),
        "Countries" : [
                "Spain",
                "France"
        ]
}
{
        "_id" : ObjectId("54a32e4ec2eaf05fc77a5ea5"),
        "Countries" : [
                "Spain"
        ]
}
{
        "_id" : ObjectId("54a32e4ec2eaf05fc77a5ea5"),
        "Countries" : [
                "Russia",
                "Egypt"
        ]
}

第二个$unwind阶段进一步展平了Countries数组:

{ "_id" : ObjectId("54a32e0fc2eaf05fc77a5ea4"), "Countries" : "Spain" }
{ "_id" : ObjectId("54a32e0fc2eaf05fc77a5ea4"), "Countries" : "France" }
{ "_id" : ObjectId("54a32e4ec2eaf05fc77a5ea5"), "Countries" : "Spain" }
{ "_id" : ObjectId("54a32e4ec2eaf05fc77a5ea5"), "Countries" : "Russia" }
{ "_id" : ObjectId("54a32e4ec2eaf05fc77a5ea5"), "Countries" : "Egypt" }

现在最后的$group阶段根据_id对记录进行分组,并将国家/地区名称累积在一个数组中。

{
        "_id" : ObjectId("54a32e4ec2eaf05fc77a5ea5"),
        "Countries" : [
                "Spain",
                "Russia",
                "Egypt"
        ]
}
{
        "_id" : ObjectId("54a32e0fc2eaf05fc77a5ea4"),
        "Countries" : [
                "Spain",
                "France"
        ]
}

如果您希望在文档中保留其他字段,则需要使用$first运算符明确指定国家/地区字段(field1,field2等)以外的字段的名称。您可以通过在$out阶段指定集合的​​名称来编写/覆盖集合。

collection.aggregate([
 {$unwind => "$Countries"},
 {$unwind => "$Countries"},
 {$group => {"_id":"$_id","Countries":{$push => "$Countries"},
             "field1":{$first => "$field1"}}},
 {$out => "collection"}
 ])

您需要明确指定字段,以便不会获得冗余的Countries字段。

您可以使用$$ROOT系统变量来存储整个文档,但这会使Countries字段变为冗余。doc之外的一个和doc内的一个

collection.aggregate([
 {$unwind => "$Countries"},
 {$unwind => "$Countries"},
 {$group => {"_id":"$_id","Countries":{$push => "$Countries"},
             "doc":{$first => "$$ROOT"}}},
 {$out => "collection"}
 ])

答案 2 :(得分:2)

在Mongo 3.4+中,您可以使用$reduce来展平2d数组。

db.collection.aggregate(
  [
    {
      $project: {
        "countries": {
          $reduce: {
            input: '$Countries',
            initialValue: [],
            in: {$concatArrays: ['$$value', '$$this']}
          }
        }
      }
    }
  ]
)

文档:https://docs.mongodb.com/manual/reference/operator/aggregation/reduce/

答案 3 :(得分:0)

您的国家/地区数据格式不佳,因此您可以考虑转换它们。这是一个在“国家/地区”字段中展平数组的脚本,并将其保存为可以在mongo shell中运行的原始文档:

function flattenArray(inArr) {
    var ret = [];
    inArr.forEach(function(arr) {
        if (arr.constructor.toString().indexOf("Array") > -1) {
           ret = ret.concat(flattenArray(arr));
        } else {
           ret.push(arr);                   
        }
    });
    return ret;
}


db.collection.find({
  'Countries': {
    '$exists': true
  }
}).forEach(function(doc){
  doc.Countries = flattenArray(doc.Countries);
  db.collection.save(doc);
});