mgm

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Nov 19, 2024 License: Apache-2.0 Imports: 13 Imported by: 0

README

GoDoc Build Status

Mongo Go Models

The Mongo ODM for Go

Features

  • Define your models and perform 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 once and get collections anywhere you need them.
  • mgm predefines all Mongo operators and keys, so you don't have to hardcode them yourself.
  • mgm wraps the official Mongo Go Driver.

Requirements

  • Go 1.17 or higher.
  • MongoDB 3.6 and higher.

Installation

go get github.com/YspCoder/mgm

Usage

To get started, import the mgm package and setup the default config:

import (
   "github.com/YspCoder/mgm"
   "go.mongodb.org/mongo-driver/mongo/options"
)

func init() {
   // Setup the mgm default config
   err := mgm.SetDefaultConfig(nil, "mgm_lab", options.Client().ApplyURI("mongodb://root:12345@localhost:27017"))
}

Define your model:

type Book struct {
   // DefaultModel adds _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 to pass the model by reference (to update the model's "updated_at", "created_at" and "id" fields by mgm).
err := mgm.Coll(book).Create(book)

Find one document

// Get the document's collection
book := &Book{}
coll := mgm.Coll(book)

// Find and decode the doc to a book model.
_ = coll.FindByID("5e0518aa8f1a52b0b9410ee3", book)

// Get the first doc of the collection 
_ = coll.First(bson.M{}, book)

// Get the first doc of a collection using a filter
_ = coll.First(bson.M{"pages":400}, book)

Update a document

// Find your book
book := findMyFavoriteBook()

// and update it
book.Name = "Moulin Rouge!"
err := mgm.Coll(book).Update(book)

Delete a document

// Just find and delete your document
err := mgm.Coll(book).Delete(book)

Find and decode a result:

result := []Book{}

err := mgm.Coll(&Book{}).SimpleFind(&result, bson.M{"pages": bson.M{operator.Gt: 24}})
A Model's Default Fields

Each model by default (by using DefaultModel struct) has the following fields:

  • _id : The document ID.

  • created_at: The creation date of a doc. When saving a new doc, this is automatically populated by the Creating hook.

  • updated_at: The last updated date of a doc. When saving a doc, this is automatically populated by the Saving hook.

You can even implement your own default model to customize its fields.

A Model's Hooks

Each model has the following hooks:

  • Creating: Called when creating a new model. Signature : Creating(context.Context) error

  • Created: Called after a new model is created. Signature : Created(context.Context) error

  • Updating: Called when updating model. Signature : Updating(context.Context) error

  • Updated : Called after a model is updated. Signature : Updated(ctx context.Context, result *mongo.UpdateResult) error

  • Saving: Called when creating or updating a model. Signature : Saving(context.Context) error

  • Saved: Called after a model is created or updated. Signature: Saved(context.Context) error

  • Deleting: Called when deleting a model. Signature: Deleting(context.Context) error

  • Deleted: Called after a model is deleted. Signature: Deleted(ctx context.Context, result *mongo.DeleteResult) error

Notes about hooks:

  • Each model by default uses the Creating and Saving hooks, so if you want to define those hooks yourself, remember to invoke the DefaultModel hooks from your own hooks.
  • Collection methods that call these hooks:
    • Create & CreateWithCtx
    • Update & UpdateWithCtx
    • Delete & DeleteWithCtx

Example:

func (model *Book) Creating(ctx context.Context) error {
   // Call the DefaultModel Creating hook
   if err := model.DefaultModel.Creating(ctx); err!=nil {
      return err
   }

   // We can validate the fields of a model and return an error to prevent a document's insertion.
   if model.Pages < 1 {
      return errors.New("book must have at least one page")
   }

   return nil
}
Configuration

The mgm default configuration has a context timeout:

func init() {
   _ = mgm.SetDefaultConfig(&mgm.Config{CtxTimeout:12 * time.Second}, "mgm_lab", options.Client().ApplyURI("mongodb://root:12345@localhost:27017"))
}

// To get the context, just call the Ctx() method, assign it to a variable
ctx := mgm.Ctx()

// and use it
coll := mgm.Coll(&Book{})
coll.FindOne(ctx, bson.M{})

// Or invoke Ctx() and use it directly
coll.FindOne(mgm.Ctx(), bson.M{})
Collections

Get a model's collection:

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

// Do something with the collection

mgm automatically detects the name of a model's collection:

book := Book{}

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

You can also set a custom collection name for your model by implementing the CollectionNameGetter interface:

func (model *Book) CollectionName() string {
   return "my_books"
}

// mgm returns the "my_books" collection
coll := mgm.Coll(&Book{})

Get a collection by its name (without needing to define a model for it):

coll := mgm.CollectionByName("my_coll")
   
// Do Aggregation, etc. with the collection

Customize the model db by implementing the 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 a model's 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 the model's db
   db := client.Database("my_second_db")

   // return the model's custom collection
   return mgm.NewCollection(db, "my_collection")
}
Aggregation

While we can use Mongo Go Driver Aggregate features, mgm also provides simpler methods to perform aggregations:

Run an aggregation and decode the result:

authorCollName := mgm.Coll(&Author{}).Name()
result := []Book{}

// Lookup with just a single line of code
_ := 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 aggregations using the mongo Aggregation method:

import (
   "github.com/YspCoder/mgm"
   "github.com/YspCoder/mgm/builder"
   "github.com/YspCoder/mgm/field"
   . "go.mongodb.org/mongo-driver/bson"
   "go.mongodb.org/mongo-driver/bson/primitive"
)

// The Author model collection
authorColl := mgm.Coll(&Author{})

cur, err := mgm.Coll(&Book{}).Aggregate(mgm.Ctx(), A{
    // The S function accepts operators as parameters and returns a bson.M type.
    builder.S(builder.Lookup(authorColl.Name(), "author_id", field.Id, "author")),
})

A more complex example and mixes with mongo raw pipelines:

import (
   "github.com/YspCoder/mgm"
   "github.com/YspCoder/mgm/builder"
   "github.com/YspCoder/mgm/field"
   "github.com/YspCoder/mgm/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)
}
Transactions
  • To run a transaction on the default connection use the 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 own context, use the mgm.TransactionWithCtx() method.
  • To run a transaction on another connection, use the mgm.TransactionWithClient() method.

Other Mongo Go Models Packages

We implemented these packages to simplify queries and aggregations in mongo

builder: simplify mongo queries and aggregations.

operator : contains mongo operators as predefined variables.
(e.g Eq = "$eq" , Gt = "$gt")

field : contains mongo fields used in aggregations and ... as predefined variable. (e.g LocalField = "localField", ForeignField = "foreignField")

Example:

import (
  "github.com/YspCoder/mgm"
  f "github.com/YspCoder/mgm/field"
  o "github.com/YspCoder/mgm/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 the 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 can be requested and bugs can be reported on Github issue tracker.

Communicate With Us

Contributing

Open in Gitpod FOSSA Status

  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

FOSSA Status

Documentation

Index

Constants

View Source
const Version = "1.0.0"

Version of the package

Variables

This section is empty.

Functions

func CollName

func CollName(m Model) string

CollName returns a model's collection name. The `CollectionNameGetter` will be used if the model implements this interface. Otherwise, the collection name is inferred based on the model's type using reflection.

func Ctx

func Ctx() context.Context

Ctx function creates and returns a new context with a default timeout value.

func Escape

func Escape(key string) string

Escape escapes the mongo key for . and $ characters.

func NewClient

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

NewClient returns a new mongodb client.

func NewCtx

func NewCtx(timeout time.Duration) context.Context

NewCtx function creates and returns a new context with the specified timeout.

func ResetDefaultConfig

func ResetDefaultConfig()

ResetDefaultConfig resets the configuration values, client and database.

func SetDefaultConfig

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

SetDefaultConfig initializes the client and database using the specified configuration values, or default.

func Transaction

func Transaction(f TransactionFunc) error

Transaction creates a transaction with the default client.

func TransactionWithClient

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

TransactionWithClient creates a transaction with the given client.

func TransactionWithCtx

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

TransactionWithCtx creates a transaction with the given context and the default client.

func Unescape

func Unescape(key string) string

Unescape unescapes the mongo key for . and $ characters.

func UpsertTrueOption

func UpsertTrueOption() *options.UpdateOptions

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

Types

type Collection

type Collection struct {
	*mongo.Collection
}

Collection performs operations on models and the given Mongodb collection

func Coll

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

Coll returns the collection associated with a model.

func CollectionByName

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

CollectionByName returns a new collection using the current configuration values.

func NewCollection

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

NewCollection returns a new collection with the supplied database.

func (*Collection) Create

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

Create method inserts a new model into the database.

func (*Collection) CreateWithCtx

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

CreateWithCtx method inserts a new model into the database.

func (*Collection) Delete

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

Delete method deletes a model (doc) from a collection. To perform additional operations when deleting a model you should use hooks rather than overriding this method.

func (*Collection) DeleteWithCtx

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

DeleteWithCtx method deletes a model (doc) from a collection using the specified context. To perform additional operations when deleting a model you should use hooks rather than overriding this method.

func (*Collection) FindByID

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

FindByID method finds a doc and decodes it to a model, otherwise returns an error. The id field can be any value that if passed to the `PrepareID` method, it returns a valid ID (e.g string, bson.ObjectId).

func (*Collection) FindByIDWithCtx

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

FindByIDWithCtx method finds a doc and decodes it to a model, otherwise returns an error. The id field can be any value that if passed to the `PrepareID` method, it returns a valid ID (e.g string, bson.ObjectId).

func (*Collection) First

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

First method searches and returns the first document in the search results.

func (*Collection) FirstWithCtx

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

FirstWithCtx method searches and returns the first document in the search results.

func (*Collection) SimpleAggregate

func (coll *Collection) SimpleAggregate(results interface{}, stages []interface{}, opts ...*options.AggregateOptions) error

SimpleAggregate is just same as SimpleAggregateWithCtx, but doesn't get context param.

func (*Collection) SimpleAggregateCursor

func (coll *Collection) SimpleAggregateCursor(stages []interface{}, opts ...*options.AggregateOptions) (*mongo.Cursor, error)

SimpleAggregateCursor is just same as SimpleAggregateCursorWithCtx, but doesn't get context.

func (*Collection) SimpleAggregateCursorWithCtx

func (coll *Collection) SimpleAggregateCursorWithCtx(ctx context.Context, stages []interface{}, opts ...*options.AggregateOptions) (*mongo.Cursor, error)

SimpleAggregateCursorWithCtx performs a simple aggregation and returns a cursor over the resulting documents. Note: you can not use this method in a transaction because it does not accept a context. To participate in transactions, please use the regular aggregation method.

func (*Collection) SimpleAggregateFirst

func (coll *Collection) SimpleAggregateFirst(result interface{}, stages []interface{}, opts ...*options.AggregateOptions) (bool, error)

SimpleAggregateFirst is just same as SimpleAggregateFirstWithCtx, but doesn't get context param.

func (*Collection) SimpleAggregateFirstWithCtx

func (coll *Collection) SimpleAggregateFirstWithCtx(ctx context.Context, result interface{}, stages []interface{}, opts ...*options.AggregateOptions) (bool, error)

SimpleAggregateFirstWithCtx performs a simple aggregation, decodes the first aggregate result and returns it using the provided result parameter. The value of `stages` can be Operator|bson.M Note: you can not use this method in a transaction because it does not accept a context. To participate in transactions, please use the regular aggregation method.

func (*Collection) SimpleAggregateWithCtx

func (coll *Collection) SimpleAggregateWithCtx(ctx context.Context, results interface{}, stages []interface{}, opts ...*options.AggregateOptions) error

SimpleAggregateWithCtx performs a simple aggregation, decodes the aggregate result and returns the list using the provided result parameter. The value of `stages` can be Operator|bson.M Note: you can not use this method in a transaction because it does not accept a context. To participate in transactions, please use the regular aggregation method.

func (*Collection) SimpleFind

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

SimpleFind finds, decodes and returns the results.

func (*Collection) SimpleFindWithCtx

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

SimpleFindWithCtx finds, decodes and returns the results using the specified context.

func (*Collection) Update

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

Update function persists the changes made to a model to the database. Calling this method also invokes the model's mgm updating, updated, saving, and saved hooks.

func (*Collection) UpdateWithCtx

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

UpdateWithCtx function persists the changes made to a model to the database using the specified context. Calling this method also invokes the model's mgm updating, updated, saving, and saved hooks.

type CollectionGetter

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

CollectionGetter interface contains a method to return a model's custom collection.

type CollectionNameGetter

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

CollectionNameGetter interface contains a method to return the collection name of a model.

type Config

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

Config struct contains extra configuration properties for the mgm package.

func DefaultConfigs

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

DefaultConfigs returns the current configuration values, client and database.

type CreatedHook

type CreatedHook interface {
	Created() error
}

CreatedHook is called after a model has been created Deprecated: Please use CreatedHookWithCtx

type CreatedHookWithCtx

type CreatedHookWithCtx interface {
	Created(context.Context) error
}

CreatedHookWithCtx is called after a model has been created

type CreatingHook

type CreatingHook interface {
	Creating() error
}

CreatingHook is called before saving a new model to the database Deprecated: please use CreatingHookWithCtx

type CreatingHookWithCtx

type CreatingHookWithCtx interface {
	Creating(context.Context) error
}

CreatingHookWithCtx is called before saving a new model to the database

type DateFields

type DateFields struct {
	CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
	UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"`
}

DateFields struct contains the `created_at` and `updated_at` fields that autofill when inserting or updating a model.

func (*DateFields) Creating

func (f *DateFields) Creating() error

Creating hook is used here to set the `created_at` field value when inserting a new model into the database. TODO: get context as param the next version(4).

func (*DateFields) Saving

func (f *DateFields) Saving() error

Saving hook is used here to set the `updated_at` field value when creating or updating a model. TODO: get context as param the next version(4).

type DefaultModel

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

DefaultModel struct contains a model's default fields.

func (*DefaultModel) Creating

func (model *DefaultModel) Creating() error

Creating function calls the inner fields' defined hooks TODO: get context as param in the next version (4).

func (*DefaultModel) Saving

func (model *DefaultModel) Saving() error

Saving function calls the inner fields' defined hooks TODO: get context as param the next version(4).

type DeletedHook

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

DeletedHook is called after a model is deleted Deprecated: Please use DeletedHookWithCtx

type DeletedHookWithCtx

type DeletedHookWithCtx interface {
	Deleted(ctx context.Context, result *mongo.DeleteResult) error
}

DeletedHookWithCtx is called after a model is deleted

type DeletingHook

type DeletingHook interface {
	Deleting() error
}

DeletingHook is called before a model is deleted Deprecated: Please use DeletingHookWithCtx

type DeletingHookWithCtx

type DeletingHookWithCtx interface {
	Deleting(context.Context) error
}

DeletingHookWithCtx is called before a model is deleted

type IDField

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

IDField struct contains a model's ID field.

func (*IDField) GetID

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

GetID method returns a model's ID

func (*IDField) PrepareID

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

PrepareID method prepares the ID value to be used for filtering e.g convert hex-string ID value to bson.ObjectId

func (*IDField) SetID

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

SetID sets the value of a model's ID field.

type Model

type Model interface {
	// PrepareID converts the id value if needed, then
	// returns it (e.g convert string to objectId).
	PrepareID(id interface{}) (interface{}, error)

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

Model interface contains base methods that must be implemented by each model. If you're using the `DefaultModel` struct in your model, you don't need to implement any of these methods.

type SavedHook

type SavedHook interface {
	Saved() error
}

SavedHook is called after a model is saved to the database. Deprecated: Please use SavedHookWithCtx

type SavedHookWithCtx

type SavedHookWithCtx interface {
	Saved(context.Context) error
}

SavedHookWithCtx is called after a model is saved to the database.

type SavingHook

type SavingHook interface {
	Saving() error
}

SavingHook is called before a model (new or existing) is saved to the database. Deprecated: Please use SavingHookWithCtx

type SavingHookWithCtx

type SavingHookWithCtx interface {
	Saving(context.Context) error
}

SavingHookWithCtx is called before a model (new or existing) is saved to the database.

type TransactionFunc

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

TransactionFunc is a handler to manage a transaction.

type UpdatedHook

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

UpdatedHook is called after a model is updated Deprecated: Please use UpdatedHookWithCtx

type UpdatedHookWithCtx

type UpdatedHookWithCtx interface {
	Updated(ctx context.Context, result *mongo.UpdateResult) error
}

UpdatedHookWithCtx is called after a model is updated

type UpdatingHook

type UpdatingHook interface {
	Updating() error
}

UpdatingHook is called before updating a model Deprecated: Please use UpdatingHookWithCtx

type UpdatingHookWithCtx

type UpdatingHookWithCtx interface {
	Updating(context.Context) error
}

UpdatingHookWithCtx is called before updating a model

Directories

Path Synopsis
Package builder help us to write aggregates, filters, update maps simpler.
Package builder help us to write aggregates, filters, update maps simpler.
examples
internal

Jump to

Keyboard shortcuts

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