Skip to content

goodm v0.3.0

Choose a tag to compare

@dwoolworth dwoolworth released this 17 Feb 02:15
· 22 commits to main since this release

What's New

Subdocument Support

goodm now recursively parses goodm tags on nested struct fields. Validation, defaults, and schema introspection work at any nesting depth — no separate registration needed for subdocument types.

type Address struct {
    Street string `bson:"street" goodm:"required"`
    City   string `bson:"city"   goodm:"required"`
    Zip    string `bson:"zip"    goodm:"default=00000"`
}

type Order struct {
    goodm.Model `bson:",inline"`
    Name        string      `bson:"name"    goodm:"required"`
    Address     Address     `bson:"address" goodm:"required"`
    Items       []OrderItem `bson:"items"`
}

New Features

  • Recursive validation — all goodm tags (required, enum, min, max) are enforced on nested struct fields and slice elements
  • Dotted error paths — validation errors use MongoDB-style paths: "address.street", "items[0].name", "shipping.address.city"
  • Subdocument defaults — default=X tags inside nested structs and slice elements are applied during Create and CreateMany
  • Schema introspection — FieldSchema.SubFields contains parsed inner fields; FieldSchema.IsSlice is true for []struct fields

Per-Schema Read/Write Concern

  • Configurable interface — implement CollectionOptions() on your model to set read preference, read concern, and write concern per collection
  • All CRUD, bulk, and pipeline operations automatically use the configured options

Schema Defaults & Optimistic Concurrency

  • default=X tag — zero-valued fields are set to their schema default during Create and CreateMany
  • __v version key — documents get a version field that increments on each Update, enabling optimistic concurrency control with ErrVersionConflict

Design Details

  • Leaf types (time.Time, bson.ObjectID, bson.Decimal128) are never recursed into
  • Circular references handled via seen-type tracking during parsing
  • Nil pointer subdocuments are skipped (no auto-materialization)
  • Marking a subdocument field as immutable makes the entire subdocument immutable