1 Commits

Author SHA1 Message Date
03b83d3fb9 nil lookup fix 2022-07-25 10:24:56 +02:00
6 changed files with 35 additions and 223 deletions

22
crud.go
View File

@@ -7,6 +7,7 @@ import (
"strings" "strings"
"time" "time"
mgo "gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
) )
@@ -27,7 +28,7 @@ func (e *ErrorWithStack) Stack() string {
} }
// CreateDocument creates a document from specified model // CreateDocument creates a document from specified model
func (db *Database) CreateDocument(m ModelInterface) error { func CreateDocument(db *mgo.Database, m ModelInterface) error {
m.PrepareInsert() m.PrepareInsert()
c := db.C(GetCollectionName(m)) c := db.C(GetCollectionName(m))
@@ -37,7 +38,7 @@ func (db *Database) CreateDocument(m ModelInterface) error {
} }
// ReadDocument gets one document via its id // ReadDocument gets one document via its id
func (db *Database) ReadDocument(m ModelInterface, selector bson.M) error { func ReadDocument(db *mgo.Database, 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())
@@ -136,7 +137,7 @@ func idToObjectID(filter interface{}) {
} }
// ReadCollection gets the filtered collection of the model // ReadCollection gets the filtered collection of the model
func (db *Database) ReadCollection(results interface{}, filter bson.M, selector bson.M, offset int, limit int, sort []string, pipelineModifier PipelineModifierFunction) (err error) { func ReadCollection(db *mgo.Database, 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())}
@@ -211,7 +212,8 @@ func (db *Database) ReadCollection(results interface{}, filter bson.M, selector
pipeline = pipelineModifier(pipeline) pipeline = pipelineModifier(pipeline)
} }
_err = c.Pipe(pipeline).All(results) q := c.Pipe(pipeline).AllowDiskUse().Iter()
_err = q.All(results)
} else { } else {
// search without pipe is faster // search without pipe is faster
@@ -243,7 +245,7 @@ func (db *Database) ReadCollection(results interface{}, filter bson.M, selector
} }
// ReadCollectionCount gets the count of elements in filtered collection // ReadCollectionCount gets the count of elements in filtered collection
func (db *Database) ReadCollectionCount(m ModelInterface, filter bson.M) (count int, err error) { func ReadCollectionCount(db *mgo.Database, 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)
@@ -255,7 +257,7 @@ func (db *Database) ReadCollectionCount(m ModelInterface, filter bson.M) (count
} }
// UpdateDocument updates a document from specified model // UpdateDocument updates a document from specified model
func (db *Database) UpdateDocument(m ModelInterface, changes bson.M) error { func UpdateDocument(db *mgo.Database, m ModelInterface, changes bson.M) error {
m.PrepareUpdate() m.PrepareUpdate()
changes["updateTime"] = time.Now() changes["updateTime"] = time.Now()
@@ -266,7 +268,7 @@ func (db *Database) UpdateDocument(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 (db *Database) UpsertDocument(m ModelInterface, changes bson.M) error { func UpsertDocument(db *mgo.Database, m ModelInterface, changes bson.M) error {
m.PrepareUpdate() m.PrepareUpdate()
changes["updateTime"] = time.Now() changes["updateTime"] = time.Now()
@@ -277,7 +279,7 @@ func (db *Database) UpsertDocument(m ModelInterface, changes bson.M) error {
} }
// DeleteDocument deletes one document via its id // DeleteDocument deletes one document via its id
func (db *Database) DeleteDocument(m ModelInterface) error { func DeleteDocument(db *mgo.Database, m ModelInterface) error {
c := db.C(GetCollectionName(m)) c := db.C(GetCollectionName(m))
err := c.RemoveId(m.GetID()) err := c.RemoveId(m.GetID())
@@ -286,12 +288,12 @@ func (db *Database) DeleteDocument(m ModelInterface) error {
} }
// DeleteDocuments deletes documents found by filter // DeleteDocuments deletes documents found by filter
func (db *Database) DeleteDocuments(m ModelInterface, filter bson.M) (removed int, err error) { func DeleteDocuments(db *mgo.Database, 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/v2 module gitbase.de/gopackage/mgocrud
go 1.16 go 1.16

View File

@@ -4,11 +4,12 @@ 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 (db *Database) Lookup(structField string, results interface{}, selector bson.M) error { func Lookup(db *mgo.Database, structField string, results interface{}, selector bson.M) error {
t := reflect.TypeOf(results) t := reflect.TypeOf(results)
v := reflect.ValueOf(results) v := reflect.ValueOf(results)
@@ -93,7 +94,7 @@ func (db *Database) Lookup(structField string, results interface{}, selector bso
// no entries to map // no entries to map
return nil return nil
} }
sArr := make([]bson.M, lArr) sArr := make([]bson.M, lArr, lArr)
aI := 0 aI := 0
for sID := range objectIDs { for sID := range objectIDs {
sArr[aI] = bson.M{ sArr[aI] = bson.M{
@@ -122,7 +123,7 @@ func (db *Database) Lookup(structField string, results interface{}, selector bso
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 := db.ReadCollection(objectResults, sQuery, selector, 0, 0, nil, nil) err := ReadCollection(db, objectResults, sQuery, selector, 0, 0, nil, nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -155,10 +156,14 @@ func (db *Database) Lookup(structField string, results interface{}, selector bso
object := objectIDs[objectID] object := objectIDs[objectID]
field := elV.FieldByName(structField) field := elV.FieldByName(structField)
if object != nil && field.CanSet() {
objectVal := reflect.ValueOf(object)
if fieldIsPtr { if fieldIsPtr {
field.Set(reflect.ValueOf(object)) field.Set(objectVal)
} else { } else {
field.Set(reflect.ValueOf(object).Elem()) field.Set(objectVal.Elem())
}
} }
} }

View File

@@ -1,196 +0,0 @@
package mgocrud
import (
"errors"
mgo "gopkg.in/mgo.v2"
)
var (
ErrNotFound = errors.New("not found")
)
type Connection struct {
connection *mgo.Session
}
func NewConnection(dial string) (*Connection, error) {
connection, err := mgo.Dial(dial)
if err != nil {
return nil, err
}
connection.SetMode(mgo.Monotonic, true)
return &Connection{connection: connection}, nil
}
func (c *Connection) Close() {
c.connection.Close()
}
type Session struct {
session *mgo.Session
}
func (s *Session) Close() {
s.session.Close()
}
func (c *Connection) NewSession() (*Session, error) {
return &Session{session: c.connection.Copy()}, nil
}
type Database struct {
database *mgo.Database
session *Session
}
func (s *Session) DB(name string) *Database {
return &Database{database: s.session.DB(name), session: s}
}
type Collection struct {
collection *mgo.Collection
}
func (db *Database) Session() *Session {
return db.session
}
func (db *Database) C(name string) *Collection {
return &Collection{collection: db.database.C(name)}
}
func (db *Database) Name() string {
return db.database.Name
}
func (c *Collection) Insert(docs ...interface{}) error {
return c.collection.Insert(docs...)
}
func (c *Collection) UpdateId(id interface{}, update interface{}) error {
return c.collection.UpdateId(id, update)
}
func (c *Collection) RemoveId(id interface{}) error {
return c.collection.RemoveId(id)
}
type ChangeInfo struct {
changeInfo *mgo.ChangeInfo
}
func (ci *ChangeInfo) Matched() int {
return ci.changeInfo.Matched
}
func (ci *ChangeInfo) Removed() int {
return ci.changeInfo.Removed
}
func (ci *ChangeInfo) Updated() int {
return ci.changeInfo.Updated
}
func (c *Collection) Upsert(selector interface{}, update interface{}) (*ChangeInfo, error) {
ci, err := c.collection.Upsert(selector, update)
if err != nil {
return nil, err
}
return &ChangeInfo{changeInfo: ci}, nil
}
func (c *Collection) RemoveAll(filter interface{}) (*ChangeInfo, error) {
ci, err := c.collection.RemoveAll(filter)
if err != nil {
return nil, err
}
return &ChangeInfo{changeInfo: ci}, nil
}
type Query struct {
query *mgo.Query
}
func (c *Collection) FindId(id interface{}) *Query {
return &Query{query: c.collection.FindId(id)}
}
func (c *Collection) Find(query interface{}) *Query {
return &Query{query: c.collection.Find(query)}
}
func (q *Query) Select(selector interface{}) *Query {
q.query = q.query.Select(selector)
return q
}
func (q *Query) One(result interface{}) error {
err := q.query.One(result)
if err == mgo.ErrNotFound {
err = ErrNotFound
}
return err
}
func (q *Query) Sort(fields ...string) *Query {
q.query = q.query.Sort(fields...)
return q
}
func (q *Query) Skip(n int) *Query {
q.query = q.query.Skip(n)
return q
}
func (q *Query) Limit(n int) *Query {
q.query = q.query.Limit(n)
return q
}
func (q *Query) All(result interface{}) error {
err := q.query.All(result)
if err == mgo.ErrNotFound {
err = ErrNotFound
}
return err
}
func (q *Query) Count() (int, error) {
c, err := q.query.Count()
if err == mgo.ErrNotFound {
err = ErrNotFound
}
return c, err
}
type Index struct {
index *mgo.Index
}
func NewMgoIndex(index mgo.Index) *Index {
return &Index{index: &index}
}
func (c *Collection) EnsureIndex(index *Index) error {
if index != nil && index.index != nil {
return c.collection.EnsureIndex(*index.index)
}
return errors.New("index parameter not initialized with mgo.Index")
}
type Pipe struct {
pipe *mgo.Pipe
}
func (p *Pipe) All(result interface{}) error {
err := p.pipe.All(result)
if err == mgo.ErrNotFound {
err = ErrNotFound
}
return err
}
func (c *Collection) Pipe(pipeline interface{}) *Pipe {
return &Pipe{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 (db *Database) EnsureIndex(m ModelInterface) error { func EnsureIndex(db *mgo.Database, m ModelInterface) error {
colName := GetCollectionName(m) colName := GetCollectionName(m)
col := db.C(colName) col := db.C(colName)
@@ -57,14 +57,14 @@ func (db *Database) EnsureIndex(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(NewMgoIndex(index)) err := col.EnsureIndex(index)
if err != nil { if err != nil {
return err return err
} }
@@ -93,13 +93,13 @@ func (db *Database) EnsureIndex(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(NewMgoIndex(mgo.Index{ err := col.EnsureIndex(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
} }

View File

@@ -2,11 +2,12 @@ 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 (db *Database) ValidateObject(m ModelInterface, changes bson.M) error { func ValidateObject(db *mgo.Database, 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",
@@ -18,7 +19,7 @@ func (db *Database) ValidateObject(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 *Database, changes bson.M) error Validate(db *mgo.Database, changes bson.M) error
}); ok { }); ok {
return i.Validate(db, changes) return i.Validate(db, changes)
} }