提问者:小点点

使用聚合$lookup连接多个字段


我需要加入两个以上的字段在两个集合使用聚合$lookup.是可以加入吗?如果可能的话,请告诉我。这里我有两个收藏:

例如:"人民"集合字段"城市,州,国家"在"国家"集合字段"city_id,state_id,country_id",我想加入这三个字段在以下集合。

“人”

    {    
        "_id" : 1,
        "email" : "admin@gmail.com",
        "userId" : "AD",
        "userName" : "admin",
        "city" : 1,
        "state" : 1,
        "country" : 1  
    }

“国家”

     {
        "country_id" : 1,
        "userId" : "AD",
        "phone" : "0000000000",
        "stateinfo":[{
           "state_id" : 1,
           "state_name" : "State1"
         },{
           "state_id" : 2,
           "state_name" : "State2"
         }
         ],
        "cityinfo":[{
           "city_id" : 1,
           "city_name" : "city1"
         },{
           "city_id" : 2,
           "city_name" : "city2" 
         }
         ]
     }

共1个答案

匿名用户

考虑到所有“三个”字段都包含在一个“country”文档中,这可能比您想象的要简单得多。因此,只需通过“country\u id”执行$lookup,然后使用检索到的内容填充其他字段即可。

var pipeline = [
  { "$lookup": {
    "from": "country",
    "localField": "country",
    "foreignField": "country_id",
    "as": "country"
  }},
  { "$project": {
    "email": 1,
    "userId": 1,
    "userName": 1,
    "country": {
      "$arrayElemAt": [
        { "$filter": {
          "input": { 
            "$map": {
              "input": "$country",
              "as": "country",
              "in": {
                "country_id": "$$country.country_id",
                "userId": "$$country.userId",
                "phone": "$$country.phone",
                "stateInfo": {
                  "$arrayElemAt": [
                    { "$filter": {
                      "input": "$$country.stateInfo",
                      "as": "state",
                      "cond": { "$eq": [ "$$state.state_id", "$state" ] }
                    }},
                    0
                  ]
                },
                "cityinfo": {
                  "$arrayElemAt": [
                    { "$filter": {
                      "input": "$$country.cityinfo",
                      "as": "city",
                      "cond": { "$eq": [ "$$city.city_id", "$city" ] }
                    }},
                    0
                  ]
                }
              }
            }
          },
          "as": "country",
          "cond": { "$eq": [ "$$country.userId", "$userId" ] }
        }},
        0
      ]
    }
  }}
]

db.people.aggregate(pipeline)

这会给你一个类似的结果:

{    
  "_id" : 1,
  "email" : "admin@gmail.com",
  "userId" : "AD",
  "userName" : "admin",
  "country" : {
    "country_id" : 1,
    "userId" : "AD",
    "phone" : "0000000000",
    "stateinfo": {
       "state_id" : 1,
       "state_name" : "State1"
    },
    "cityinfo": {
       "city_id" : 1,
       "city_name" : "city1"
    }
}

因此,一旦数组通过$lookup进行匹配,就可以归结为使用$filter进行匹配,并使用$arrayElemAt从每个过滤的数组中获得第一个匹配。

由于外部数组具有“内部”数组,因此您希望对“外部”源使用$map,并将$filter应用于它的每个“内部”数组。

您可以使用$let获得更高级的功能,将“减少的”数组内容下放到返回的子文档中,然后直接引用结果属性以获得更“平坦”的响应,但是“匹配”数组元素的一般概念与上面的相同。

对于PHP结构转换:

$pipeline = array(
  array(
    '$lookup' => array(
      'from' => 'country',
      'localField' => 'country'
      'foreignField' => 'country_id',
      'as' => 'country'
    )
  )
  array(
    '$project' => array(
      'email' => 1,
      'userId' => 1,
      'userName' => 1,
      'country' => array(
        '$arrayElemAt' => array(
          array(
            '$filter' => array(
              'input' => array(
                '$map' => array(
                  'input' => '$country',
                  'as' => 'country',
                  'in' => {
                    'country_id' => '$$country.country_id',
                    'userId' => '$$country.userId',
                    'phone' => '$$country.phone',
                    'stateInfo' => array(
                      '$arrayElemAt' => array(
                        array(
                          '$filter' => array(
                            'input' => '$$country.stateInfo',
                            'as' => 'state',
                            'cond' => array( '$eq' => array( '$$state.state_id', '$state' ) )
                          )
                        ),
                        0
                      )
                    ),
                    'cityinfo' => array(
                      '$arrayElemAt' => array(
                        array(
                          '$filter' => array(
                            'input' => '$$country.cityinfo',
                            'as' => 'city',
                            'cond' => array( '$eq' => array( '$$city.city_id', '$city' ) )
                          )
                        ),
                        0
                      )
                    )
                  }
                )
              ),
              'as' => 'country',
              'cond' => array( '$eq' => array( '$$country.userId', '$userId' ) )
            )
          ),
          0
        )
      )
    )
  )
);

$people->aggregate($pipeline);

在处理JSON示例时,通常可以通过转储管道结构来检查PHP是否匹配JSON结构:

echo json_encode($pipeline, JSON_PRETTY_PRINT)

这里的最后一点是,完成$lookup后的过程非常“复杂”,即使非常有效。因此,我建议,除非有必要进一步使用这个聚合管道并实际“聚合”某些内容,否则最好在客户机代码中进行“过滤”,而不是在服务器上进行。

做同样的事情的客户端代码远没有你需要告诉聚合管道做的事情那么“迟钝”。因此,除非这个“真正”通过减少匹配的数组来节省大量带宽使用,或者事实上,如果你可以通过做另一个查询来“查找”,那么坚持用代码和/或单独的查询来做。