Repository navigation
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*ChangeStreamwhoseNext,TryNext, andEachdeliverChangeEventvalues: operation type, resume token, cluster and wall time, namespace, document key, full document, pre-image, and update description.ChangeEvent.Decodeunmarshals the full document into a model and records it as the merge base, so a model decoded from an event can be saved withWithRetry.WatchOptionscovers the pipeline, full-document and pre-image modes (FullDocumentdefaults tooptions.UpdateLookup),ResumeAfter/StartAfter/StartAtOperationTime, batch size, max await time, collation, and expanded events.ChangeStream.Decodedecodes the current event through the collection, so client codecs andBSONOptionsapply as inFindOne. Middleware runs underOpWatchand may replaceOpInfo.Pipeline. See docs/watch.md. - Collection validators from the schema.
Schema.JSONSchema()derives a$jsonSchemadocument from the struct's Go types and goodm tags.EnforceOptions.ValidatorwithValidatorApplyinstalls it as each collection's validator (creating the collection if needed, leaving a matching validator untouched) andValidatorVerifyreports a mismatch without changing anything;ValidationLevelandValidationActionset 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 byErrDocumentValidation) with the server'serrInfoinDetails.goodm inspect --json-schemaprints each model's validator;CollectionOptions.SkipValidatorexempts a model whose stored form the schema cannot describe. - Population of refs inside subdocuments.
PopulateandBatchPopulateaccept 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.ObjectIDref fields are supported.
Changed
min/maxon a string field count characters, not bytes, matching the server'sminLength/maxLength.requiredtreats a value whose ownIsZeromethod reports zero as missing (a non-nil*time.Timepointing at the zero time), as the driver'somitemptydoes.UpdateFieldsgivennilfor a field that cannot hold one (a string, number, struct, or date) now stores the zero value the model holds instead of null.Populatechecks 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.BatchPopulatereports a ref field of an unsupported type instead of skipping it.
Fixed
SaveManysilently ignoredUnsetFieldsandWithRetry. It now removes the unset fields from every document and zeroes them on the saved models, and returns an error forWithRetry, which it cannot honor.UnsetFieldscould remove animmutablefield; unsetting one is now rejected byUpdateandSaveMany.goodm migratedid 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.