Basic stuff, loadOne

This commit is contained in:
Nikita Tokarchuk
2018-12-09 02:03:12 +01:00
parent 2e9637c110
commit 913dcb2f1e
11 changed files with 461 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
package base
import (
"github.com/mongodb/mongo-go-driver/bson/primitive"
)
// ObjectID is a structure with objectId as an _id field
type ObjectID struct {
ID primitive.ObjectID `bson:"_id" json:"_id"`
}
// GetID returns an _id
func (db *ObjectID) GetID() primitive.ObjectID {
return db.ID
}
// SetID sets an _id
func (db *ObjectID) SetID(id primitive.ObjectID) {
db.ID = id
}
+13
View File
@@ -0,0 +1,13 @@
package mongox
type Saver interface {
Save(db *Database) error
}
type Deleter interface {
Delete(db *Database) error
}
type Loader interface {
Load(db *Database, filters ...interface{}) error
}
+35
View File
@@ -0,0 +1,35 @@
package common
import (
"github.com/mainnika/mongox-go-driver/mongox"
"github.com/mainnika/mongox-go-driver/mongox/errors"
"github.com/mainnika/mongox-go-driver/mongox/query"
"github.com/mongodb/mongo-go-driver/mongo"
"github.com/mongodb/mongo-go-driver/mongo/options"
)
// LoadOne function loads a first single target document by a query
func LoadOne(db *mongox.Database, target interface{}, composed *query.Query) error {
collection := db.GetCollectionOf(target)
opts := &options.FindOneOptions{}
if composed.Sorter() != nil {
opts.Sort = composed.Sorter().Sort()
}
result := collection.FindOne(db.Context(), composed.M(), opts)
if result.Err() != nil {
return errors.InternalErrorf("can't create find one result: %s", result.Err())
}
err := result.Decode(target)
if err == mongo.ErrNoDocuments {
return errors.NotFoundErrorf("%s", err)
}
if err != nil {
return errors.InternalErrorf("can't decode desult: %s", err)
}
return nil
}
+80
View File
@@ -0,0 +1,80 @@
package mongox
import (
"context"
"reflect"
"github.com/mainnika/mongox-go-driver/mongox/errors"
"github.com/mongodb/mongo-go-driver/mongo"
)
// Database handler
type Database struct {
client *mongo.Client
dbname string
ctx context.Context
}
// NewDatabase function creates new database instance with mongo client and empty context
func NewDatabase(client *mongo.Client, dbname string) *Database {
db := &Database{}
db.client = client
db.dbname = dbname
return db
}
// Client function returns a mongo client
func (d *Database) Client() *mongo.Client {
return d.client
}
// Context function returns a context
func (d *Database) Context() context.Context {
return d.ctx
}
// Name function returns a database name
func (d *Database) Name() string {
return d.dbname
}
// New function creates new database context with same client
func (d *Database) New(ctx context.Context) *Database {
if ctx != nil {
ctx = context.Background()
}
return &Database{
client: d.client,
dbname: d.dbname,
ctx: ctx,
}
}
// GetCollectionOf returns the collection object by the «collection» tag of the given document;
// the «collection» tag should exists, e.g.:
// type Foobar struct {
// base.ObjectID `bson:",inline" json:",inline" collection:"foobars"`
// ...
// Will panic if there is no «collection» tag
func (d *Database) GetCollectionOf(document interface{}) *mongo.Collection {
el := reflect.TypeOf(document).Elem()
numField := el.NumField()
for i := 0; i < numField; i++ {
field := el.Field(i)
tag := field.Tag
found, ok := tag.Lookup("collection")
if !ok {
continue
}
return d.client.Database(d.dbname).Collection(found)
}
panic(errors.InternalErrorf("document %v does not have a collection tag", document))
}
+16
View File
@@ -0,0 +1,16 @@
package errors
import "fmt"
// InternalError error
type InternalError string
// Error message
func (ie InternalError) Error() string {
return fmt.Sprintf("internal error, %s", string(ie))
}
// InternalErrorf function creates an instance of InternalError
func InternalErrorf(format string, params ...interface{}) error {
return InternalError(fmt.Sprintf(format, params...))
}
+16
View File
@@ -0,0 +1,16 @@
package errors
import "fmt"
// NotFound error
type NotFound string
// Error message
func (nf NotFound) Error() string {
return fmt.Sprintf("can not find, %s", string(nf))
}
// NotFoundErrorf function creates an instance of BadRequestError
func NotFoundErrorf(format string, params ...interface{}) error {
return NotFound(fmt.Sprintf(format, params...))
}
+53
View File
@@ -0,0 +1,53 @@
package query
import (
"github.com/mainnika/mongox-go-driver/mongox/errors"
"github.com/mongodb/mongo-go-driver/bson"
)
// ComposeQuery is a function to compose filters into a single query
func Compose(filters ...interface{}) *Query {
q := &Query{}
for _, f := range filters {
ok := false
ok = ok || applyBson(q, f)
ok = ok || applyLimits(q, f)
if !ok {
panic(errors.InternalErrorf("unknown filter %v", f))
}
}
return q
}
// applyBson is a fallback for a custom bson.M
func applyBson(q *Query, f interface{}) bool {
switch f := f.(type) {
case bson.M:
q.And(f)
default:
return false
}
return true
}
// applyLimits extends query with contol functions
func applyLimits(q *Query, f interface{}) bool {
switch f := f.(type) {
case Limiter:
q.limiter = f
case Sorter:
q.sorter = f
default:
return false
}
return true
}
+60
View File
@@ -0,0 +1,60 @@
package query
import (
"github.com/mongodb/mongo-go-driver/bson"
"reflect"
)
// Query is an enchanched bson.M map
type Query struct {
m bson.M
limiter Limiter
sorter Sorter
}
// And function pushes the elem query to the $and array of the query
func (q *Query) And(elem bson.M) *Query {
if q.m == nil {
q.m = bson.M{}
}
queries, exists := q.m["$and"].(bson.A)
if !exists {
q.m["$and"] = bson.A{elem}
return q
}
q.m["$and"] = append(queries, elem)
return q
}
// Limiter is a limit function for a query
func (q *Query) Limiter() Limiter {
return q.limiter
}
// Sorter is a sort rule for a query
func (q *Query) Sorter() Sorter {
return q.sorter
}
// Empty checks the query for any content
func (q *Query) Empty() bool {
qv := reflect.ValueOf(q)
keys := qv.MapKeys()
return len(keys) == 0
}
// M returns underlying query map
func (q *Query) M() bson.M {
return q.m
}
+20
View File
@@ -0,0 +1,20 @@
package query
import (
"github.com/mongodb/mongo-go-driver/bson"
)
// Sorter is a filter to sort the data before query
type Sorter interface {
Sort() bson.M
}
// Sort is a simple implementations of the Sorter filter
type Sort bson.M
var _ Sorter = &Sort{}
// Sort returns a slice of fields which have to be sorted
func (f Sort) Sort() bson.M {
return bson.M(f)
}