【问题标题】:MongoDB aggregation with nested arrays of objects具有嵌套对象数组的 MongoDB 聚合
【发布时间】:2020-04-23 14:35:50
【问题描述】:

我正在努力从 Mongo DB 中获取一些汇总数据。我有以下收藏:

餐厅:

{
        "_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 数组,它也只返回具有社交媒体值的餐厅对象。

有什么线索可以解决这个问题吗?

【问题讨论】:

    标签: mongodb aggregation-framework mongodb-lookup


    【解决方案1】:

    您将$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,因为当前的解决方案容易出错。

    Mongo Playground

    【讨论】:

      【解决方案2】:

      非常感谢您的帮助。我试过这个查询:

      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
                      }
              ]
      }
      

      【讨论】:

        猜你喜欢
        • 2020-01-24
        • 2015-03-10
        • 2018-09-10
        • 1970-01-01
        • 2021-08-13
        • 1970-01-01
        • 2017-07-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多