Мне нужно объединить более двух полей в двух коллекциях, используя метод 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"
}
]
}
Это, наверное, намного проще, чем вы думаете, учитывая, что, конечно, все "три" поля содержатся в одном документе "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
выполняется довольно "сложно", даже если он очень эффективен. Поэтому я бы посоветовал, что, если нет необходимости использовать этот агрегатный конвейер дальше и фактически "агрегировать" что-то, тогда вам, вероятно, лучше делать эту "фильтрацию" в клиентском коде, а не делать это на сервере.
Клиентский код, чтобы сделать то же самое, гораздо менее "тупым", чем то, что вам нужно сообщить конвейеру агрегации. Поэтому, если это "действительно" не позволяет вам использовать большую пропускную способность, уменьшая сопоставленный массив или даже вы, если вы можете просто "искать", выполняя другой запрос, а затем придерживайтесь его в коде и/или выполняете отдельный запрос.