The Mongo ODM for Go
- Define your models and do CRUD operations with hooks before/after each operation.
mgmmakes Mongo search and aggregation super easy to do in Golang.- Just set up your configs one time and get collections anywhere you need those.
mgmpredefined all Mongo operators and keys, So you don't have to hardcode them.- The wrapper of the official Mongo Go Driver.
- Go 1.10 or higher.
- MongoDB 2.6 and higher.
go get github.com/Kamva/mgm/v3To get started, import the mgm package, setup default config:
import (
"github.com/Kamva/mgm/v3"
"go.mongodb.org/mongo-driver/mongo/options"
)
func init() {
// Setup mgm default config
err := mgm.SetDefaultConfig(nil, "mgm_lab", options.Client().ApplyURI("mongodb://root:12345@localhost:27017"))
}Define your model:
type Book struct {
// DefaultModel add _id,created_at and updated_at fields to the Model
mgm.DefaultModel `bson:",inline"`
Name string `json:"name" bson:"name"`
Pages int `json:"pages" bson:"pages"`
}
func NewBook(name string, pages int) *Book {
return &Book{
Name: name,
Pages: pages,
}
}Insert new document:
book:=NewBook("Pride and Prejudice", 345)
// Make sure pass the model by reference.
err := mgm.Coll(book).Create(book)Find one document
//Get document's collection
book := &Book{}
coll := mgm.Coll(book)
// Find and decode doc to the book model.
_ = coll.FindByID("5e0518aa8f1a52b0b9410ee3", book)
// Get first doc of collection
_ = coll.First(bson.M{}, book)
// Get first doc of collection with filter
_ = coll.First(bson.M{"page":400}, book)Update document
// Find your book
book:=findMyFavoriteBook()
// and update it
book.Name="Moulin Rouge!"
err:=mgm.Coll(book).Update(book)Delete document
// Just find and delete your document
err := mgm.Coll(book).Delete(book)Find and decode result:
result := []Book{}
err := mgm.Coll(&Book{}).SimpleFind(&result, bson.M{"age": bson.M{operator.Gt: 24}})Each model by default (by using DefaultModel struct) has
this fields:
-
_id: Document Id. -
created_at: Creation date of doc. On save new doc, autofill byCreatinghook. -
updated_at: Last update date of doc. On save doc, autofill bySavinghook
Each model has these hooks :
-
Creating: Call on creating a new model.
Signature :Creating() error -
Created: Call on new model created.
Signature :Created() error -
Updating: Call on updating model.
Signature :Updating() error -
Updated: Call on models updated.
Signature :Updated(result *mongo.UpdateResult) error -
Saving: Call on creating or updating the model.
Signature :Saving() error -
Saved: Call on models Created or updated.
Signature:Saved() error -
Deleting: Call on deleting model.
Signature:Deleting() error -
Deleted: Call on models deleted.
Signature:Deleted(result *mongo.DeleteResult) error
Notes about hooks:
- Each model by default use the
CreatingandSavinghooks, So if you want to define those hooks, call toDefaultModelhooks in your defined hooks. - collection's methods which call to the hooks:
Create&CreateWithCtxUpdate&UpdateWithCtxDelete&DeleteWithCtx
Example:
func (model *Book) Creating() error {
// Call to DefaultModel Creating hook
if err:=model.DefaultModel.Creating();err!=nil{
return err
}
// We can check if model fields is not valid, return error to
// cancel document insertion .
if model.Pages < 1 {
return errors.New("book must have at least one page")
}
return nil
}mgm default config contains context timeout:
func init() {
_ = mgm.SetDefaultConfig(&mgm.Config{CtxTimeout:12 * time.Second}, "mgm_lab", options.Client().ApplyURI("mongodb://root:12345@localhost:27017"))
}
// To get context , just call to Ctx() method.
ctx:=mgm.Ctx()
// Now we can get context by calling to `Ctx` method.
coll := mgm.Coll(&Book{})
coll.FindOne(ctx,bson.M{})
// Or call it without assign variable to it.
coll.FindOne(mgm.Ctx(),bson.M{})Get model collection:
coll:=mgm.Coll(&Book{})
// Do something with the collectionmgm automatically detect model's collection name:
book:=Book{}
// Print your model collection name.
collName := mgm.CollName(&book)
fmt.Println(collName) // print: booksYou can also set custom collection name for your model by
implementing CollectionNameGetter interface:
func (model *Book) CollectionName() string {
return "my_books"
}
// mgm return "my_books" collection
coll:=mgm.Coll(&Book{})Get collection by its name (without need of defining model for it):
coll := mgm.CollectionByName("my_coll")
//Do Aggregation,... with collectionCustomize model db by implementing CollectionGetter
interface:
func (model *Book) Collection() *mgm.Collection {
// Get default connection client
_,client,_, err := mgm.DefaultConfigs()
if err != nil {
panic(err)
}
db := client.Database("another_db")
return mgm.NewCollection(db, "my_collection")
}Or return model collection from another connection:
func (model *Book) Collection() *mgm.Collection {
// Create new client
client, err := mgm.NewClient(options.Client().ApplyURI("mongodb://root:12345@localhost:27017"))
if err != nil {
panic(err)
}
// Get model db
db := client.Database("my_second_db")
// return model custom collection
return mgm.NewCollection(db, "my_collection")
}while we can haveing Mongo Go Driver Aggregate features, mgm also provide simpler methods to aggregate:
Run aggregate and decode result:
authorCollName := mgm.Coll(&Author{}).Name()
result := []Book{}
// Lookup in just single line
_ := mgm.Coll(&Book{}).SimpleAggregate(&result, builder.Lookup(authorCollName, "auth_id", "_id", "author"))
// Multi stage(mix of mgm builders and raw stages)
_ := mgm.Coll(&Book{}).SimpleAggregate(&result,
builder.Lookup(authorCollName, "auth_id", "_id", "author"),
M{operator.Project: M{"pages": 0}},
)
// Do something with result...Do aggregate using mongo Aggregation method:
import (
"github.com/Kamva/mgm/v3"
"github.com/Kamva/mgm/v3/builder"
"github.com/Kamva/mgm/v3/field"
. "go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// Author model collection
authorColl := mgm.Coll(&Author{})
cur, err := mgm.Coll(&Book{}).Aggregate(mgm.Ctx(), A{
// S function get operators and return bson.M type.
builder.S(builder.Lookup(authorColl.Name(), "author_id", field.Id, "author")),
})More complex and mix with mongo raw pipelines:
import (
"github.com/Kamva/mgm/v3"
"github.com/Kamva/mgm/v3/builder"
"github.com/Kamva/mgm/v3/field"
"github.com/Kamva/mgm/v3/operator"
. "go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// Author model collection
authorColl := mgm.Coll(&Author{})
_, err := mgm.Coll(&Book{}).Aggregate(mgm.Ctx(), A{
// S function get operators and return bson.M type.
builder.S(builder.Lookup(authorColl.Name(), "author_id", field.Id, "author")),
builder.S(builder.Group("pages", M{"books": M{operator.Push: M{"name": "$name", "author": "$author"}}})),
M{operator.Unwind: "$books"},
})
if err != nil {
panic(err)
}- To run a transaction on default connection use
mgm.Transaction()function, e.g:
d := &Doc{Name: "Mehran", Age: 10}
err := mgm.Transaction(func(session mongo.Session, sc mongo.SessionContext) error {
// do not forget to pass the session's context to the collection methods.
err := mgm.Coll(d).CreateWithCtx(sc, d)
if err != nil {
return err
}
return session.CommitTransaction(sc)
})- To run a transaction with your context, use
mgm.TransactionWithCtx()method. - To run a transaction on another connection, use
mgm.TransactionWithClient()method.
We implemented these packages to simplify query and aggregate in mongo
builder: simplify mongo query and aggregation.
operator : contain mongo operators as predefined variable.
(e.g Eq = "$eq" , Gt = "$gt")
field : contain mongo fields using in aggregation
and ... as predefined variable.
(e.g LocalField = "localField", ForeignField = "foreignField")
example:
import (
"github.com/Kamva/mgm/v3"
f "github.com/Kamva/mgm/v3/field"
o "github.com/Kamva/mgm/v3/operator"
"go.mongodb.org/mongo-driver/bson"
)
// Instead of hard-coding mongo operators and fields
_, _ = mgm.Coll(&Book{}).Aggregate(mgm.Ctx(), bson.A{
bson.M{"$count": ""},
bson.M{"$project": bson.M{"_id": 0}},
})
// Use predefined operators and pipeline fields.
_, _ = mgm.Coll(&Book{}).Aggregate(mgm.Ctx(), bson.A{
bson.M{o.Count: ""},
bson.M{o.Project: bson.M{f.Id: 0}},
})New Features and bugs can be reported on Github issue tracker.
- Create new Topic at mongo-go-models Google Group
- Ask your question or request new feature by creating issue at Github issue tracker
- Fork the repository
- Clone your fork (
git clone https://github.com/<your_username>/mgm && cd mgm) - Create your feature branch (
git checkout -b my-new-feature) - Make changes and add them (
git add .) - Commit your changes (
git commit -m 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create new pull request
Mongo Go Models is released under the Apache License
