Skip to content

Population

Population in goose allows you to automatically replace specified paths in a document with actual documents from other collections, mimicking relational joins.

Under the hood, goose translates population requests into optimized $lookup aggregation pipelines.

Basic Usage

Given a schema with a referenced field:

type Post struct {
    ID      primitive.ObjectID `bson:"_id,omitempty"`
    Title   string             `bson:"title"`
    Author  primitive.ObjectID `bson:"author" goose:"ref=User"`
}

You can populate the Author field when querying:

posts, err := postModel.Find(ctx, bson.M{}).
    Populate("Author").
    All()

When .Populate("Author") is called, goose looks up the goose:"ref=User" tag (or the Ref property in the FieldDef), automatically joins the users collection, and maps the joined document into your struct.

Target Field Type

Ensure the field you are populating is defined properly in your struct. If it's a single reference, the target field should typically be a nested struct or pointer. However, due to MongoDB's nature, $lookup usually requires bson unmarshaling to handle the populated object.

FieldDef Configuration

If you configure your schema via code, you define relationships like this:

fields := map[string]goose.FieldDef{
    "Author": {
        Ref: "users",
        JustOne: true, // Tells goose to unwrap the single object from the $lookup array
    },
}
  • Ref: The target collection name.
  • LocalField: The field in the current collection (defaults to _id).
  • ForeignField: The field in the target collection (defaults to _id).
  • JustOne: Set to true if the relationship is 1-to-1 or N-to-1, meaning you want a single document instead of an array.