Несколько $ lookup и сортировка вложенных массивов в MongoDB

Я изучаю mongodb, я искал несколько советов по всему Интернету, но все еще не могу получить правильный результат. Единственное, что мне нужно сделать, это присоединиться к 2 коллекциям.

Позвольте представить проблему.

КОЛЛЕКЦИИ

Художники

{
    _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
}

ЧЕМ Я ХОЧУ ЗАЙДИТЬ?

запрос должен возвращать результат, как показано ниже. Альбомы следует отсортировать по годам по возрастанию. Дорожки должны быть отсортированы по 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']
        }
    ]
}

Код просто очень помог бы мне понять структуру агрегации.


person Chris    schedule 24.01.2020    source источник
comment
Вам необходимо использовать условное $lookup stackoverflow.com/a/52472998/3710490   -  person Valijon    schedule 24.01.2020


Ответы (2)


Из-за сортировки запрос немного сложен

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 МБ.

person Rashad Ibrahimov    schedule 24.01.2020

Начиная с 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

person Valijon    schedule 24.01.2020