【问题标题】:Join more than one field using aggregate $lookup使用聚合 $lookup 连接多个字段
【发布时间】:2016-07-16 15:02:00
【问题描述】:

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

例如: "people" 集合字段 "city,state,country" 在 "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" 
         }
         ]
     }

【问题讨论】:

  • 这些集合是 PHP 还是 mongo 的?

标签: php mongodb mongodb-query aggregation-framework


【解决方案1】:

这可能比您想象的要简单得多,当然考虑到所有“三个”字段都包含在一个 "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 完成后的过程非常“复杂”,即使非常有效。因此,我建议除非有必要进一步使用此聚合管道并实际“聚合”某些内容,否则您最好在客户端代码中进行“过滤”,而不是在服务器上进行。

执行相同操作的客户端代码远没有您需要告诉聚合管道执行的代码那么“钝”。因此,除非这个“真的”通过减少匹配的数组来为您节省大量带宽使用,或者实际上您可以通过执行另一个查询来“查找”,然后坚持在代码中执行它和/或执行单独的查询。

【讨论】:

    猜你喜欢
    • 2019-11-08
    • 1970-01-01
    • 2020-08-02
    • 1970-01-01
    • 2019-01-27
    • 2019-04-18
    • 1970-01-01
    • 2017-10-08
    • 2023-01-04
    相关资源
    最近更新 更多