【问题标题】:How can I flatten double arrays in mongoDB?如何在 mongoDB 中展平双数组?
【发布时间】:2015-01-01 22:43:49
【问题描述】:

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

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

或者这个:

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

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

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

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

【问题讨论】:

    标签: ruby arrays mongodb


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

      您需要使用两个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"}
       ])
      

      【讨论】:

      • 我比你早 3 秒写出结果 ;),这很有趣。无论如何+1
      • @Disposer 是的 - 你打败了我。花时间格式化结果:),为您的聚合+1。 :)
      • 谢谢。那我能再澄清两个问题吗?我注意到这个组运算符只返回指定的字段(id 和国家)。是否可以在不手动指定每个字段的情况下包含所有其他字段。因为还有很多其他字段,并且数据库还没有优化,甚至只有极少数文档共享的唯一字段,并且很难搜索和指定所有这些字段。其次,也许是愚蠢的问题:我如何用聚合输出覆盖原始集合?
      • @AntonDinoMois,您应该明确指定所有其他字段。您可以制作字段列表并提出统一的结构,将必填字段和可选字段分开并进行条目。那将是我的建议。您需要使用管道的 $out 阶段来覆盖集合。请查看我的更新答案。
      【解决方案3】:

      您的国家/地区数据格式不正确,因此您可以考虑转换它们。这是一个脚本,用于展平国家字段中的数组并将其保存为可以在 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);
      });
      

      【讨论】:

        【解决方案4】:

        试试这个:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-05-21
          • 2011-12-14
          • 2019-11-16
          • 2017-05-31
          • 2019-04-17
          相关资源
          最近更新 更多