π¦ΈββοΈ Advanced Lookups
info
Extra activity! Do it if you have extra time or are following along at home. It won't be covered during the hands-on lab.
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:
- Each book can have several authors. This many-to-many relationship (as an author can also write many books) is modeled using two different arrays: a
booksarray in theauthorscollection and anauthorsarray in thebookscollection. - 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
$unwindto get three documents with the same data except for the author, which will be each of the three authors.
You can try this with this aggregation pipeline:
- Atlas UI
- MongoDB Shell
[
// as a book can have many authors, we get one doc per book's author
{$unwind: "$authors"},
// remove some noisy fields
{$project: {attributes: 0, reviews: 0}}
]
db.books.aggregate([
// as a book can have many authors, we get one doc per book's author
{$unwind: "$authors"},
// remove some noisy fields
{$project: {attributes: 0, reviews: 0}}
])
- Now, we need to get the authors' information. For that, we'll use
$lookup, linking the_idin theauthorscollection with the_idwe have in each book'sauthorsarray. But as we can see here, these have a different type: The ones inside our array are strings, while theauthorcollection_idareObjectId.
authors: {
_id: '64cc2db4830ba29148da64a2',
name: 'Timothy Findley'
},
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:
- Atlas UI
- MongoDB Shell
[
// as a book can have many authors, we get one doc per book's author
{$unwind: "$authors"},
// convert it to an objectId
{"$set":{"authorId":{"$toObjectId":"$authors._id"}}},
// remove some noisy fields
{$project: {attributes: 0, reviews: 0}}
]
db.books.aggregate([
// as a book can have many authors, we get one doc per book's author
{$unwind: "$authors"},
// convert it to an objectId
{"$set":{"authorId":{"$toObjectId":"$authors._id"}}},
// remove some noisy fields
{$project: {attributes: 0, reviews: 0}}
])
- Now, we're ready to do the
$lookup: We want all documents fromauthorsthat have the same_idas theauthorIdwe just created. We use apipelineto get justauthorsnameandbio.
- Atlas UI
- MongoDB Shell
[
// as a book can have many authors, we get one doc per book's author
{$unwind: "$authors"},
// convert it to an objectId
{"$set":{"authorId":{"$toObjectId":"$authors._id"}}},
{$lookup: {
from: "authors",
localField: "authorId",
foreignField: "_id",
pipeline: [
{$project: {name: 1, bio: 1}},
],
as: "bookAuthorDetails"
}
},
// remove some noisy fields
{$project: {attributes: 0, reviews: 0}}
]
db.books.aggregate([
// as a book can have many authors, we get one doc per book's author
{$unwind: "$authors"},
// convert it to an objectId
{"$set":{"authorId":{"$toObjectId":"$authors._id"}}},
{$lookup: {
from: "authors",
localField: "authorId",
foreignField: "_id",
pipeline: [
{$project: {name: 1, bio: 1}},
],
as: "bookAuthorDetails"
}
},
// remove some noisy fields
{$project: {attributes: 0, reviews: 0}}
])