Documentation ¶
Overview ¶
This package is an object document mapper for mongodb which uses the mgo adapter.
First step is to create a model, for example:
type User struct { mongodm.DocumentBase `json:",inline" bson:",inline"` FirstName string `json:"firstname" bson:"firstname"` LastName string `json:"lastname" bson:"lastname"` UserName string `json:"username" bson:"username"` Messages interface{} `json:"messages" bson:"messages" model:"Message" relation:"1n" autosave:"true"` }
It is important that each schema embeds the IDocumentBase type (mongodm.DocumentBase) and make sure that it is tagged as 'inline' for json and bson. This base type also includes the default values id, createdAt, updatedAt and deleted. Those values are set automatically from the ODM. The given example also uses a relation (User has Messages). Relations must always be from type interface{} for storing bson.ObjectId OR a completely populated object. And of course we also need the related model for each stored message:
type Message struct { mongodm.DocumentBase `json:",inline" bson:",inline"` Sender string `json:"sender" bson:"sender"` Receiver string `json:"receiver" bson:"receiver"` Text string `json:"text" bson:"text"` }
Note that when you are using relations, each model will be stored in his own collection. So the values are not embedded and instead stored as object ID or array of object ID's.
To configure a relation the ODM understands three more tags:
model:"Message" This must be the struct type you want to relate to. Default: none, must be set relation:"1n" It is important that you specify the relation type one-to-one or one-to-many because the ODM must decide whether it accepts an array or object. Possible: "1n", "11" Default: "11" autosave:"true" If you manipulate values of the message relation in this example and then call 'Save()' on the user instance, this flag decides if this is possible or not. When autosave is activated, all relations will also be saved recursively. Otherwise you have to call 'Save()' manually for each relation. Possible: "true", "false" Default: "false"
But it is not necessary to always create relations - you also can use embedded types:
type Customer struct { mongodm.DocumentBase `json:",inline" bson:",inline"` FirstName string `json:"firstname" bson:"firstname"` LastName string `json:"lastname" bson:"lastname"` Address *Address `json:"address" bson:"address"` } type Address struct { City string `json:"city" bson:"city"` Street string `json:"street" bson:"street"` ZipCode int16 `json:"zip" bson:"zip"` }
Persisting a customer instance to the database would result in embedding an complete address object. You can embed all supported types.
Now that you got some models it is important to create a connection to the database and to register these models for the ODM.
Index ¶
- Constants
- func L(key string, values ...interface{}) string
- type Config
- type Connection
- type DocumentBase
- func (self *DocumentBase) AppendError(errorList *[]error, message string)
- func (self *DocumentBase) DefaultValidate() (bool, []error)
- func (self *DocumentBase) Delete() error
- func (self *DocumentBase) GetCreatedAt() time.Time
- func (self *DocumentBase) GetId() bson.ObjectId
- func (self *DocumentBase) GetUpdatedAt() time.Time
- func (self *DocumentBase) IsDeleted() bool
- func (self *DocumentBase) Populate(field ...string) error
- func (self *DocumentBase) Save() error
- func (self *DocumentBase) SetCollection(collection *mgo.Collection)
- func (self *DocumentBase) SetConnection(connection *Connection)
- func (self *DocumentBase) SetCreatedAt(createdAt time.Time)
- func (self *DocumentBase) SetDeleted(deleted bool)
- func (self *DocumentBase) SetDocument(document IDocumentBase)
- func (self *DocumentBase) SetId(id bson.ObjectId)
- func (self *DocumentBase) SetUpdatedAt(updatedAt time.Time)
- func (self *DocumentBase) Update(content interface{}) (error, map[string]interface{})
- func (self *DocumentBase) Validate(Values ...interface{}) (bool, []error)
- type DuplicateError
- type IDocumentBase
- type InvalidIdError
- type Model
- type NotFoundError
- type Query
- func (self *Query) Count() (n int, err error)
- func (self *Query) Exec(result interface{}) error
- func (self *Query) Limit(limit int) *Query
- func (self *Query) Populate(fields ...string) *Query
- func (self *Query) Select(selector interface{}) *Query
- func (self *Query) Skip(skip int) *Query
- func (self *Query) Sort(fields ...string) *Query
- type QueryError
- type ValidationError
Constants ¶
const REL_11 string = "11" // one-to-one relation
const REL_1N string = "1n" // one-to-many relation
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Config ¶
type Config struct { DatabaseHosts []string DatabaseName string DatabaseUser string DatabasePassword string DatabaseSource string DialInfo *mgo.DialInfo Locals map[string]string }
Simple config object which has to be passed/set to create a new connection
type Connection ¶
type Connection struct { Config *Config Session *mgo.Session // contains filtered or unexported fields }
The "Database" object which stores all connections
func Connect ¶
func Connect(config *Config) (*Connection, error)
Use this method to connect to a mongo db instance. The only parameter which is expected is a *mongodm.Config object.
For example:
dbConfig := &mongodm.Config{ DatabaseHosts: []string{"localhost"}, DatabaseName: "mongodm_sample", } connection, err := mongodm.Connect(dbConfig) if err != nil { log.E("Database connection error: %v", err) }
After this step you can register your created models. See: func (*Connection) Register
func (*Connection) Model ¶
func (self *Connection) Model(typeName string) *Model
To create actions on each collection you have to request a model instance with this method. Make sure that you registered your collections and schemes first, otherwise it will panic.
For example:
User := connection.Model("User") User.Find() ...
func (*Connection) Open ¶
func (self *Connection) Open() (err error)
Opens a database connection manually if the config was set. This method gets called automatically from the Connect() method.
func (*Connection) Register ¶
func (self *Connection) Register(document IDocumentBase, collectionName string)
It is necessary to register your created models to the ODM to work with. Within this process the ODM creates an internal model and type registry to work fully automatically and consistent. Make sure you already created a connection. Registration expects a pointer to an IDocumentBase type and the collection name where the documents should be stored in.
For example:
connection.Register(&User{}, "users") connection.Register(&Message{}, "messages") connection.Register(&Customer{}, "customers")
type DocumentBase ¶
type DocumentBase struct { Id bson.ObjectId `json:"id" bson:"_id,omitempty"` CreatedAt time.Time `json:"createdAt" bson:"createdAt"` UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` Deleted bool `json:"-" bson:"deleted"` // contains filtered or unexported fields }
This is the base type each model needs for working with the ODM. Of course you can create your own base type but make sure that you implement the IDocumentBase type interface!
func (*DocumentBase) AppendError ¶
func (self *DocumentBase) AppendError(errorList *[]error, message string)
func (*DocumentBase) DefaultValidate ¶
func (self *DocumentBase) DefaultValidate() (bool, []error)
func (*DocumentBase) Delete ¶
func (self *DocumentBase) Delete() error
Calling this method will not remove the object from the database. Instead the deleted flag is set to true. So you can use bson.M{"deleted":false} in your query to filter those documents.
func (*DocumentBase) GetCreatedAt ¶
func (self *DocumentBase) GetCreatedAt() time.Time
func (*DocumentBase) GetId ¶
func (self *DocumentBase) GetId() bson.ObjectId
func (*DocumentBase) GetUpdatedAt ¶
func (self *DocumentBase) GetUpdatedAt() time.Time
func (*DocumentBase) IsDeleted ¶
func (self *DocumentBase) IsDeleted() bool
func (*DocumentBase) Populate ¶
func (self *DocumentBase) Populate(field ...string) error
Populate works exactly like func (*Query) Populate. The only difference is that you call this method on each model which embeds the DocumentBase type. This means that you can populate single elements or sub-sub-levels.
For example:
User := connection.Model("User") user := &models.User{} err := User.Find().Exec(user) if err != nil { fmt.Println(err) } for _, user := range users { if user.FirstName == "Max" { //maybe NSA needs some information about Max's messages err := user.Populate("Messages") if err != nil { //some error occured continue } if messages, ok := user.Messages.([]*models.Message); ok { for _, message := range messages { fmt.Println(message.text) } } else { fmt.Println("something went wrong during cast. wrong type?") } } }
func (*DocumentBase) Save ¶
func (self *DocumentBase) Save() error
This method saves all changes for a document. Populated relations are getting converted to object ID's / array of object ID's so you dont have to handle this by yourself. Use this function also when the document was newly created, if it is not existent the method will call insert. During the save process createdAt and updatedAt gets also automatically persisted.
For example:
User := connection.Model("User") user := &models.User{} User.New(user) //this sets the connection/collection for this type and is strongly necessary(!) (otherwise panic) user.FirstName = "Max" user.LastName = "Mustermann" err := user.Save()
func (*DocumentBase) SetCollection ¶
func (self *DocumentBase) SetCollection(collection *mgo.Collection)
func (*DocumentBase) SetConnection ¶
func (self *DocumentBase) SetConnection(connection *Connection)
func (*DocumentBase) SetCreatedAt ¶
func (self *DocumentBase) SetCreatedAt(createdAt time.Time)
func (*DocumentBase) SetDeleted ¶
func (self *DocumentBase) SetDeleted(deleted bool)
func (*DocumentBase) SetDocument ¶
func (self *DocumentBase) SetDocument(document IDocumentBase)
func (*DocumentBase) SetId ¶
func (self *DocumentBase) SetId(id bson.ObjectId)
func (*DocumentBase) SetUpdatedAt ¶
func (self *DocumentBase) SetUpdatedAt(updatedAt time.Time)
func (*DocumentBase) Update ¶
func (self *DocumentBase) Update(content interface{}) (error, map[string]interface{})
func (*DocumentBase) Validate ¶
func (self *DocumentBase) Validate(Values ...interface{}) (bool, []error)
type DuplicateError ¶
type DuplicateError struct {
*QueryError
}
type IDocumentBase ¶
type IDocumentBase interface { GetId() bson.ObjectId SetId(bson.ObjectId) SetCreatedAt(time.Time) GetCreatedAt() time.Time SetUpdatedAt(time.Time) GetUpdatedAt() time.Time SetDeleted(bool) IsDeleted() bool SetCollection(*mgo.Collection) SetDocument(document IDocumentBase) SetConnection(*Connection) Save() error Update(interface{}) (error, map[string]interface{}) Validate(...interface{}) (bool, []error) DefaultValidate() (bool, []error) }
Interface which each collection document (model) hast to implement
type InvalidIdError ¶
type InvalidIdError struct {
*QueryError
}
type Model ¶
type Model struct { *mgo.Collection // contains filtered or unexported fields }
The Model type stores a databse connection and single collection for a specific type (e.g. "users"). New model types can be registered with the help of the connection (see func (*Connection) Register). Also an instance of this type embeds the default *mgo.Collection functionallity so you can call all native mgo collection API`s, too.
func (*Model) Find ¶
Use this method if you want to find a set of matching documents. Like FindOne, a map is expected as query param, but you also can call this method without any arguments. When the query is executed you have to pass a pointer to a slice of IDocumentBase types.
For example:
User := connection.Model("User") users := []*models.User{} err := User.Find(bson.M{"firstname" : "Max", "deleted" : false}).Populate("Messages").Exec(&users) if _, ok := err.(*mongodm.NotFoundError); ok { //you also can check the length of the slice //no records were found } else if err != nil { //database error } else { for user, _ := range users { fmt.Println("%v", user) } }
func (*Model) FindId ¶
If you have an object ID it is possible to find the matching document with this param.
For example:
User := connection.Model("User") user := &models.User{} err := User.FindId(bson.ObjectIdHex("55dccbf4113c615e49000001")).Select("firstname").Exec(user) if _, ok := err.(*mongodm.NotFoundError); ok { //no records were found } else if err != nil { //database error } else { fmt.Println("%v", user) }
func (*Model) FindOne ¶
If you want to find a single document by specifing query options you have to use this method. The query param expects a map (e.g. bson.M{}) and returns a query object which has to be executed manually. Make sure that you pass an IDocumentBase type to the exec function. After this you obtain the first matching object. You also can check the error if something was found.
For example:
User := connection.Model("User") user := &models.User{} err := User.FindOne(bson.M{"firstname" : "Max", "deleted" : false}).Populate("Messages").Exec(user) if _, ok := err.(*mongodm.NotFoundError); ok { //no records were found } else if err != nil { //database error } else { fmt.Println("%v", user) }
type NotFoundError ¶
type NotFoundError struct {
*QueryError
}
type Query ¶
type Query struct {
// contains filtered or unexported fields
}
Query is used to configure your find requests on a specific collection / model in more detail. Each query object method returns the same query reference to enable chains. After you have finished your configuration run the exec function (see: func (*Query) Exec).
For Example:
users := []*models.User{} User.Find(bson.M{"lastname":"Mustermann"}).Populate("Messages").Skip(10).Limit(5).Exec(&users)
func (*Query) Populate ¶
This method replaces the default object ID value with the defined relation type by specifing one or more field names. After it was succesfully populated you can access the relation field values. Note that you need type assertion for this process.
For example:
User := connection.Model("User") user := &models.User{} err := User.Find(bson.M{"firstname" : "Max"}).Populate("Messages").Exec(user) if err != nil { fmt.Println(err) } for _, user := range users { if messages, ok := user.Messages.([]*models.Message); ok { for _, message := range messages { fmt.Println(message.Sender) } } else { fmt.Println("something went wrong during cast. wrong type?") } }
Note: Only the first relation level gets populated! This process is not recursive.
type QueryError ¶
type QueryError struct {
// contains filtered or unexported fields
}
func (*QueryError) Error ¶
func (self *QueryError) Error() string
type ValidationError ¶
type ValidationError struct { *QueryError Errors []error }