MongoDB 中的多个 $lookup 和 sort 嵌套数组

Multiple $lookup and sort nested arrays in MongoDB

我正在学习mongodb,我在整个互联网上搜索了一些技巧,但我仍然无法得到正确的结果。 我唯一要做的就是加入 2 collections.

让我介绍一下问题。

COLLECTIONS

艺术家

{
    _id: 1,
    Name: 'Artists one'
}

相册

{
    _id: 1,
    title: "Album 01",
    year
    artists_id: 1
}
{
    _id: 2,
    title: "Album 02",
    year: 2020,
    artists_id: 1
}

曲目

{
    albums_id: 1,
    track_number: 1,
    title: 'Track 01',
    time: 123
}
{
    albums_id: 1,
    track_number: 2,
    title: 'Track 02',
    time: 123
}
{
    albums_id: 2,
    track_number: 1,
    title: 'Track 01',
    time: 123
}
{
    albums_id: 2,
    track_number: 2,
    title: 'Track 02',
    time: 123
}

我想达到什么目标?

查询应该 return 结果如下。 相册应按年份升序排序。 曲目应按 track_number 升序(或按我希望的降序)

排序
{
    Name: 'Artists one',
    Albums: [
        {
            title: "Album 01",
            tracks: [
                {
                    title: 'Track 01'
                },
                {
                    title: 'Track 02'
                }
            ]
        },
        {
            title: "Album 02",
            tracks: [
                {
                    title: 'Track 01'
                },
                {
                    title: 'Track 02'
                }
            ]
        }
    ]
}

我最终得到了什么?

我可以使用已排序的专辑成功打印所有数据,但我不知道如何展开曲目以按 track_number 对它们进行排序并像在代码中一样再次分组

db.artists.aggregate([
    {
        $lookup:
        {
            from: "albums",
            localField: "_id",
            foreignField: "artists_id",
            as: "albums"
        }
    },
    {
        $unwind: "$albums"
    },
    {
        $lookup:
        {
            from: "tracks",
            localField: "albums._id",
            foreignField: "albums_id",
            as: "albums.tracks"
        }
    },
    {
        $sort:
        {
            "albums.year": 1
        }
    },
    {
        $group: 
        {
            _id : "$_id",
            "Name" : { $first: "$Name" },
            albums: { $push: "$albums" }
        }
    },
    {
        $project:
        {
            "_id":0,
            "Name":1,
            "albums":  {"title":1, "tracks": {"title":1}}

        }
    }
]).pretty()

我需要什么

我知道这并不难,我只是仍在尝试理解聚合框架。如果有人能告诉我如何完成这项工作,我将非常高兴 - 如果您能另外解释如何获得与我之前提到的假设一致但看起来一致的结果:

{
    Name: 'Artists one',
    Albums: [
        {
            title: "Album 01",
            tracks: ['Track 01' 'Track 02']
        },
        {
            title: "Album 02",
            tracks: ['Track 01' 'Track 02']
        }
    ]
}

这些代码对我理解聚合框架非常有帮助。

因为排序查询有点复杂

db.artists.aggregate([
  {
    $lookup: {
      from: "albums",
      localField: "_id",
      foreignField: "artists_id",
      as: "albums"
    }
  },
  {
    $unwind: "$albums"
  },
  {
    $lookup: {
      from: "tracks",
      localField: "albums._id",
      foreignField: "albums_id",
      as: "albums.tracks"
    }
  },
  {
    $unwind: "$albums.tracks"
  },
  {
    $sort: {
      "albums.tracks.track_number": 1
    }
  },
  {
    $group: {
      _id: {
        _id: "$_id",
        Name: "$Name",
        albumId: "$albums._id",
        albumTitle: "$albums.title",
        albumYear: "$albums.year"
      },
      albumsTracks: {
        $push: "$albums.tracks"
      }
    }
  },
  {
    $project: {
      _id: "$_id._id",
      Name: "$_id.Name",
      albumId: "$_id.albumId",
      albumTitle: "$_id.albumTitle",
      albumYear: "$_id.albumYear",
      tracks: "$albumsTracks"
    }
  },
  {
    $sort: {
      albumYear: 1
    }
  },
  {
    $group: {
      _id: {
        _id: "$_id",
        Name: "$Name"
      },
      Albums: {
        $push: {
          title: "$albumTitle",
          tracks: "$tracks"
        }
      }
    }
  },
  {
    $project: {
      _id: "$_id._id",
      Name: "$_id.Name",
      "Albums.tracks.title": 1
    }
  }
]).pretty()

一般来说,如果您看到这样的开销,则表明您需要考虑使用不同的结构来存储您的数据。例如,如果您确定单个记录在某个时间点不会超过 16 MB,您可能希望将所有数据合并到一个集合中。

从 MongoDB 3.6 开始,您可以使用条件 $lookup。对于每个 albums,您获取 tracks.

db.artists.aggregate([
  {
    $lookup: {
      from: "albums",
      let: {
        "artists_id": "$_id"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $eq: [
                "$artists_id",
                "$$artists_id"
              ]
            }
          }
        },
        {
          $sort: {
            year: 1
          }
        },
        {
          $lookup: {
            from: "tracks",
            let: {
              "albums_id": "$_id"
            },
            pipeline: [
              {
                $match: {
                  $expr: {
                    $eq: [
                      "$albums_id",
                      "$$albums_id"
                    ]
                  }
                }
              },
              {
                $sort: {
                  track_number: 1
                }
              }
            ],
            as: "tracks"
          }
        },
        {
          $project: {
            _id: 0,
            title: 1,
            tracks: {
              $map: {
                input: "$tracks",
                in: "$$this.title"
              }
            }
          }
        }
      ],
      as: "Albums"
    }
  },
  {
    $unset: "_id"
  }
])

MongoPlayground