diff --git a/README.md b/README.md index ad03c51..4673089 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,11 @@ -dal -=== +# DAL -Data access layer abstraction for go incremental applications. Currently only supports mongo via mgo. +Golang Database abstraction layer + +## Goals +- Allow higher level libraries e.g. github.com/goincremental/crud to share a common interface for persistence. + +- lightweight interface that can be implemented for specific databases in separate libraries + +## Non Goals +- This is not intended to be an ORM diff --git a/dal.go b/dal.go index eadec0d..4cc6c45 100644 --- a/dal.go +++ b/dal.go @@ -1,69 +1,36 @@ package dal -import ( - "errors" - "time" -) +import "errors" -var ErrNotFound = errors.New("ErrNotFound") +var ErrInvalidConnectionString = errors.New("Invalid Connection String") +var ErrAlreadyConnected = errors.New("Already Connected") +var ErrNotConnected = errors.New("Not Connected") -type Q map[string]interface{} +//Query is a simple mapping type that allows +//clients to filter the data they wish to retrieve +type Query map[string]interface{} -type DAL interface { - Connect(string) (Connection, error) - IsObjectIdHex(string) bool -} - -type Connection interface { - Clone() Connection - Close() - DB(s string) Database -} - -type Database interface { - C(string) Collection -} - -type Collection interface { - Find(Q) Query - EnsureIndex(Index) error - FindID(interface{}) Query - RemoveID(interface{}) error - UpsertID(interface{}, interface{}) (*ChangeInfo, error) - Upsert(interface{}, interface{}) (*ChangeInfo, error) - Insert(...interface{}) error - Save(interface{}, interface{}) (*ChangeInfo, error) - SaveID(interface{}, interface{}) (*ChangeInfo, error) +//ID is required to identify the item being operated on +type ID interface { + IsValid() bool } -type Query interface { - One(interface{}) error - All(interface{}) error - Sort(...string) Query - Iter() Iter - Apply(Change, interface{}) (*ChangeInfo, error) +//Item is an inteface that defines the necessary methods +//to enable Connection to perform its jobs +type Item interface { + GetID() ID } -type Iter interface { - Next(interface{}) bool -} - -type Index struct { - Key []string - Background bool - Sparse bool - ExpireAfter time.Duration -} - -type ChangeInfo struct { - Updated int - Removed int - UpsertedId interface{} -} - -type Change struct { - Update interface{} - Upsert bool - Remove bool - ReturnNew bool +//Connection is the main interface definition for DAL +//it defines the contract +type Connection interface { + Connect(string) (Connection, error) + Clone() (Connection, error) + Close() error + Tag(string) (Connection, error) + Create(Item) (interface{}, error) + Read(ID) (interface{}, error) + // Update(ID, Item) error + // Delete(ID) error + // Find(Query) (interface{}, error) } diff --git a/mgo.go b/mgo.go deleted file mode 100644 index f5d2ae3..0000000 --- a/mgo.go +++ /dev/null @@ -1,214 +0,0 @@ -package dal - -import ( - "encoding/gob" - "log" - - "gopkg.in/mgo.v2" - "gopkg.in/mgo.v2/bson" -) - -func init() { - gob.Register(ObjectID("")) -} - -type iter struct { - Iter - iter *mgo.Iter -} - -func (i *iter) Next(inter interface{}) bool { - return i.iter.Next(inter) -} - -type query struct { - query *mgo.Query -} - -func (q *query) One(i interface{}) error { - err := q.query.One(i) - if err == mgo.ErrNotFound { - err = ErrNotFound - } - return err -} - -func (q *query) All(i interface{}) error { - err := q.query.All(i) - if err == mgo.ErrNotFound { - err = ErrNotFound - } - return err -} - -func (q *query) Iter() Iter { - i := q.query.Iter() - return &iter{iter: i} -} - -func (q *query) Sort(s ...string) Query { - q2 := q.query.Sort(s...) - return &query{query: q2} -} - -func (q *query) Apply(change Change, result interface{}) (info *ChangeInfo, err error) { - c := mgo.Change{ - Update: change.Update, - Upsert: change.Upsert, - Remove: change.Remove, - ReturnNew: change.ReturnNew, - } - mci, err := q.query.Apply(c, result) - info = &ChangeInfo{} - if mci != nil { - info.Updated = mci.Updated - info.Removed = mci.Removed - info.UpsertedId = mci.UpsertedId - } - return -} - -type collection struct { - col *mgo.Collection -} - -func (c *collection) Find(q Q) Query { - bsonQ := c.col.Find(q) - return &query{query: bsonQ} -} - -func (c *collection) EnsureIndex(index Index) error { - i := mgo.Index{ - Key: index.Key, - Background: index.Background, - Sparse: index.Sparse, - ExpireAfter: index.ExpireAfter, - } - return c.col.EnsureIndex(i) -} - -func (c *collection) FindID(id interface{}) Query { - q := c.col.FindId(id) - return &query{query: q} -} - -func (c *collection) RemoveID(id interface{}) error { - return c.col.RemoveId(id) -} - -func (c *collection) UpsertID(id interface{}, update interface{}) (*ChangeInfo, error) { - return c.Upsert(bson.M{"_id": id}, update) -} - -func (c *collection) SaveID(id interface{}, item interface{}) (*ChangeInfo, error) { - return c.Upsert(bson.M{"_id": id}, bson.M{"$set": item}) -} - -func (c *collection) Save(selector interface{}, update interface{}) (*ChangeInfo, error) { - return c.Upsert(selector, bson.M{"$set": update}) -} - -func (c *collection) Upsert(selector interface{}, update interface{}) (*ChangeInfo, error) { - mci, err := c.col.Upsert(selector, update) - if err != nil { - log.Printf("Error upserting: %s\n", err) - } - ci := &ChangeInfo{} - if mci != nil { - ci.Updated = mci.Updated - ci.Removed = mci.Removed - ci.UpsertedId = mci.UpsertedId - } - log.Printf("change info %s", ci) - return ci, err -} - -func (c *collection) Insert(docs ...interface{}) error { - return c.col.Insert(docs) -} - -type database struct { - Database - db *mgo.Database -} - -func (d *database) C(name string) Collection { - col := d.db.C(name) - return &collection{col: col} -} - -type connection struct { - Connection - mgoSession *mgo.Session -} - -func (c *connection) Clone() Connection { - a := c.mgoSession.Clone() - return &connection{mgoSession: a} -} - -func (c *connection) Close() { - c.mgoSession.Close() -} - -func (c *connection) DB(name string) Database { - db := c.mgoSession.DB(name) - return &database{db: db} -} - -type dal struct { - DAL -} - -func (d *dal) Connect(s string) (Connection, error) { - log.Printf("Connect: %s\n", s) - mgoSession, err := mgo.Dial(s) - return &connection{mgoSession: mgoSession}, err -} - -func NewDAL() DAL { - return &dal{} -} - -type ObjectID string - -func (id ObjectID) GetBSON() (interface{}, error) { - return bson.ObjectId(id), nil -} - -func (id ObjectID) Hex() string { - return bson.ObjectId(id).Hex() -} - -func (id ObjectID) Valid() bool { - return bson.ObjectId(id).Valid() -} - -// MarshalJSON turns a dal.ObjectId into a json.Marshaller. -func (id ObjectID) MarshalJSON() ([]byte, error) { - return bson.ObjectId(id).MarshalJSON() -} - -// UnmarshalJSON turns *dal.ObjectId into a json.Unmarshaller. -func (id *ObjectID) UnmarshalJSON(data []byte) (err error) { - a := bson.NewObjectId() - err = a.UnmarshalJSON(data) - if err != nil { - log.Printf("ERROR: %s\n", err) - return - } - *id = ObjectID(a) - return -} - -func ObjectIDHex(s string) ObjectID { - return ObjectID(bson.ObjectIdHex(s)) -} - -func IsObjectIDHex(s string) bool { - return bson.IsObjectIdHex(s) -} - -func NewObjectID() ObjectID { - return ObjectID(bson.NewObjectId()) -}