9 Commits

Author SHA1 Message Date
75e76fb056 types 2022-02-09 21:57:31 +01:00
754c6dd624 db connect 2022-02-09 21:06:31 +01:00
6b367dbd7a db methods 2022-02-09 20:09:13 +01:00
98801edbc0 db.Session() 2022-02-09 18:55:58 +01:00
7dd238342c encapsulate ErrNotFound 2022-02-09 15:53:37 +01:00
115c288a0b encapsulate ErrNotFound 2022-02-09 15:49:15 +01:00
702a05f405 session copy 2022-02-09 15:24:44 +01:00
100eb29007 db via interfaces 2022-02-09 15:15:18 +01:00
b0af52178c db via interfaces 2022-02-09 15:11:15 +01:00
7 changed files with 292 additions and 35 deletions

22
crud.go
View File

@@ -7,7 +7,6 @@ import (
"strings" "strings"
"time" "time"
mgo "gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
) )
@@ -28,7 +27,7 @@ func (e *ErrorWithStack) Stack() string {
} }
// CreateDocument creates a document from specified model // CreateDocument creates a document from specified model
func CreateDocument(db *mgo.Database, m ModelInterface) error { func (db *MgoDatabase) CreateDocument(m ModelInterface) error {
m.PrepareInsert() m.PrepareInsert()
c := db.C(GetCollectionName(m)) c := db.C(GetCollectionName(m))
@@ -38,7 +37,7 @@ func CreateDocument(db *mgo.Database, m ModelInterface) error {
} }
// ReadDocument gets one document via its id // ReadDocument gets one document via its id
func ReadDocument(db *mgo.Database, m ModelInterface, selector bson.M) error { func (db *MgoDatabase) ReadDocument(m ModelInterface, selector bson.M) error {
c := db.C(GetCollectionName(m)) c := db.C(GetCollectionName(m))
q := c.FindId(m.GetID()) q := c.FindId(m.GetID())
@@ -137,7 +136,7 @@ func idToObjectID(filter interface{}) {
} }
// ReadCollection gets the filtered collection of the model // ReadCollection gets the filtered collection of the model
func ReadCollection(db *mgo.Database, results interface{}, filter bson.M, selector bson.M, offset int, limit int, sort []string, pipelineModifier PipelineModifierFunction) (err error) { func (db *MgoDatabase) ReadCollection(results interface{}, filter bson.M, selector bson.M, offset int, limit int, sort []string, pipelineModifier PipelineModifierFunction) (err error) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
err = &ErrorWithStack{Message: fmt.Sprintf("%v", r), StackTrace: string(debug.Stack())} err = &ErrorWithStack{Message: fmt.Sprintf("%v", r), StackTrace: string(debug.Stack())}
@@ -212,8 +211,7 @@ func ReadCollection(db *mgo.Database, results interface{}, filter bson.M, select
pipeline = pipelineModifier(pipeline) pipeline = pipelineModifier(pipeline)
} }
q := c.Pipe(pipeline).AllowDiskUse().Iter() _err = c.Pipe(pipeline).All(results)
_err = q.All(results)
} else { } else {
// search without pipe is faster // search without pipe is faster
@@ -245,7 +243,7 @@ func ReadCollection(db *mgo.Database, results interface{}, filter bson.M, select
} }
// ReadCollectionCount gets the count of elements in filtered collection // ReadCollectionCount gets the count of elements in filtered collection
func ReadCollectionCount(db *mgo.Database, m ModelInterface, filter bson.M) (count int, err error) { func (db *MgoDatabase) ReadCollectionCount(m ModelInterface, filter bson.M) (count int, err error) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
err = fmt.Errorf("%v", r) err = fmt.Errorf("%v", r)
@@ -257,7 +255,7 @@ func ReadCollectionCount(db *mgo.Database, m ModelInterface, filter bson.M) (cou
} }
// UpdateDocument updates a document from specified model // UpdateDocument updates a document from specified model
func UpdateDocument(db *mgo.Database, m ModelInterface, changes bson.M) error { func (db *MgoDatabase) UpdateDocument(m ModelInterface, changes bson.M) error {
m.PrepareUpdate() m.PrepareUpdate()
changes["updateTime"] = time.Now() changes["updateTime"] = time.Now()
@@ -268,7 +266,7 @@ func UpdateDocument(db *mgo.Database, m ModelInterface, changes bson.M) error {
} }
// UpsertDocument updates a document from specified model or inserts it, of not found // UpsertDocument updates a document from specified model or inserts it, of not found
func UpsertDocument(db *mgo.Database, m ModelInterface, changes bson.M) error { func (db *MgoDatabase) UpsertDocument(m ModelInterface, changes bson.M) error {
m.PrepareUpdate() m.PrepareUpdate()
changes["updateTime"] = time.Now() changes["updateTime"] = time.Now()
@@ -279,7 +277,7 @@ func UpsertDocument(db *mgo.Database, m ModelInterface, changes bson.M) error {
} }
// DeleteDocument deletes one document via its id // DeleteDocument deletes one document via its id
func DeleteDocument(db *mgo.Database, m ModelInterface) error { func (db *MgoDatabase) DeleteDocument(m ModelInterface) error {
c := db.C(GetCollectionName(m)) c := db.C(GetCollectionName(m))
err := c.RemoveId(m.GetID()) err := c.RemoveId(m.GetID())
@@ -288,12 +286,12 @@ func DeleteDocument(db *mgo.Database, m ModelInterface) error {
} }
// DeleteDocuments deletes documents found by filter // DeleteDocuments deletes documents found by filter
func DeleteDocuments(db *mgo.Database, m ModelInterface, filter bson.M) (removed int, err error) { func (db *MgoDatabase) DeleteDocuments(m ModelInterface, filter bson.M) (removed int, err error) {
c := db.C(GetCollectionName(m)) c := db.C(GetCollectionName(m))
info, err := c.RemoveAll(filter) info, err := c.RemoveAll(filter)
if info != nil { if info != nil {
removed = info.Removed removed = info.Removed()
} }
return removed, err return removed, err

2
go.mod
View File

@@ -1,4 +1,4 @@
module gitbase.de/gopackage/mgocrud module gitbase.de/gopackage/mgocrud/v2
go 1.16 go 1.16

View File

@@ -4,12 +4,11 @@ import (
"fmt" "fmt"
"reflect" "reflect"
mgo "gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
) )
// Lookup extends results with data for inline structs // Lookup extends results with data for inline structs
func Lookup(db *mgo.Database, structField string, results interface{}, selector bson.M) error { func (db *MgoDatabase) Lookup(structField string, results interface{}, selector bson.M) error {
t := reflect.TypeOf(results) t := reflect.TypeOf(results)
v := reflect.ValueOf(results) v := reflect.ValueOf(results)
@@ -94,7 +93,7 @@ func Lookup(db *mgo.Database, structField string, results interface{}, selector
// no entries to map // no entries to map
return nil return nil
} }
sArr := make([]bson.M, lArr, lArr) sArr := make([]bson.M, lArr)
aI := 0 aI := 0
for sID := range objectIDs { for sID := range objectIDs {
sArr[aI] = bson.M{ sArr[aI] = bson.M{
@@ -123,7 +122,7 @@ func Lookup(db *mgo.Database, structField string, results interface{}, selector
return fmt.Errorf("ID type in objects struct %+v is not bson.ObjectId", fieldType) return fmt.Errorf("ID type in objects struct %+v is not bson.ObjectId", fieldType)
} }
err := ReadCollection(db, objectResults, sQuery, selector, 0, 0, nil, nil) err := db.ReadCollection(objectResults, sQuery, selector, 0, 0, nil, nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -156,14 +155,10 @@ func Lookup(db *mgo.Database, structField string, results interface{}, selector
object := objectIDs[objectID] object := objectIDs[objectID]
field := elV.FieldByName(structField) field := elV.FieldByName(structField)
if object != nil && field.CanSet() { if fieldIsPtr {
objectVal := reflect.ValueOf(object) field.Set(reflect.ValueOf(object))
} else {
if fieldIsPtr { field.Set(reflect.ValueOf(object).Elem())
field.Set(objectVal)
} else {
field.Set(objectVal.Elem())
}
} }
} }

194
mgo.go Normal file
View File

@@ -0,0 +1,194 @@
package mgocrud
import (
"errors"
mgo "gopkg.in/mgo.v2"
)
type MgoConnection struct {
connection *mgo.Session
}
func NewMgoConnection(dial string) (Connection, error) {
connection, err := mgo.Dial(dial)
if err != nil {
return nil, err
}
connection.SetMode(mgo.Monotonic, true)
return &MgoConnection{connection: connection}, nil
}
func (c *MgoConnection) Close() {
c.connection.Close()
}
type MgoSession struct {
session *mgo.Session
}
func (s *MgoSession) Close() {
s.session.Close()
}
func (c *MgoConnection) NewSession() Session {
return &MgoSession{session: c.connection.Copy()}
}
type MgoDatabase struct {
database *mgo.Database
session Session
}
func (s *MgoSession) DB(name string) Database {
return &MgoDatabase{database: s.session.DB(name), session: s}
}
type MgoCollection struct {
collection *mgo.Collection
}
func (db *MgoDatabase) Session() Session {
return db.session
}
func (db *MgoDatabase) C(name string) Collection {
return &MgoCollection{collection: db.database.C(name)}
}
func (db *MgoDatabase) Name() string {
return db.database.Name
}
func (c *MgoCollection) Insert(docs ...interface{}) error {
return c.collection.Insert(docs...)
}
func (c *MgoCollection) UpdateId(id interface{}, update interface{}) error {
return c.collection.UpdateId(id, update)
}
func (c *MgoCollection) RemoveId(id interface{}) error {
return c.collection.RemoveId(id)
}
type MgoChangeInfo struct {
changeInfo *mgo.ChangeInfo
}
func (ci *MgoChangeInfo) Matched() int {
return ci.changeInfo.Matched
}
func (ci *MgoChangeInfo) Removed() int {
return ci.changeInfo.Removed
}
func (ci *MgoChangeInfo) Updated() int {
return ci.changeInfo.Updated
}
func (c *MgoCollection) Upsert(selector interface{}, update interface{}) (ChangeInfo, error) {
ci, err := c.collection.Upsert(selector, update)
if err != nil {
return nil, err
}
return &MgoChangeInfo{changeInfo: ci}, nil
}
func (c *MgoCollection) RemoveAll(filter interface{}) (ChangeInfo, error) {
ci, err := c.collection.RemoveAll(filter)
if err != nil {
return nil, err
}
return &MgoChangeInfo{changeInfo: ci}, nil
}
type MgoQuery struct {
query *mgo.Query
}
func (c *MgoCollection) FindId(id interface{}) Query {
return &MgoQuery{query: c.collection.FindId(id)}
}
func (c *MgoCollection) Find(query interface{}) Query {
return &MgoQuery{query: c.collection.Find(query)}
}
func (q *MgoQuery) Select(selector interface{}) Query {
q.query = q.query.Select(selector)
return q
}
func (q *MgoQuery) One(result interface{}) error {
err := q.query.One(result)
if err == mgo.ErrNotFound {
err = ErrNotFound
}
return err
}
func (q *MgoQuery) Sort(fields ...string) Query {
q.query = q.query.Sort(fields...)
return q
}
func (q *MgoQuery) Skip(n int) Query {
q.query = q.query.Skip(n)
return q
}
func (q *MgoQuery) Limit(n int) Query {
q.query = q.query.Limit(n)
return q
}
func (q *MgoQuery) All(result interface{}) error {
err := q.query.All(result)
if err == mgo.ErrNotFound {
err = ErrNotFound
}
return err
}
func (q *MgoQuery) Count() (int, error) {
c, err := q.query.Count()
if err == mgo.ErrNotFound {
err = ErrNotFound
}
return c, err
}
type MgoIndex struct {
index *mgo.Index
}
func NewMgoIndex(index mgo.Index) Index {
return &MgoIndex{index: &index}
}
func (c *MgoCollection) EnsureIndex(index Index) error {
if i, ok := index.(*MgoIndex); ok {
if i != nil && i.index != nil {
return c.collection.EnsureIndex(*i.index)
}
}
return errors.New("index parameter not initialized with mgo.Index")
}
type MgoPipe struct {
pipe *mgo.Pipe
}
func (p *MgoPipe) All(result interface{}) error {
err := p.pipe.All(result)
if err == mgo.ErrNotFound {
err = ErrNotFound
}
return err
}
func (c *MgoCollection) Pipe(pipeline interface{}) Pipe {
return &MgoPipe{pipe: c.collection.Pipe(pipeline).AllowDiskUse()}
}

View File

@@ -9,7 +9,7 @@ import (
) )
// EnsureIndex ensured mongodb index reflecting model struct index tag // EnsureIndex ensured mongodb index reflecting model struct index tag
func EnsureIndex(db *mgo.Database, m ModelInterface) error { func (db *MgoDatabase) EnsureIndex(m ModelInterface) error {
colName := GetCollectionName(m) colName := GetCollectionName(m)
col := db.C(colName) col := db.C(colName)
@@ -57,14 +57,14 @@ func EnsureIndex(db *mgo.Database, m ModelInterface) error {
case indexEl == "text": case indexEl == "text":
textFields = append(textFields, "$text:"+fieldbase+bsonField) textFields = append(textFields, "$text:"+fieldbase+bsonField)
default: default:
return fmt.Errorf("invalid index tag on collection %s.%s for field %s%s in model %+v", db.Name, colName, fieldbase, bsonField, t) return fmt.Errorf("invalid index tag on collection %s.%s for field %s%s in model %+v", db.Name(), colName, fieldbase, bsonField, t)
} }
} }
if len(index.Key) > 0 { if len(index.Key) > 0 {
// fmt.Println(bsonField, index) // fmt.Println(bsonField, index)
fmt.Printf("ensure index on collection %s.%s for field %s%s\n", db.Name, colName, fieldbase, bsonField) fmt.Printf("ensure index on collection %s.%s for field %s%s\n", db.Name(), colName, fieldbase, bsonField)
err := col.EnsureIndex(index) err := col.EnsureIndex(NewMgoIndex(index))
if err != nil { if err != nil {
return err return err
} }
@@ -93,13 +93,13 @@ func EnsureIndex(db *mgo.Database, m ModelInterface) error {
if len(textFields) > 0 { if len(textFields) > 0 {
// fmt.Println("$text", textFields) // fmt.Println("$text", textFields)
fmt.Printf("ensure text index on collection %s.%s for fields %v\n", db.Name, GetCollectionName(m), textFields) fmt.Printf("ensure text index on collection %s.%s for fields %v\n", db.Name(), GetCollectionName(m), textFields)
err := col.EnsureIndex(mgo.Index{ err := col.EnsureIndex(NewMgoIndex(mgo.Index{
Name: "textindex", Name: "textindex",
Key: textFields, Key: textFields,
DefaultLanguage: "german", DefaultLanguage: "german",
Background: false, Background: false,
}) }))
if err != nil { if err != nil {
return err return err
} }

71
types.go Normal file
View File

@@ -0,0 +1,71 @@
package mgocrud
import (
"errors"
"gopkg.in/mgo.v2/bson"
)
var (
ErrNotFound = errors.New("not found")
)
type Connection interface {
Close()
NewSession() Session
}
type Session interface {
Close()
DB(name string) Database
}
type Database interface {
Session() Session
C(name string) Collection
Name() string
EnsureIndex(m ModelInterface) error
ValidateObject(m ModelInterface, changes bson.M) error
ReadDocument(m ModelInterface, selector bson.M) error
CreateDocument(m ModelInterface) error
ReadCollection(results interface{}, filter bson.M, selector bson.M, offset int, limit int, sort []string, pipelineModifier PipelineModifierFunction) error
ReadCollectionCount(m ModelInterface, filter bson.M) (count int, err error)
UpdateDocument(m ModelInterface, changes bson.M) error
UpsertDocument(m ModelInterface, changes bson.M) error
DeleteDocument(m ModelInterface) error
DeleteDocuments(m ModelInterface, filter bson.M) (removed int, err error)
}
type Collection interface {
Insert(docs ...interface{}) error
UpdateId(id interface{}, update interface{}) error
RemoveId(id interface{}) error
Upsert(selector interface{}, update interface{}) (ChangeInfo, error)
RemoveAll(filter interface{}) (ChangeInfo, error)
FindId(id interface{}) Query
Find(query interface{}) Query
EnsureIndex(index Index) error
Pipe(pipeline interface{}) Pipe
}
type ChangeInfo interface {
Matched() int
Removed() int
Updated() int
}
type Query interface {
Select(selector interface{}) Query
One(result interface{}) error
Sort(fields ...string) Query
Skip(n int) Query
Limit(n int) Query
All(result interface{}) error
Count() (int, error)
}
type Index interface{}
type Pipe interface {
All(result interface{}) error
}

View File

@@ -2,12 +2,11 @@ package mgocrud
import ( import (
validator "gopkg.in/go-playground/validator.v8" validator "gopkg.in/go-playground/validator.v8"
mgo "gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
) )
// ValidateObject validates object via validator tag and custom method // ValidateObject validates object via validator tag and custom method
func ValidateObject(db *mgo.Database, m ModelInterface, changes bson.M) error { func (db *MgoDatabase) ValidateObject(m ModelInterface, changes bson.M) error {
// first validate via struct tag // first validate via struct tag
validator := validator.New(&validator.Config{ validator := validator.New(&validator.Config{
TagName: "validator", TagName: "validator",
@@ -19,7 +18,7 @@ func ValidateObject(db *mgo.Database, m ModelInterface, changes bson.M) error {
// next execute custom model validator if exists // next execute custom model validator if exists
if i, ok := m.(interface { if i, ok := m.(interface {
Validate(db *mgo.Database, changes bson.M) error Validate(db Database, changes bson.M) error
}); ok { }); ok {
return i.Validate(db, changes) return i.Validate(db, changes)
} }