mgm

package module
v3.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 28, 2020 License: Apache-2.0 Imports: 12 Imported by: 0

README

GoDoc Build Status

Mongo Go Models

The Mongo ODM for Go

Features

  • Define your models and do CRUD operations with hooks before/after each operation.
  • mgm makes Mongo search and aggregation super easy to do in Golang.
  • Just set up your configs one time and get collections anywhere you need those.
  • mgm predefined all Mongo operators and keys, So you don't have to hardcode them.
  • The wrapper of the official Mongo Go Driver.

Requirements

  • Go 1.10 or higher.
  • MongoDB 2.6 and higher.

Install

go get github.com/Kamva/mgm/v3

Usage

To 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}})

Model's default fields

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 by Creating hook.

  • updated_at: Last update date of doc. On save doc, autofill by Saving hook

Model's hooks:

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 Creating and Saving hooks, So if you want to define those hooks, call to DefaultModel hooks in your defined hooks.
  • collection's methods which call to the hooks:
    • Create & CreateWithCtx
    • Update & UpdateWithCtx
    • Delete & 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
}

config :

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{})

Collection

Get model collection:

coll:=mgm.Coll(&Book{})

// Do something with the collection

mgm automatically detect model's collection name:

book:=Book{}

// Print your model collection name.
collName := mgm.CollName(&book)
fmt.Println(collName) // print: books

You 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 collection

Customize 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")
}

Aggregation

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)
}

Transaction

  • 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.

Mongo Go Models other packages

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}},
})

Bugs / Feature request

New Features and bugs can be reported on Github issue tracker.

Communicate With Us

Contributing

  1. Fork the repository
  2. Clone your fork (git clone https://github.com/<your_username>/mgm && cd mgm)
  3. Create your feature branch (git checkout -b my-new-feature)
  4. Make changes and add them (git add .)
  5. Commit your changes (git commit -m 'Add some feature')
  6. Push to the branch (git push origin my-new-feature)
  7. Create new pull request

License

Mongo Go Models is released under the Apache License

Documentation

Index

Constants

View Source
const Version = "3.0.0"

Version of the package

Variables

This section is empty.

Functions

func CollName

func CollName(m Model) string

CollName check if you provided collection name in your model, return it's name, otherwise guess model collection's name.

func Ctx

func Ctx() context.Context

Ctx function create new context with default timeout and return it.

func NewClient

func NewClient(opts ...*options.ClientOptions) (*mongo.Client, error)

NewClient return new mongodb client.

func NewCtx

func NewCtx(timeout time.Duration) context.Context

NewCtx function create and return new context with your specified timeout.

func ResetDefaultConfig

func ResetDefaultConfig()

ResetDefaultConfig reset all of the default config

func SetDefaultConfig

func SetDefaultConfig(conf *Config, dbName string, opts ...*options.ClientOptions) (err error)

SetDefaultConfig initial default client and Database .

func Transaction

func Transaction(f TransactionFunc) error

Transaction run a transaction with default client..

func TransactionWithClient

func TransactionWithClient(ctx context.Context, client *mongo.Client, f TransactionFunc) error

TransactionWithClient run transaction with the given client.

func TransactionWithCtx

func TransactionWithCtx(ctx context.Context, f TransactionFunc) error

TransactionWithCtx run transaction with the given context and default client.

func UpsertTrueOption

func UpsertTrueOption() *options.UpdateOptions

UpsertTrueOption returns new instance of the UpdateOptions with upsert=true property.

Types

type Collection

type Collection struct {
	*mongo.Collection
}

Collection performs operations on models and given Mongodb collection

func Coll

func Coll(m Model, opts ...*options.CollectionOptions) *Collection

Coll return model's collection.

func CollectionByName

func CollectionByName(name string, opts ...*options.CollectionOptions) *Collection

CollectionByName return new collection from default config

func NewCollection

func NewCollection(db *mongo.Database, name string, opts ...*options.CollectionOptions) *Collection

NewCollection return new collection with passed database

func (*Collection) Create

func (coll *Collection) Create(model Model, opts ...*options.InsertOneOptions) error

Create method insert new model into database.

func (*Collection) CreateWithCtx

func (coll *Collection) CreateWithCtx(ctx context.Context, model Model, opts ...*options.InsertOneOptions) error

CreateWithCtx method insert new model into database.

func (*Collection) Delete

func (coll *Collection) Delete(model Model) error

Delete method delete model (doc) from collection. If you want to doing something on deleting some model use hooks, don't need to override this method.

func (*Collection) DeleteWithCtx

func (coll *Collection) DeleteWithCtx(ctx context.Context, model Model) error

DeleteWithCtx method delete model (doc) from collection. If you want to doing something on deleting some model use hooks, don't need to override this method.

func (*Collection) FindByID

func (coll *Collection) FindByID(id interface{}, model Model) error

FindByID method find a doc and decode it to model, otherwise return error. id field can be any value that if passed to `PrepareID` method, it return valid id(e.g string,bson.ObjectId).

func (*Collection) FindByIDWithCtx

func (coll *Collection) FindByIDWithCtx(ctx context.Context, id interface{}, model Model) error

FindByIDWithCtx method find a doc and decode it to model, otherwise return error. id field can be any value that if passed to `PrepareID` method, it return valid id(e.g string,bson.ObjectId).

func (*Collection) First

func (coll *Collection) First(filter interface{}, model Model, opts ...*options.FindOneOptions) error

First method search and return first document of search result.

func (*Collection) FirstWithCtx

func (coll *Collection) FirstWithCtx(ctx context.Context, filter interface{}, model Model, opts ...*options.FindOneOptions) error

FirstWithCtx method search and return first document of search result.

func (*Collection) SimpleAggregate

func (coll *Collection) SimpleAggregate(results interface{}, stages ...interface{}) error

SimpleAggregate does simple aggregation and decode aggregate result to the results. stages value can be Operator|bson.M Note: you can not use this method in a transaction because it does not get context. So you should use the regular aggregation method in transactions.

func (*Collection) SimpleAggregateCursor

func (coll *Collection) SimpleAggregateCursor(stages ...interface{}) (*mongo.Cursor, error)

SimpleAggregateCursor doing simple aggregation and return cursor. Note: you can not use this method in a transaction because it does not get context. So you should use the regular aggregation method in transactions.

func (*Collection) SimpleAggregateFirst

func (coll *Collection) SimpleAggregateFirst(result interface{}, stages ...interface{}) (bool, error)

SimpleAggregateFirst does simple aggregation and decode first aggregate result to the provided result param. stages value can be Operator|bson.M Note: you can not use this method in a transaction because it does not get context. So you should use the regular aggregation method in transactions.

func (*Collection) SimpleFind

func (coll *Collection) SimpleFind(results interface{}, filter interface{}, opts ...*options.FindOptions) error

SimpleFind find and decode result to results.

func (*Collection) SimpleFindWithCtx

func (coll *Collection) SimpleFindWithCtx(ctx context.Context, results interface{}, filter interface{}, opts ...*options.FindOptions) error

SimpleFindWithCtx find and decode result to results.

func (*Collection) Update

func (coll *Collection) Update(model Model, opts ...*options.UpdateOptions) error

Update function update save changed model into database. On call to this method also mgm call to model's updating,updated, saving,saved hooks.

func (*Collection) UpdateWithCtx

func (coll *Collection) UpdateWithCtx(ctx context.Context, model Model, opts ...*options.UpdateOptions) error

UpdateWithCtx function update save changed model into database. On call to this method also mgm call to model's updating,updated, saving,saved hooks.

type CollectionGetter

type CollectionGetter interface {
	// Collection method return collection
	Collection() *Collection
}

CollectionGetter interface contain method to return model's custom collection.

type CollectionNameGetter

type CollectionNameGetter interface {
	// CollectionName method return model collection's name.
	CollectionName() string
}

CollectionNameGetter interface contain method to return collection name of model.

type Config

type Config struct {
	// Set to 10 second (10*time.Second) for example.
	CtxTimeout time.Duration
}

Config struct contain extra config of mgm package.

func DefaultConfigs

func DefaultConfigs() (*Config, *mongo.Client, *mongo.Database, error)

DefaultConfigs return you'r default mongodb configs.

type CreatedHook

type CreatedHook interface {
	Created() error
}

CreatedHook call after model has been created

type CreatingHook

type CreatingHook interface {
	Creating() error
}

CreatingHook call before saving new model into database

type DateFields

type DateFields struct {
	CreatedAt time.Time `json:"created_at" bson:"created_at"`
	UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
}

DateFields struct contain `created_at` and `updated_at` fields that autofill on insert/update model.

func (*DateFields) Creating

func (f *DateFields) Creating() error

Creating hook used here to set `created_at` field value on inserting new model into database.

func (*DateFields) Saving

func (f *DateFields) Saving() error

Saving hook used here to set `updated_at` field value on create/update model.

type DefaultModel

type DefaultModel struct {
	IDField    `bson:",inline"`
	DateFields `bson:",inline"`
}

DefaultModel struct contain model's default fields.

func (*DefaultModel) Creating

func (model *DefaultModel) Creating() error

Creating function call to it's inner fields defined hooks

func (*DefaultModel) Saving

func (model *DefaultModel) Saving() error

Saving function call to it's inner fields defined hooks

type DeletedHook

type DeletedHook interface {
	Deleted(result *mongo.DeleteResult) error
}

DeletedHook call after model has been deleted)

type DeletingHook

type DeletingHook interface {
	Deleting() error
}

DeletingHook call before deleting model

type IDField

type IDField struct {
	ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
}

IDField struct contain model's ID field.

func (*IDField) GetID

func (f *IDField) GetID() interface{}

GetID method return model's id

func (*IDField) PrepareID

func (f *IDField) PrepareID(id interface{}) (interface{}, error)

PrepareID method prepare id value to using it as id in filtering,... e.g convert hex-string id value to bson.ObjectId

func (*IDField) SetID

func (f *IDField) SetID(id interface{})

SetID set id value of model's id field.

type Model

type Model interface {
	// PrepareID convert id value if need, and then
	// return it.(e.g convert string to objectId)
	PrepareID(id interface{}) (interface{}, error)

	GetID() interface{}
	SetID(id interface{})
}

Model interface is base method that must implement by each model, If you're using `DefaultModel` struct in your model, don't need to implement any of those method.

type SavedHook

type SavedHook interface {
	Saved() error
}

SavedHook call after model has been saved in database.

type SavingHook

type SavingHook interface {
	Saving() error
}

SavingHook call before save model(new or existed model) into database.

type TransactionFunc

type TransactionFunc func(session mongo.Session, sc mongo.SessionContext) error

TransactionFunc is handler to manage transaction.

type UpdatedHook

type UpdatedHook interface {
	Updated(result *mongo.UpdateResult) error
}

UpdatedHook call after model updated

type UpdatingHook

type UpdatingHook interface {
	Updating() error
}

UpdatingHook call when before updating model

Directories

Path Synopsis
Package builder help us to write aggregate,filter,update maps simpler.
Package builder help us to write aggregate,filter,update maps simpler.
examples
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL