marango

package module
v0.0.0-...-59b16e2 Latest Latest
Warning

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

Go to latest
Published: May 30, 2016 License: MIT Imports: 7 Imported by: 0

README

marango

marango is an ODM (Object Document Mapper) for MongoDB written in Go. It is written on top of the mgo library. marango doesn't try to replace mgo, but rather simply augments it.

####Why do I we need an ODM? You don't. Though, it is nice to have.. specially in the context of a web application. It allows you to couple your business logic with your data. But it is only useful as long as it doesn't get in your way, and you can drop down the to the DB driver any time.

Features :

  • Populate - MongoDB doesn't have JOINs, but we still want to query based on relationships. marango makes this task easy. Relationships are mapped via tags in the model definition. marango can run populate on a single ObjectId or a slice of them. And it can even run queries on these relationships! For example, lets say you have a social networking site and a user has 200 friends, you can run a query on those friends to only return the friends that are older than 30, sort them by their ages, and limit it to 5 results

  • Hooks - The hooks functionality allows you to register functions to be called before or after an action has taken place on the document. Ex: PreSave(), PreRemove(). Use these to consolidate your business logic in one place. See bellow for a full list of supported hooks

  • Virtuals - Store computed and temporary data along with your document. These values live only for the lifetime of the document, and are not persisted to the database.

  • Extends mgo.Collection - marango extends mgo's Collection struct. Reimplements operations that take just a bson.ObjectId to also accept string, because often times we only have a string representation of the ObjectId and we can let marango handle the conversion. Mgo's Query struct is replaced with one that understands the populate functions.

  • Convenience methods - All documents get methods such as Save(), Remove(), Apply(), Populate(), PopulateQuery()


API Docs

The docs include lots of detailed explainations and examples:

In all of its glory: http://godoc.org/github.com/mansoor-s/marango

Usage:

Note:

marango suppresses mgo's ErrNotFound error and instead provides the method IsValid() on every document. It returns true if the document was found, otherwise returns false

Define your Model:

package Models

type User struct {
	marango.Document 	`bson:"-"` //This is important! All models must have an anonymous composition of marango.Document 	
	Id 		 	bson.ObjectId 		`bson:"_id"`   	//Nothing different from mgo here
	Email 		string
	Password 	string
	Friends 	[]bson.ObjectId 	`model:"User"`  //define relationship - other Users
}

//This is an optional hook implemented to be called when the document is retrieved from the DB
func (u *User) OnResult() {
	u.Virtual.SetInt("totalFriends", len(u.Friends))
}

func (u *User) MySuperDuperMethod() {
	//do cool stuff
}

Implementation:

package main

import (
	"github.com/dsmontoya/marango"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)


func main() {
	//Business as usual.. dial up the DB
	session, err := mgo.Dial("localhost")
	if err != nil {
		panic(err)
	}
	defer session.Close()

	//Create new marango instance
	mago := marango.New(session, "MY_DB_NAME")

	//All models must be registered with marango
	//It expects an instance of the schema and the collection name that it represents documents in
	// and returns a pointer to a Model representing the mongodb collection
	User := mago.Register(User{}, "MY_COLLECTION_NAME")

	   /////////////////////////
	  //// Ready to rock!  ////
	 /////////////////////////

	user := &User{}
	//marango can infer the model to use from the type of the pointer we passed to Exec()
	//We can pass in both types "string" and "bson.ObjectId"
	err = mago.FindId("5232171fc081671e81000001").Exec(user)
	if err != nil {
		//do stuff
	}

	if !user.IsValid() {
		//user not found!
	}



	// We can also explictly call Find on the model pointer that we got back from marango.Register()
	// Also showing how to handle multiple results
	users := []*User{}
	User.Find(bson.M{"age": 40, "planet": "Earth"}).Sort("firstname", "-lastname").Limit(10).Exec(&users)


	//Using Populate()
	//A populate operation can either be part of a query or can be performed on an existing document

	//////////////////
	//Using Populate In a query:
	//////////////////
	users := []*User{}
	User.Find(bson.M{"age": 40}).Sort("firstname").Limit(10).Populate("Friends").Exec(&users)
	//In this example, lets assume that we got back 10 results. For all of those 10 results, marango just populated the references
	//made in its "Friends" field

	//To access a populated field:
	theFirstUser := users[0]
	thisUsersFriends := []*User{}
	theFirstUser.Populated("Friends", thisUsersFriends)
	// THAT WAS EASY!!!

	///////////////////
	//Using Populate on an existing document ... say one that you queried from the DB earlier
	//////////////////
	//This example will also show the PopulateQuery method, which allows you to further filter and sort your relationships!
	friends := []*User{}
	popQuery := User.Find(bson.M{"age": bson.M{"$gt": 30}}).Sort("age").Limit(5)
	err := myDoc.PopulateQuery(popQuery, friends)


	//The model inherits from the mgo.C struct that it represents. For instance, even though marango.Model does not implement
	// an EnsureIndex() method, when called, the underlying mgo.C.EnsureIndex() method is called.
	User.EnsureIndex(.....)
	//or
	User.UpdateAll(.....)
	//or
	User.Count()
}

###Hooks (Hooks are optional):

PreSave()
PostSave()
PreRemove()
PostRemove()
OnCreate()
OnResult()

Implement these methods in your schema and they will be called when triggered.

Look at the API docs for marango.Document for more info

###Virtuals Example Usage:

//get the document from DB here..

//Setting virtuals:
myDoc.Virtual.SetString("stringVal", "ABCDEF")

//Store an arbitrary type:
//myIds := []bson.ObjectId{...........}
myDoc.Virtual.Set("myIds", myIds)

//Getting arbitrary typed values
idsInterface, ok := myDoc.Virtual.Get("myIds")
if !ok {
	//handle not found here
}
//assert it back to the type we want
myIds := idsInterface.([]bson.ObjectId)

Documentation

Overview

Package Marango provides an intuitive ODM (Object Document Model) library for working with MongoDB documents. It builds on top of the awesome mgo library

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ObjectId

func ObjectId(id string) bson.ObjectId

ObjectId converts a string hex representation of an ObjectId into type bson.ObjectId.

Types

type D

type D bson.D

Convenient access to bson.D

type Document

type Document struct {
	C     *mgo.Collection
	Model *Model

	Found   bool
	Virtual *Virtual
	// contains filtered or unexported fields
}

func (*Document) Apply

func (d *Document) Apply(update interface{}) error

implement Apply function here it takes care of applying changes/merging to the document from another document

func (*Document) IsValid

func (d *Document) IsValid() bool

Use this method to check if this document is in fact populated with data from the database. Sleep suppresses mgo's ErrNotFound error and instead provides this interface for checking if results were returned.

func (*Document) OnCreate

func (d *Document) OnCreate()

OnCreate is a stand-in method that can be implemented in the schema defination struct to be called when the document is created using Sleep.Model.CreateDoc method. Use `OnResult()` to be called then the document is queried from the database.

The method should have a reciever that is a pointer to the schema type

func (*Document) OnResult

func (d *Document) OnResult()

OnResult is a stand-in method that can be implemented in the schema defination struct to be called when the document is created from the results out of the database. Use `OnCreate()` to be called then the document is created using Sleep.Model.CreateDoc method

The method should have a reciever that is a pointer to the schema type

func (*Document) Populate

func (d *Document) Populate(fields ...string) error

Same as Query.Populate() except it can be called on an existing document.

func (*Document) PopulateOne

func (d *Document) PopulateOne(field string, value interface{}) error

Same as populate but used to populate only a single field. Its last parameter is a pointer to the variable to hold the value of the result

func (*Document) PopulateQuery

func (d *Document) PopulateQuery(path string, q *Query, value interface{}) error

Same as Query.PopulateQuery() except the last parameter is a pointer to the variable to hold the value of the result.

func (*Document) Populated

func (d *Document) Populated(path string, result interface{}) bool

Populated gives access to the document's populated fields. This method does NOT make a database query. It returns only existing populated fields.

The path must be exactly the same as what was passed to Query.Populate() or Query.PopulateQuery() and is case sensitive.

The result parameter must be of the correct Type. For example, if the field was defined as such in the schema:

Foo: bson.ObjectId   `model:"Bar"`

Then the argument must be of type *Bar Or, if the field was defined as:

Foo: []bson.ObjectId   `model:"Bar"`

Then the argument must be of type: *[]*Bar

func (*Document) PostRemove

func (d *Document) PostRemove()

PostRemove is a stand-in method that can be implemented in the schema defination struct to be called after the document is removed from the database.

The method should have a reciever that is a pointer to the schema type

func (*Document) PostSave

func (d *Document) PostSave()

PostSave is a stand-in method that can be implemented in the schema defination struct to be called after the document is saved to the database.

The method should have a reciever that is a pointer to the schema type

func (*Document) PreRemove

func (d *Document) PreRemove()

PreRemove is a stand-in method that can be implemented in the schema defination struct to be called before the document is removed from the database.

The method should have a reciever that is a pointer to the schema type

func (*Document) PreSave

func (d *Document) PreSave()

PreSave is a stand-in method that can be implemented in the schema defination struct to be called before the document is saved to the database.

The method should have a reciever that is a pointer to the schema type

func (*Document) Remove

func (d *Document) Remove() error

Removes the document from the database

func (*Document) Save

func (d *Document) Save() error

Save uses MongoDB's upsert command to either update an existing document or insert it into the collection. The document's schma MUST have an Id field.

type M

type M bson.M

Convenient access to bson.M

type Marango

type Marango struct {
	Db *mgo.Database
	// contains filtered or unexported fields
}

func New

func New(s *mgo.Session, db string) *Marango

New returns a new intance of the Marango type

func (*Marango) C

func (z *Marango) C(model string) (*mgo.Collection, bool)

C gives access to the underlying *mgo.Collection value for a model. The model name is case sensitive.

func (*Marango) CreateDoc

func (z *Marango) CreateDoc(doc interface{})

CreateDoc conditions an instance of the model to become a document. Will create an ObjectId for the document.

See Model.CreateDoc. They are the same

func (*Marango) Model

func (z *Marango) Model(name string) *Model

Model returns a pointer to the Model of the registered schema

func (*Marango) ObjectId

func (z *Marango) ObjectId(id string) bson.ObjectId

See ObjectId

func (*Marango) Register

func (z *Marango) Register(schema interface{}, collectionName string) *Model

Register registers a given schema and its corresponding collection name with Marango. All schemas MUST be registered using this function. Function will return a pointer to the Marango.Model value for this model

func (*Marango) SetModelTag

func (z *Marango) SetModelTag(key string)

SetModelTag changes the default tag key of `model` to an arbitrary key. This value is read to make relationships for populting based on ObjectIds

type Model

type Model struct {
	*mgo.Collection
	//C is the underlying mgo.collection value for this model.
	//Refer to http://godoc.org/labix.org/v2/mgo#Collection for full usage information
	C *mgo.Collection
	// contains filtered or unexported fields
}

Model struct represents a collection in MongoDB. It inherits from mgo.Collection and overwrides the functions below to provide more functionality and to create compatibility with Marango.

func (*Model) CreateDoc

func (m *Model) CreateDoc(i interface{})

CreateDoc conditions an instance of the model to become a document.

What it means in pratical terms is that Create sets a value for the schema's *Marango.Document anonymous field. This will allow Marango to work with the value as a document. Calling this function is only necessary when wishing to create documents "manually". It is not necessary to call this function on a value that will be holding the result of a query; Marango will do that.

After a document is created with this function, the document will expose all of the public methods and fields of the Marango.Model struct as its own.

func (*Model) Find

func (m *Model) Find(query interface{}) *Query

Find starts and returns a chainable *Query value This function passes the passed value to mgo.Collection.Find

To borrow from the mgo docs: "The document(argument) may be a map or a struct value capable of being marshalled with bson. The map may be a generic one using interface{} for its key and/or values, such as bson.M, or it may be a properly typed map. Providing nil as the document is equivalent to providing an empty document such as bson.M{}".

Further reading: http://godoc.org/labix.org/v2/mgo#Collection.Find

func (*Model) FindId

func (m *Model) FindId(id interface{}) *Query

FindId is a convenience function equivalent to:

query := myModel.Find(bson.M{"_id": id})

Unlike the Mgo.Collection.FindId function, this function will accept Id both in hex representation as a string or a bson.ObjectId.

FindId will return a chainable *Query value

func (*Model) RemoveId

func (m *Model) RemoveId(id interface{}) error

RemoveId removes a document from the collection based on its _id field. Same as mgo.Collection.RemoveId, except that it accepts the Id as a string or bson.ObjectId

See http://godoc.org/labix.org/v2/mgo#Collection.RemoveId

func (*Model) UpdateId

func (m *Model) UpdateId(id interface{}, change interface{}) error

UpdateId updates a document in the collection based on its _id field. Same as mgo.Collection.UpdateId, except that it accepts the Id as a string or bson.ObjectId

See http://godoc.org/labix.org/v2/mgo#Collection.UpdateId

func (*Model) UpsertId

func (m *Model) UpsertId(id interface{}, change interface{}) (*mgo.ChangeInfo, error)

UpsertId updates or inserts a document in the collection based on its _id field. Same as mgo.Collection.UpsertId, except that it accepts the Id as a string or bson.ObjectId

See http://godoc.org/labix.org/v2/mgo#Collection.UpsertId

type Query

type Query struct {
	// contains filtered or unexported fields
}

func (*Query) Exec

func (query *Query) Exec(result interface{}) error

Exec executes the query.

What collection to query on is determined by the result parameter. Exec does the job of both mgo.Collection.One() and mgo.Collection.All().

Example 1 (Equivalent to mgo.Collection.One() ):

type Foo struct {...}
foo := &Foo{} //foo is a pointer to the value for a single Foo struct
marango.Find(bson.M{"location:": "Earth"}).Exec(foo)

Example 2 (Equivalent to mgo.Collection.All() ):

type Foo struct {...}
foo := []*Foo{} //foo is the value for a slice of pointers to Foo structs
marango.Find(bson.M{"location:": "Earth"}).Exec(&foo)
//Another example showing further filtering
marango.Find(bson.M{"location:": "Earth"}).Sort("name", "age").Limit(200).Exec(&foo)

func (*Query) Limit

func (q *Query) Limit(lim int) *Query

Limit sets the maximum number of document the database should return

func (*Query) Populate

func (q *Query) Populate(fields ...string) *Query

Populate sets the fields to be automatically populated based on the field's bson.ObjectId value.

This function takes a variable number of arguments. Each argument must be the full path to the field to be populated. The field to be populated can be either of type bson.ObjectId or []bson.ObjectId. The field must also have a tag with the key "model" and a case sensative value with the name of the model.

Example:

type Contact struct {
	BusinessPartner   bson.ObjectId
	Competitors       []bson.ObjectId
}

type Person struct {
	Marango.Document `bson:"-"`
	Name           string
	PhoneNumber    string
	Friend         bson.ObjectId    `model:"Person"`
	Acquaintances  []bson.ObjectId  `model:"Person"`
	Contacts       []Contact
}

marango.FindId("...").Populate("Friend", "Acquaintances").Exec(personResult)

The path argument can also describe embeded structs. Every step into an embeded struct is seperated by a "."

Example:

marango.FindId("...").Populate("Contacts.BusinessPartner", "Contacts.Competitors").Exec(personResult)

func (*Query) PopulateQuery

func (q *Query) PopulateQuery(field string, query *Query) *Query

PopulateQuery does the same thing the Populate function does, except it only takes one field path at a time and the second parameter is a value of type *Marango.Query

Example (continuing with the Populate example):

//I want 10 acquantances that are older than 30 and sorted by their names
popQuery := marango.Find(bson.M{"age": bson.M{"$gt": 30} }).Limit(10).Sort("name")
marango.FindId("...").PopulateQuery("Acquaintances", popQuery).Exec(personResult)

You may also call Populate() and PopulateQuery() on the populate Query value to run auto-population on the populated results.

popQuery := marango.Find(bson.M{"age": bson.M{"$gt": 30} }).Limit(10).Sort("name").Populate("Friend")

func (*Query) Select

func (q *Query) Select(selection interface{}) *Query

Select enables selecting which fields should be retrieved for the results found. For example, the following query would only retrieve the name field:

err := marango.Find(bson.M{"age": 50}).Select(bson.M{"name": 1}).Exec(result)

Note 1: The _id field is always selected.. unless explicitly stated otherwise

Note 2**: If only some fields are selected for retrieval and then the Save() is called on the document, the fields not retrieved will be blank and will overwrite the database values with the default value for their respective types.

func (*Query) Skip

func (q *Query) Skip(skip int) *Query

Skip skips over the n initial documents from the query results. Using Skip only makes sense with ordered results and capped collections where documents are naturally ordered by insertion time.

func (*Query) Sort

func (q *Query) Sort(fields ...string) *Query

Sort sets the fields by which the database should sort the query results

Example: For example:

query1 := marango.Find(nil).Sort("firstname", "lastname")
query2 := marango.Find(nil).Sort("-age")
query3 := marango.Find(nil).Sort("$natural")

Further reading: http://godoc.org/labix.org/v2/mgo#Query.Sort

type Virtual

type Virtual struct {
	// contains filtered or unexported fields
}

Virtual holds temporary/computed values related to the document. As of right now Virtual implements getters and setters for types most commonly used in web developement. It also implements a generic getter and setter for storing and retrieving any type as type interface{} and must be asserted to its proper type upon retrieval.

These fields that are kept for the lifetime of the document in memory and are NOT persisted to the database.

func (*Virtual) Get

func (v *Virtual) Get(name string) (interface{}, bool)

Get returns the stored value with the given name as type interface{}. It also returns a boolean value indicating whether a value was found.

Get is a generic getter for any arbitrary type

func (*Virtual) GetBool

func (v *Virtual) GetBool(name string) (bool, bool)

Get returns the stored boolean value with the given name. It also returns a boolean value indicating whether a value was found.

func (*Virtual) GetFloat

func (v *Virtual) GetFloat(name string) (float64, bool)

GetFloat returns the stored float64 value with the given name. It also returns a boolean value indicating whether a value was found.

func (*Virtual) GetInt

func (v *Virtual) GetInt(name string) (int, bool)

GetInt returns the stored int value with the given name. It also returns a boolean value indicating whether a value was found.

func (*Virtual) GetObjectId

func (v *Virtual) GetObjectId(name string) (bson.ObjectId, bool)

GetObjectId returns the stored bson.ObjectId value with the given name. It also returns a boolean value indicating whether a value was found.

func (*Virtual) GetString

func (v *Virtual) GetString(name string) (string, bool)

GetString returns the stored string value with the given name. It also returns a boolean value indicating whether a value was found.

func (*Virtual) GetTime

func (v *Virtual) GetTime(name string) (time.Time, bool)

GetTime returns the stored time.Time value with the given name. It also returns a boolean value indicating whether a value was found.

func (*Virtual) Set

func (v *Virtual) Set(name string, val interface{})

Set stores the value with the given name as type interface{}.

Set is a generic setter for any arbitrary type

func (*Virtual) SetBool

func (v *Virtual) SetBool(name string, val bool)

SetBool stores the boolean value with the given name.

func (*Virtual) SetFloat

func (v *Virtual) SetFloat(name string, val float64)

SetFloat stores the float64 value with the given name.

func (*Virtual) SetInt

func (v *Virtual) SetInt(name string, val int)

SetInt stores the int value with the given name.

func (*Virtual) SetObjectId

func (v *Virtual) SetObjectId(name string, val bson.ObjectId)

SetObjectId stores the bson.ObjectId value with the given name.

func (*Virtual) SetString

func (v *Virtual) SetString(name string, val string)

SetString stores the string value with the given name.

func (*Virtual) SetTime

func (v *Virtual) SetTime(name string, val time.Time)

SetTime stores the time.Time value with the given name.

Jump to

Keyboard shortcuts

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