Open this lesson in your favourite AI. It'll walk you through the why, explain the demo, and quiz you on the try-it list.
MongoDB is often described as schema-less and join-less, but lookup eliminates by executing the join server-side. Understanding $lookup's performance characteristics — it triggers a separate collection scan per pipeline document unless you index the foreign key — prevents it from becoming a new performance problem.
Join an orders collection with a users collection to embed user details on each order.
users._id. Then create an index and re-run. Use explain('executionStats') to compare totalDocsExamined in the lookup stage.. Insert an order with no matching userId. Confirm the order still appears with user: null`.active field is true. Verify inactive users produce empty arrays.Use these three in order. Each builds on the one before.
Explain MongoDB's $lookup stage. What SQL operation does it approximate, and what are the required fields in the $lookup document?
How does MongoDB physically execute a $lookup — does it load the entire foreign collection into memory, or does it use an index? What index should exist on the foreign collection for best performance?
I need to join three collections in sequence: orders → line_items → products. Design the pipeline. At what point does it become worth denormalizing instead of chaining $lookup stages?
package main
import (
"context"; "fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
client, _ := mongo.Connect(context.TODO(), options.Client().ApplyURI("mongodb://localhost:27017"))
orders := client.Database("demo").Collection("orders")
pipeline := bson.A{
bson.D{{"$lookup", bson.D{
{"from", "users"},
{"localField", "userId"},
{"foreignField", "_id"},
{"as", "user"},
}}},
bson.D{{"$unwind", "$user"}},
bson.D{{"$project", bson.D{
{"orderId", "$_id"},
{"amount", 1},
{"userName", "$user.name"},
{"_id", 0},
}}},
}
cursor, _ := orders.Aggregate(context.TODO(), pipeline)
var results []bson.M
cursor.All(context.TODO(), &results)
for _, r := range results { fmt.Println(r) }
}go run main.go