Skip to content

v0.8.0

Latest

Choose a tag to compare

@dwoolworth dwoolworth released this 25 Oct 19:31
· 1 commit to main since this release
35cfbca

goodm 0.8.0 adds the three features deferred from 0.7: change streams, collection validators derived from the schema, and population of refs inside subdocuments. It also fixes SaveMany ignoring UnsetFields. Four behavior changes are called out below. Full detail is in the CHANGELOG.

Tested against

MongoDB Go
6.0.28, 7.0.45, 8.0.34 as single-node replica sets 1.21, 1.22, 1.23 in CI; 1.26 locally

The full suite runs with the race detector on every combination. Each pull request went through two or three adversarial review rounds until clean, and the merged release had a final cross-cutting review before this tag.

Added

  • Change streams. Watch(ctx, &Model{}) opens a change stream on the model's collection and returns a *ChangeStream whose Next, TryNext, and Each deliver ChangeEvent values: operation type, resume token, cluster and wall time, namespace, document key, full document, pre-image, and update description. ChangeEvent.Decode unmarshals the full document into a model and records it as the merge base, so a model decoded from an event can be saved with WithRetry. WatchOptions covers the pipeline, full-document and pre-image modes (FullDocument defaults to options.UpdateLookup), ResumeAfter/StartAfter/StartAtOperationTime, batch size, max await time, collation, and expanded events. ChangeStream.Decode decodes the current event through the collection, so client codecs and BSONOptions apply as in FindOne. Middleware runs under OpWatch and may replace OpInfo.Pipeline. See docs/watch.md.
  • Collection validators from the schema. Schema.JSONSchema() derives a $jsonSchema document from the struct's Go types and goodm tags. EnforceOptions.Validator with ValidatorApply installs it as each collection's validator (creating the collection if needed, leaving a matching validator untouched) and ValidatorVerify reports a mismatch without changing anything; ValidationLevel and ValidationAction set the server's level and action. A document goodm writes always passes the validator generated from its own schema; self-marshaling types are left unconstrained. Writes the server rejects return *DocumentValidationError (matched by ErrDocumentValidation) with the server's errInfo in Details. goodm inspect --json-schema prints each model's validator; CollectionOptions.SkipValidator exempts a model whose stored form the schema cannot describe.
  • Population of refs inside subdocuments. Populate and BatchPopulate accept dotted paths ("ship.carrier", "items.product") through struct, pointer, and slice fields, including self-recursive subdocument types. A path through a slice collects every element's refs into a slice target; a numeric segment ("items.0.product") selects one element. *bson.ObjectID ref fields are supported.

Changed

  • min/max on a string field count characters, not bytes, matching the server's minLength/maxLength.
  • required treats a value whose own IsZero method reports zero as missing (a non-nil *time.Time pointing at the zero time), as the driver's omitempty does.
  • UpdateFields given nil for a field that cannot hold one (a string, number, struct, or date) now stores the zero value the model holds instead of null.
  • Populate checks its arguments before looking anything up: the model must be a non-nil pointer to a struct (a slice used to be silently ignored), every target must be a non-nil pointer, and a path that can yield several ids must have a slice target, even when the field is currently empty. BatchPopulate reports a ref field of an unsupported type instead of skipping it.

Fixed

  • SaveMany silently ignored UnsetFields and WithRetry. It now removes the unset fields from every document and zeroes them on the saved models, and returns an error for WithRetry, which it cannot honor.
  • UnsetFields could remove an immutable field; unsetting one is now rejected by Update and SaveMany.
  • goodm migrate did not list planned index rebuilds; they are shown with ~ and counted.

Known limitations

Writes with UnsetFields (in Update and SaveMany) and the WithRetry merge use the default BSON registry, so a model whose stored form depends on codecs registered on the client cannot be unset-saved or merged with them; ChangeEvent.Decode on a detached event has the same limit, while ChangeStream.Decode does not. The driver exposes no way to read a collection's registry.

Pull requests: #12, #13, #14, #15, #16.