使用聚合$ lookup加入多个字段

时间:2016-03-29 07:18:34

标签: php mongodb mongodb-query aggregation-framework

我需要使用aggregate $ lookup加入两个集合中的两个以上的字段。有可能加入吗?如果有可能请告诉我。这里我有两个系列:

例如: "人"收藏领域"城市,州,国家" in" country"收集字段" 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 个答案:

答案 0 :(得分:2)

这可能比你想象的要简单得多,考虑到当然所有的"三"字段包含在一个"country"文档中。因此,只需按"country_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完成之后的过程非常复杂"即使非常有效率。所以我建议,除非有必要进一步采用这种聚合管道,实际上"聚合"什么东西,那么你可能会更好地做到这一点"过滤"在客户端代码而不是在服务器上执行。

执行相同操作的客户端代码远远不够"钝"而不是你需要告诉聚合管道做什么。所以除非这个"真的"通过减少匹配的数组来节省大量的带宽使用,或者实际上只要你能查找"通过做另一个查询,然后坚持在代码中执行它和/或进行单独的查询。