Присоединитесь к более чем одному полю, используя агрегат $ lookup

1

Мне нужно объединить более двух полей в двух коллекциях, используя метод aggregate $ lookup. можно ли присоединиться? сообщите мне, если это возможно. Здесь у меня есть две коллекции:

Например: поля "люди" собирают поля "город, штат, страна" в полях "страна" "city_id, state_id, country_id", я хочу присоединиться к этим трем полям в следующих коллекциях.

"Люди"

    {    
        "_id" : 1,
        "email" : "[email protected]",
        "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" 
         }
         ]
     }
  • 0
    Эти коллекции на PHP или на монго?
  • 0
    Для обоих @chris
Теги:
mongodb-query
aggregation-framework

1 ответ

1

Это, наверное, намного проще, чем вы думаете, учитывая, что, конечно, все "три" поля содержатся в одном документе "country". Таким образом, это просто вопрос $lookup помощью "country_id" а затем с использованием загруженного содержимого для заполнения других полей.

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" : "[email protected]",
  "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 для выполнения matcing и $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);

Обычно вы можете проверить, что ваш PHP соответствует структуре JSON, когда вы работаете с примером JSON, сбросив структуру конвейера:

echo json_encode($pipeline, JSON_PRETTY_PRINT)

И таким образом вы не ошибетесь.

В качестве еще одной заключительной заметки здесь процесс после $lookup выполняется довольно "сложно", даже если он очень эффективен. Поэтому я бы посоветовал, что, если нет необходимости использовать этот агрегатный конвейер дальше и фактически "агрегировать" что-то, тогда вам, вероятно, лучше делать эту "фильтрацию" в клиентском коде, а не делать это на сервере.

Клиентский код, чтобы сделать то же самое, гораздо менее "тупым", чем то, что вам нужно сообщить конвейеру агрегации. Поэтому, если это "действительно" не позволяет вам использовать большую пропускную способность, уменьшая сопоставленный массив или даже вы, если вы можете просто "искать", выполняя другой запрос, а затем придерживайтесь его в коде и/или выполняете отдельный запрос.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню