Skip to content

Aggregation Pipeline Stage - $lookup

Build the $lookup stage through the aggregation pipeline stage builder aggregation.NewStageBuilder using the method Lookup.

Basic Usage

Suppose we want to find orders in the orders collection that correspond to each user in the users collection and add the found orders as an array to each user document.

go
type User struct {
	mongox.Model `bson:"inline"`
	Name         string `bson:"name"`
	Age          int    `bson:"age"`
}

// mongo.Pipeline{bson.D{bson.E{Key:"$lookup", Value:bson.D{bson.E{Key:"from", Value:"orders"}, bson.E{Key:"localField", Value:"_id"}, bson.E{Key:"foreignField", Value:"userId"}, bson.E{Key:"as", Value:"userOrders"}}}}}
//[
//  {
//    "$lookup": {
//      "from": "orders",
//      "localField": "_id",
//      "foreignField": "userId",
//      "as": "userOrders"
//    }
//  }
//]
aggregation.NewStageBuilder().Lookup("orders", "userOrders", &aggregation.LookUpOptions{
    LocalField:   "_id",
    ForeignField: "userId",
}).Build()

The Lookup method has three parameters: from string, as string, and opt *LookUpOptions. from specifies the collection to join, as specifies the name of the field for the joined data, and opt specifies join options such as LocalField, ForeignField, Let, and Pipeline.

Advanced Usage

Suppose we only want to add orders with a total amount greater than 100 to the user documents.

go
type User struct {
	mongox.Model `bson:"inline"`
	Name         string `bson:"name"`
	Age          int    `bson:"age"`
}

type Order struct {
    ID          primitive.ObjectID `bson:"_id,omitempty"` // Unique identifier for the order
    UserID      primitive.ObjectID `bson:"userId"`        // Associated user ID, corresponding to the _id in the User collection
    TotalAmount float64            `bson:"totalAmount"`   // Total amount of the order
    OrderDate   time.Time          `bson:"orderDate"`     // Date of the order
}

// mongo.Pipeline{bson.D{bson.E{Key:"$lookup", Value:bson.D{bson.E{Key:"from", Value:"orders"}, bson.E{Key:"let", Value:bson.D{bson.E{Key:"userId", Value:"$_id"}}}, bson.E{Key:"pipeline", Value:mongo.Pipeline{bson.D{bson.E{Key:"$match", Value:bson.D{bson.E{Key:"$expr", Value:bson.D{bson.E{Key:"$and", Value:[]interface {}{bson.D{bson.E{Key:"$eq", Value:[]interface {}{"$userId", "$$userId"}}}, bson.D{bson.E{Key:"$gt", Value:[]interface {}{"$totalAmount", 100}}}}}}}}}}}}, bson.E{Key:"as", Value:"largeOrders"}}}}}
//[
//  {
//    "$lookup": {
//      "from": "orders",
//      "let": { "userId": "$_id" },
//      "pipeline": [
//        {
//          "$match": {
//            "$expr": {
//              "$and": [ { "$eq": ["$userId", "$$userId"] }, { "$gt": ["$totalAmount", 100] } ]
//            }
//          }
//        }
//      ],
//      "as": "largeOrders"
//    }
//  }
//]
aggregation.NewStageBuilder().Lookup("orders", "largeOrders", &aggregation.LookUpOptions{
    Let: bsonx.D("userId", "$_id"),
    Pipeline: aggregation.NewStageBuilder().Match(
        aggregation.And("$expr", aggregation.EqWithoutKey("$userId", "$$userId"), aggregation.GtWithoutKey("$totalAmount", 100)),
    ).Build(),
}).Build()
go
type User struct {
    mongox.Model `bson:"inline"`
    Name         string `bson:"name"`
    Age          int    `bson:"age"`
}

// mongo.Pipeline{bson.D{bson.E{Key:"$lookup", Value:bson.D{bson.E{Key:"from", Value:"orders"}, bson.E{Key:"localField", Value:"_id"}, bson.E{Key:"foreignField", Value:"userId"}, bson.E{Key:"as", Value:"userOrders"}}}}}
//[
//  {
//    "$lookup": {
//      "from": "orders",
//      "localField": "_id",
//      "foreignField": "userId",
//      "as": "userOrders"
//    }
//  }
//]
aggregation.NewStageBuilder().Lookup("orders", "userOrders", &aggregation.LookUpOptions{
    LocalField:   "_id",
    ForeignField: "userId",
}).Build()