Агрегация MongoDB с вложенными массивами объектов - PullRequest
1 голос
/ 06 января 2020

Я стремлюсь получить некоторые агрегированные данные из базы данных Mon go. У меня есть следующие коллекции:

Рестораны:

{
        "_id" : ObjectId("5e0ff6d424f9fc12bc3d9464"),
        "name" : "Pizzaria Don Juan",
        "active" : true,
        "users" : [
                {
                        "_id" : ObjectId("5e10fc2adc147a373c312144")
                },
                {
                        "_id" : ObjectId("5e11ff8003eb832ef84342a6")
                }
        ],
        "socialMedias" : [
                {
                        "_id" : ObjectId("5e1008943330ad05d4e1867c"),
                        "url" : "https://instagram/jetpizzas"
                },
                {
                        "_id" : ObjectId("5e10089a3330ad05d4e1867d"),
                        "url" : "https://facebook.com/jetpizzas"
                }
        ],
        "branches" : [
                {
                        "name" : "Teste"
                },
                {
                        "name" : "Teste 2"
                }
        ],
        "sections" : [
                {
                        "name" : "Bebidas"
                }
        ],
        "__v" : 0
}
{
        "_id" : ObjectId("5e0ffd23991918424c8d7c3b"),
        "name" : "Pizza Ruth",
        "active" : true,
        "users" : [ ],
        "socialMedias" : [ ],
        "branches" : [ ],
        "sections" : [ ],
        "__v" : 0
}
{
        "_id" : ObjectId("5e0ffd3d991918424c8d7c3c"),
        "name" : "Feijão de Corda",
        "active" : true,
        "users" : [ ],
        "socialMedias" : [ ],
        "branches" : [ ],
        "sections" : [ ],
        "__v" : 0
}

Пользователи

{
        "_id" : ObjectId("5e10fc2adc147a373c312144"),
        "isExpired" : false,
        "isBlocked" : false,
        "loginTentatives" : 0,
        "profile" : 2,
        "active" : true,
        "username" : "contato@pizzariadonjuan.com.br",
        "password" : "$2a$10$xhmw83QXbMvSqmrKAUYn.O4fOxboEyVkVB0DGkSsJUOp7K4bYQkCm",
        "email" : "",
        "phone" : "",
        "createdAt" : ISODate("2020-01-04T20:57:14.634Z"),
        "__v" : 0
}
{
        "_id" : ObjectId("5e11ff8003eb832ef84342a6"),
        "isExpired" : false,
        "isBlocked" : false,
        "loginTentatives" : 0,
        "profile" : 2,
        "active" : true,
        "username" : "sac@pizzariadonjuan.com.br",
        "password" : "$2a$10$wby3cs89jyO0HUbEiGLKye0jOB3U295zzIsu8xGJ4wnQtw5jcvSZO",
        "email" : "",
        "phone" : "",
        "createdAt" : ISODate("2020-01-05T15:23:44.386Z"),
        "__v" : 0
}
{
        "_id" : ObjectId("5e11ff9c03eb832ef84342a7"),
        "isExpired" : false,
        "isBlocked" : false,
        "loginTentatives" : 0,
        "profile" : 2,
        "active" : true,
        "username" : "juan@pizzariadonjuan.com.br",
        "password" : "$2a$10$nEM3RxEjYbI77R9vOWUrMOGeHFDmdZqVKUNtTLuKZVLNQBQqIbew.",
        "email" : "",
        "phone" : "",
        "createdAt" : ISODate("2020-01-05T15:24:12.456Z"),
        "__v" : 0
}

Профили

{
        "_id" : ObjectId("5e0ea5f6832df0473cacacda"),
        "number" : 1,
        "name" : "Cliente",
        "__v" : 0
}
{
        "_id" : ObjectId("5e0ea5ff832df0473cacacdb"),
        "number" : 2,
        "name" : "Restaurante",
        "__v" : 0
}
{
        "_id" : ObjectId("5e0ea607832df0473cacacdc"),
        "number" : 0,
        "name" : "Admin",
        "__v" : 0
}

И социальные медиа:

{
        "_id" : ObjectId("5e1008943330ad05d4e1867c"),
        "name" : "Instagram",
        "__v" : 0
}
{
        "_id" : ObjectId("5e10089a3330ad05d4e1867d"),
        "name" : "Facebook",
        "__v" : 0
}
{
        "_id" : ObjectId("5e1009043330ad05d4e1867f"),
        "name" : "LinkedIn",
        "__v" : 0
}

Моя цель - получить все объекты, связанные с объектами ресторана. Со следующим кодом:

db.restaurants.aggregate([
{ $lookup: { from: "users", localField: "users._id", foreignField: "_id", as: "foundUsers" } }, 
{$group: { 
'_id': '$_id', 
'name': { "$first": "$name" }, 
'active': { "$first": "$active" }, 
users: { $push: '$foundUsers' }, 
branches: { "$first": "$branches" }, 
sections: { "$first": "$sections" },
socialMedias: { "$first": "$socialMedias" }
}
},
{$unwind: '$users'},
{ $unset: 'users.password' },
{ $lookup: { from: "profiles", localField: "users.profile", foreignField: "number", as: "profile" } },
{ $addFields: { 'users.profile': { $arrayElemAt: ['$profile', 0] } } },
{ $unset: 'profile' },


{ $lookup: { from: "socialmedias", localField: "socialMedias._id", foreignField: "_id", as: "socialMedia" } },
{ $addFields: { 'socialMedias.name': { $arrayElemAt: ['$socialMedia.name', 0] } } },
{$group: { 
'_id': '$_id', 
'name': { "$first": "$name" }, 
'active': { "$first": "$active" }, 
users: { $first: '$users' }, 
branches: { "$first": "$branches" }, 
sections: { "$first": "$sections" },
socialMedias: { "$first": "$socialMedias" }
}
}
])

Я получаю это:

    {
        "_id" : ObjectId("5e0ffd3d991918424c8d7c3c"),
        "name" : "Feijão de Corda",
        "active" : true,
        "users" : [ ],
        "branches" : [ ],
        "sections" : [ ],
        "socialMedias" : [ ]
}
{
        "_id" : ObjectId("5e0ffd23991918424c8d7c3b"),
        "name" : "Pizza Ruth",
        "active" : true,
        "users" : [ ],
        "branches" : [ ],
        "sections" : [ ],
        "socialMedias" : [ ]
}
{
        "_id" : ObjectId("5e0ff6d424f9fc12bc3d9464"),
        "name" : "Pizzaria Don Juan",
        "active" : true,
        "users" : [
                {
                        "_id" : ObjectId("5e10fc2adc147a373c312144"),
                        "isExpired" : false,
                        "isBlocked" : false,
                        "loginTentatives" : 0,
                        "profile" : {
                                "_id" : ObjectId("5e0ea5ff832df0473cacacdb"),
                                "number" : 2,
                                "name" : "Restaurante",
                                "__v" : 0
                        },
                        "active" : true,
                        "username" : "contato@pizzariadonjuan.com.br",
                        "email" : "",
                        "phone" : "",
                        "createdAt" : ISODate("2020-01-04T20:57:14.634Z"),
                        "__v" : 0
                },
                {
                        "_id" : ObjectId("5e11ff8003eb832ef84342a6"),
                        "isExpired" : false,
                        "isBlocked" : false,
                        "loginTentatives" : 0,
                        "profile" : {
                                "_id" : ObjectId("5e0ea5ff832df0473cacacdb"),
                                "number" : 2,
                                "name" : "Restaurante",
                                "__v" : 0
                        },
                        "active" : true,
                        "username" : "sac@pizzariadonjuan.com.br",
                        "email" : "",
                        "phone" : "",
                        "createdAt" : ISODate("2020-01-05T15:23:44.386Z"),
                        "__v" : 0
                }
        ],
        "branches" : [
                {
                        "name" : "Teste"
                },
                {
                        "name" : "Teste 2"
                }
        ],
        "sections" : [
                {
                        "name" : "Bebidas"
                }
        ],
        "socialMedias" : [
                {
                        "_id" : ObjectId("5e1008943330ad05d4e1867c"),
                        "url" : "https://instagram/jetpizzas",
                        "name" : "Instagram"
                },
                {
                        "_id" : ObjectId("5e10089a3330ad05d4e1867d"),
                        "url" : "https://facebook.com/jetpizzas",
                        "name" : "Instagram"
                }
        ]
}

Обратите внимание, что вложенный массив

socialMedias

имеет неправильное значение для имени социальной сети (дублированное имя «Instagram», это должна быть одна запись для Instagram и другая для Facebook). Даже если я попытаюсь развернуть массивы socialMedias из коллекции ресторанов, он вернет только тот объект ресторана, у которого в результате будут значения социальных медиа.

Любая подсказка, как сделать это правильно?

Ответы [ 2 ]

1 голос
/ 06 января 2020

Способ слияния $lookup результатов с существующим массивом является проблемой здесь. Вы не можете запустить:

{ $addFields: { 'socialMedias.name': { $arrayElemAt: ['$socialMedia.name', 0] } } },

, потому что вы всегда получите первый элемент массива. Вместо этого вам нужно объединить два массива, используя $ map , $ filter и $ mergeObjects :

{
    $addFields: {
        socialmedias: {
            $map: {
                input: "$socialMedias",
                as: "sm",
                in: {
                    $mergeObjects: [
                        "$$this",
                        {
                            $arrayElemAt: [ { $filter: { input: "$socialmedias", cond: { $eq: [ "$$sm.number", "$$this._id" ] } } }, 0 ]
                        }
                    ]
                }
            }
        }
    }
}

Вы также должны применить это для user.profile, поскольку текущее решение подвержено ошибкам.

Пн go Детская площадка

0 голосов
/ 06 января 2020

Большое спасибо за помощь. Я попробовал этот запрос:

db.restaurants.aggregate([
{ $lookup: { from: "users", localField: "users._id", foreignField: "_id", as: "foundUsers" } }, 
{$group: { 
'_id': '$_id', 
'name': { "$first": "$name" }, 
'active': { "$first": "$active" }, 
users: { $push: '$foundUsers' }, 
branches: { "$first": "$branches" }, 
sections: { "$first": "$sections" },
socialMedias: { "$first": "$socialMedias" }
}
},
{$unwind: '$users'},
{ $unset: 'users.password' },
{ $lookup: { from: "profiles", localField: "users.profile", foreignField: "number", as: "profile" } },
{ $addFields: { 'users.profile': { $arrayElemAt: ['$profile', 0] } } },
{ $unset: 'profile' },


{ $lookup: { from: "socialmedias", localField: "socialMedias._id", foreignField: "_id", as: "foundSocialMedia" } },
{
    $addFields: {
      socialMedias: {
        $map: {
          input: "$socialMedias",
          as: "sm",
          in: {
            $mergeObjects: [
              "$$sm",
              {
                $arrayElemAt: [
                  {
                    $filter: {
                      input: "$foundSocialMedia",
                      cond: {
                        $eq: [
                          "$$sm._id",
                          "$$this._id"
                        ]
                      }
                    }
                  },
                  0
                ]
              }
            ]
          }
        }
      }
    }
  },
{ $unset: 'foundSocialMedia' },
])

И получил желаемый результат:

{
        "_id" : ObjectId("5e0ffd3d991918424c8d7c3c"),
        "name" : "Feijão de Corda",
        "active" : true,
        "users" : [ ],
        "branches" : [ ],
        "sections" : [ ],
        "socialMedias" : [ ]
}
{
        "_id" : ObjectId("5e0ffd23991918424c8d7c3b"),
        "name" : "Pizza Ruth",
        "active" : true,
        "users" : [ ],
        "branches" : [ ],
        "sections" : [ ],
        "socialMedias" : [ ]
}
{
        "_id" : ObjectId("5e0ff6d424f9fc12bc3d9464"),
        "name" : "Pizzaria Don Juan",
        "active" : true,
        "users" : [
                {
                        "_id" : ObjectId("5e10fc2adc147a373c312144"),
                        "isExpired" : false,
                        "isBlocked" : false,
                        "loginTentatives" : 0,
                        "profile" : {
                                "_id" : ObjectId("5e0ea5ff832df0473cacacdb"),
                                "number" : 2,
                                "name" : "Restaurante",
                                "__v" : 0
                        },
                        "active" : true,
                        "username" : "contato@pizzariadonjuan.com.br",
                        "email" : "",
                        "phone" : "",
                        "createdAt" : ISODate("2020-01-04T20:57:14.634Z"),
                        "__v" : 0
                },
                {
                        "_id" : ObjectId("5e11ff8003eb832ef84342a6"),
                        "isExpired" : false,
                        "isBlocked" : false,
                        "loginTentatives" : 0,
                        "profile" : {
                                "_id" : ObjectId("5e0ea5ff832df0473cacacdb"),
                                "number" : 2,
                                "name" : "Restaurante",
                                "__v" : 0
                        },
                        "active" : true,
                        "username" : "sac@pizzariadonjuan.com.br",
                        "email" : "",
                        "phone" : "",
                        "createdAt" : ISODate("2020-01-05T15:23:44.386Z"),
                        "__v" : 0
                }
        ],
        "branches" : [
                {
                        "name" : "Teste"
                },
                {
                        "name" : "Teste 2"
                }
        ],
        "sections" : [
                {
                        "name" : "Bebidas"
                }
        ],
        "socialMedias" : [
                {
                        "_id" : ObjectId("5e1008943330ad05d4e1867c"),
                        "url" : "https://instagram/jetpizzas",
                        "name" : "Instagram",
                        "__v" : 0
                },
                {
                        "_id" : ObjectId("5e10089a3330ad05d4e1867d"),
                        "url" : "https://facebook.com/jetpizzas",
                        "name" : "Facebook",
                        "__v" : 0
                }
        ]
}
...