|
| 1 | +# 🦸♂️ Advanced lookups |
| 2 | + |
| 3 | +:::info |
| 4 | +Extra activity, do it if you have extra time or are following at home, won't be covered during the hands-on Lab |
| 5 | +::: |
| 6 | + |
| 7 | +We get this request: Write a `$lookup` to get `name` and `bio` from author's information inside each book document. To get this done, we need to review several things: |
| 8 | + |
| 9 | +- each book can have several authors. This many to many relationship (as an author can also write many books) is modelled using two different arrays: a `books` array in the `authors` collection and an `authors` array in the `books` collection. |
| 10 | +- so we'll need to get a separate document for each book that has more than one author. If a book has three authors we'll use `$unwind` to get three documents with the same data except for the author, that will be each of the three authors. |
| 11 | + |
| 12 | +You can try this with this Aggregation Pipeline: |
| 13 | + |
| 14 | +```js |
| 15 | +db.books.aggregate([ |
| 16 | + // as a book can have many authors, we get one doc per book's author |
| 17 | + {$unwind: "$authors"}, |
| 18 | + // remove some noisy fields |
| 19 | + {$project: {vectorizedSynopsis: 0, attributes: 0, reviews: 0}} |
| 20 | +]) |
| 21 | +``` |
| 22 | +- now, we need to get the authors'information. For that, we'll use `$lookup`, linking the `_id` in the `authors` collection with the `_id` we have in each book's `authors` array. But as we can see here, these have a different type: the ones inside our array are Strings, while the `author` collection `_id` are `ObjectId`. |
| 23 | + |
| 24 | +```js |
| 25 | + authors: { |
| 26 | + _id: '64cc2db4830ba29148da64a2', |
| 27 | + name: 'Timothy Findley' |
| 28 | + }, |
| 29 | +``` |
| 30 | + |
| 31 | +So we need to convert from `String` into `ObjectId`. We can do that using `$toObjectId`. This will add a new field `authorId` converting it into `ObjectId`: |
| 32 | + |
| 33 | +```js |
| 34 | +db.books.aggregate([ |
| 35 | + // as a book can have many authors, we get one doc per book's author |
| 36 | + {$unwind: "$authors"}, |
| 37 | + // convert it to an objectId |
| 38 | + {"$set":{"authorId":{"$toObjectId":"$authors._id"}}}, |
| 39 | + // remove some noisy fields |
| 40 | + {$project: {vectorizedSynopsis: 0, attributes: 0, reviews: 0}} |
| 41 | +]) |
| 42 | +``` |
| 43 | + |
| 44 | +- now we're ready to do the `$lookup`: we want all documents from `authors` that have the same `_id` as the `authorId` we just created. We use a `pipeline` to get just `authors` `name` and `bio`. |
| 45 | + |
| 46 | +```js |
| 47 | +db.books.aggregate([ |
| 48 | + // as a book can have many authors, we get one doc per book's author |
| 49 | + {$unwind: "$authors"}, |
| 50 | + // convert it to an objectId |
| 51 | + {"$set":{"authorId":{"$toObjectId":"$authors._id"}}}, |
| 52 | + {$lookup: { |
| 53 | + from: "authors", |
| 54 | + localField: "authorId", |
| 55 | + foreignField: "_id", |
| 56 | + pipeline: [ |
| 57 | + {$project: {name: 1, bio: 1}}, |
| 58 | + ], |
| 59 | + as: "bookAuthorDetails" |
| 60 | + } |
| 61 | + }, |
| 62 | + // remove some noisy fields |
| 63 | + {$project: {vectorizedSynopsis: 0, attributes: 0, reviews: 0}} |
| 64 | +]) |
| 65 | +``` |
| 66 | + |
| 67 | + |
0 commit comments