MongoDB: агрегація, populate vs lookup

Агрегация (aggregate) - это выполнение сложных поисковых запросов.

Например, у нас есть коллекция пользователей со следующими документами:

userModel.create(
   { "_id" : 1, "name" : "Ivan", "favouriteFilmIds" : [1,3,5] },
   { "_id" : 2, "name" : "Igor", "favouriteFilmIds" : [1,2,3] },
   { "_id" : 3, "name" : "Vasil" }
)

И коллекция фильмов со следующими документами:

filmModel.create(
   { "_id" : 1, "name": "Titanic", genre: "drama" },
   { "_id" : 2, "name": "Lord of the Rings", genre: "fantasy" },
   { "_id" : 3, "name": "The King's Speech", genre: "drama" },
   { "_id" : 4, "name": "Gentleman", genre: "Action" },
   { "_id" : 5, "name": null },
)

Для того, чтобы вытянуть пользователей вместе с их любимыми фильмами, воспользуемся aggregate:

userModel.aggregate([
   {
     $lookup:
       {
         // название колекции фильмов
         from: "films", // название колекции, а не модели!!!!
         
         // поле в колекции пользователей, которое содержит id любимых фильмов
         localField: "favouriteFilmIds", 
         
         // поле в колекции фильмов, которое должно отвечать id фильмов в
         // коллекции пользователей
         foreignField: "_id",
         
         // как поле с документами фильмов будет называтся
         as: "favouriteFilms"
       }
  }
])

В результате получим что-то вроде:

[
   { 
      "_id" : 1,
      "name" : "Ivan",
      "favouriteFilmIds" : [1,3,5],
      "favouriteFilms": [
         { "_id" : 1, "name": "Titanic", genre: "drama" },
         { "_id" : 3, "name": "The King's Speech", genre: "drama" },
         { "_id" : 5, "name": null }
      ]
   },
   { 
      "_id" : 2,
      "name" : "Igor",
      "favouriteFilmIds" : [1,2,3],
      "favouriteFilms": [
         { "_id" : 1, "name": "Titanic", genre: "drama" },
         { "_id" : 2, "name": "Lord of the Rings", genre: "fantasy" },
         { "_id" : 3, "name": "The King's Speech", genre: "drama" },
      ]
   },
   { 
      "_id" : 3,
      "name" : "Vasil",
      "favouriteFilms": []
   }
]

Помимо оператора $ lookup для вытягивания взаимосвязанных данных из разных коллекций, в aggregate есть еще много других операторов.

Для выполнения этих действий одно за другим мы можем прописать их в различных объектах аргумента-массива aggregate. Это называется aggregation pipeline. Например, нам нужно исключить из результата поиска выше favouriteFilmIds, так как достаточно наличия favouriteFilms. Для этого модифицируем наш предыдущий запрос оператором unset:

userModel.aggregate([
  {
     $lookup:
       {
         from: "films",
         localField: "favouriteFilmIds",
         foreignField: "_id",
         as: "favouriteFilms"
       }
  },
  {
    $unset: ["favouriteFilmIds"]
  }
])

Last updated