CRUD Operations¶
goodm provides full-lifecycle CRUD functions that handle ID generation, timestamps, hooks, validation, and immutable field enforcement. It also provides raw passthrough operations for performance-critical paths.
Create¶
Inserts a new document with the full ODM lifecycle:
- Generates
ID(if zero) - Sets
CreatedAt(if zero) andUpdatedAt - Applies schema defaults to zero-valued fields
- Sets
Versionto 0 - Runs
BeforeCreatehook - Validates against schema (required, enum, min/max)
- Inserts into MongoDB
- Runs
AfterCreatehook
user := &User{Email: "alice@example.com", Name: "Alice", Age: 30}
err := goodm.Create(ctx, user)
// user.ID, user.CreatedAt, user.UpdatedAt are now set
FindOne¶
func FindOne(ctx context.Context, filter interface{}, result interface{}, opts ...FindOptions) error
Finds a single document matching the filter. Returns ErrNotFound if no document matches.
user := &User{}
err := goodm.FindOne(ctx, bson.D{{Key: "email", Value: "alice@example.com"}}, user)
if errors.Is(err, goodm.ErrNotFound) {
// not found
}
Find¶
Finds all matching documents. results must be a pointer to a slice.
With options:
var users []User
err := goodm.Find(ctx, bson.D{}, &users, goodm.FindOptions{
Limit: 20,
Skip: 40,
Sort: bson.D{{Key: "created_at", Value: -1}},
})
FindCursor¶
func FindCursor(ctx context.Context, filter interface{}, model interface{}, opts ...FindOptions) (*mongo.Cursor, error)
Returns a raw *mongo.Cursor for streaming large result sets. The model parameter is used only for collection lookup.
cursor, err := goodm.FindCursor(ctx, bson.D{}, &User{}, goodm.FindOptions{
Sort: bson.D{{Key: "created_at", Value: 1}},
})
if err != nil {
log.Fatal(err)
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var user User
cursor.Decode(&user)
// process user
}
Update¶
Replaces an existing document with the full ODM lifecycle:
- Requires non-zero
ID - Fetches existing document from DB (only if immutable fields exist)
- Validates immutable fields (compares with existing doc)
- Runs
BeforeSavehook - Validates against schema
- Increments
Versionand setsUpdatedAt - Replaces document in MongoDB with version check (
__vfilter) - Returns
ErrVersionConflictif another process modified the document - Runs
AfterSavehook
If an immutable field has changed, Update returns a ValidationErrors with the offending field.
Delete¶
Removes a document by its ID with hooks:
- Requires non-zero
ID - Runs
BeforeDeletehook - Deletes from MongoDB
- Runs
AfterDeletehook
Raw Operations¶
These bypass hooks, validation, and immutable enforcement. Use them when you need direct MongoDB access for performance.
UpdateOne¶
Partial update using MongoDB update operators. The model parameter is for collection lookup only.
err := goodm.UpdateOne(ctx,
bson.D{{Key: "email", Value: "alice@example.com"}},
bson.D{{Key: "$set", Value: bson.D{{Key: "age", Value: 31}}}},
&User{},
)
Performance: Bypasses hooks, validation, and immutable field enforcement. You are responsible for data integrity.
DeleteOne¶
Deletes by filter without requiring a loaded model.
Performance: Bypasses hooks entirely.
Options¶
All operations accept an optional DB field to override the global database:
goodm.Create(ctx, user, goodm.CreateOptions{DB: otherDB})
goodm.FindOne(ctx, filter, result, goodm.FindOptions{DB: otherDB})
goodm.Update(ctx, user, goodm.UpdateOptions{DB: otherDB})
goodm.Delete(ctx, user, goodm.DeleteOptions{DB: otherDB})
Error Types¶
| Error | When |
|---|---|
ErrNotFound |
FindOne/Update/Delete finds no matching document |
ErrNoDatabase |
No database connection (Connect not called) |
ErrVersionConflict |
Update detects another process modified the document (optimistic concurrency) |
ValidationErrors |
Validation or immutable check failure |