按条件获取文档数组计数,然后加入另一个集合

时间:2017-06-29 07:00:11

标签: mongodb mongodb-query aggregation-framework

我有两个样本集

  1. Govenor Info
  2. 地区信息
  3. 我想:

    1. 显示区域信息
    2. 统计地区和城市
    3. 加入govenor info
    4. 我在问题按钮上尝试了3种方法。每个人都需要我的一个要求。希望有人能填补它。

      请注意。你可以说为什么不在相同的集合regionInfo中的govenor信息。我有理由说“govenor”集合也与其他集合相关联。 所以,我需要单独的“govenor”系列。

      govenor

      [{
          "_id" : "007",
          "name" : {
              "fname" : "Joe",
              "lname" : "McDee",
                  }
      },
      {
          "_id" : "008",
          "name" : {
              "fname" : "Martin",
              "lname" : "Neilwel",
                   }
      }]
      

      regionInfo

      {
        "country_id" : 328,
        "country_name" : "UK",
       }
        "regions" : [{
      
          "region_id" : 001,
          "region_name" : "west midlands",
          "govenor_id" : "007"
        },
                      {
          "region_id" : 002,
          "region_name" : "east midlands"
          "govenor_id" : "008"
                      }
        ],
         "cities" : [{
            "city_id" : 1,
            "region_id" : 001,
            "city_name" : "Birmingham"
          }, {
            "city_id" : 2,
            "region_id" : 001,
            "city_name" : "coventry"
          }, {
            "city_id" : 3,
            "region_id" : 001,
            "city_name" : "warsaw"
          },  {
            "city_id" : 4,
            "region_id" : 002,
            "city_name" : "dudley"
          },
           {
            "city_id" : 5,
            "region_id" : 002,
            "city_name" : "blabla"
          }]
      }
      

      期望的结果:

      { 
        "_id" : { "country_name" : "UK", "number_of_region" : 2 },
        "regions" : [
          {
            "region_name" : "west midlands",
            "govenor.fname" : "Joe",
            "cities_in_region" : 3
          },
          {
            "region_name" : "east midlands",
            "govenor.fname" : "Martin",
            "cities_in_region" : 2 
          }
        ]
      }
      

      我找不到这个城市的数量:

      db.collection('regionInfo').aggregate( [ 
        { '$unwind': "regionInfo" },  
        { '$lookup':{
          from: "govenor",
          localField: 'regionInfo.govenor_id',
          foreignField: "_id",
          as: "Gjoin"
        }},
        { '$lookup': {
          from: "regionInfo",
          localField: 'regions.region_id',
          foreignField: "cities.gevenor_id",
          as: "govjoin"
        }},
        { '$project' : {
          R_id: "$regionInfo.region_name",
          govenor_fname:"$Gjoin..name.fname",
          govenor_lname:"$Gjoin.name.lname",
        }}
      ])
      

      如果我使用mapReduce,我可以计算城市(格式不正确)而不加入govenor信息

          map = function(){
          for (var index = 0; index < this.regions.length; ++index) {
                  var auth = this.regions[ index ];
                  emit( auth._id ,{cmt : {'this.cities.region_id' :'this.regions.region_id'}} );
              }}
          reduce = function(auth, counters) {
              count = 0;
              for (var index = 0; index < counters.length; ++index) {
                  count += counters[index];
              }
              return count;
          }
      

      这是Neil Lunn的My Fav Method。但我需要实施加入govenor信息:

      db.collection.find().map(doc => ({ 
        _id: { 
          country_name: doc.country_name,
          number_of_regions: doc.regions.length
        }, 
        regions: doc.regions.map( r => ({ 
          region_name: r.region_name,
          cities_in_region: doc.cities.filter( c => 
            c.region_id === r.region_id ).length })
         )
      

      }))

1 个答案:

答案 0 :(得分:1)

为了做到这一点,你基本上想要遵循我首先给你$project阶段的基本“重塑”声明的模式。然后在$unwind之前执行"regions"数组上的$lookup,因为您需要这样做,因为$lookup无法将输出“放在”数组中:

db.getCollection('regionInfo').aggregate([
  { "$project": {
    "_id": {
      "country_name": "$country_name",
      "number_of_region": { "$size": "$regions" }
    },
    "regions": {
      "$map": {
        "input": "$regions",
        "as": "r",
        "in": {
          "region_name": "$$r.region_name",
          "govenor_fname": "$$r.govenor_id",
          "cities_in_region": {
            "$size": {
              "$filter": {
                "input": "$cities",
                "as": "c",
                "cond": { "$eq": [ "$$c.region_id", "$$r.region_id" ] }
              }   
            }
          }
        }
      } 
    }  
  }},
  { "$unwind": "$regions" },
  { "$lookup": {
    "from": "govenor",
    "localField": "regions.govenor_fname",
    "foreignField": "_id",
    "as": "regions.govenor_fname"
  }},
  { "$addFields": {
    "regions.govenor_fname": {
        "$arrayElemAt": [
          "$regions.govenor_fname.name.fname",
          0
        ]
      }
  }},
  { "$group": {
    "_id": "$_id",
    "regions": { "$push": "$regions" }    
  }}
])

对于第一部分,它全部发生在$project内,因为我们只是改变文档的形状,就像在JavaScript示例中演示的那样简单地重塑事物。

您想要的“计数”实际上是通过从文档中的各个数组中获取$size来实现的。第一个是包含"regions"数组的简单“大小”。

每个“区域”内的“城市”的第二个“计数”是通过在"cities"数组上使用$filter来完成的,以匹配当前的“区域”。然后,简单地测试过滤后的列表的“大小”。

由于"regions"是一个“数组”,我们想要加入另一个集合,我们需要先对它进行“去规范化”。 $unwind通过为每个数组成员创建文档副本来执行此操作。

然后$lookup可以引用我们实际重命名的提供的"govenor_id"到早期阶段的目标字段。我们在那里重命名,因此结果文档中的顺序不会改变。

由于$lookup的结果也是一个数组(因为它可能匹配很多),我们知道它在这里是一个1:1关系,所以我们只需从第一个数组索引中获取$arrayElemAt的结果。

$addFields管道阶段发生这种情况,我们只能“指定'新'字段而不是所有字段。这是在MongoDB 3.4中添加的,但是在早期版本中(3.2因为我们使用$lookup),我们需要使用$project明确指定所有字段。

现在剩下的就是在$unwind之前的状态下将文档重新组合在一起,因此我们使用$group管道阶段以便按文档“分组”{ {1}}和$push将所有_id数据恢复为数组形式。

然后输出:

"regions"

还修复了您的数据,因为它在多个地方都有格式错误。 { "_id" : { "country_name" : "UK", "number_of_region" : 2 }, "regions" : [ { "region_name" : "west midlands", "govenor_fname" : "Joe", "cities_in_region" : 3 }, { "region_name" : "east midlands", "govenor_fname" : "Martin", "cities_in_region" : 2 } ] } 集合保持不变:

<强> regionInfo

"govenor"