Skip to content

Instantly share code, notes, and snippets.

@osw4l
Created March 13, 2021 18:43
Show Gist options
  • Save osw4l/b3c0970ae2654d2dc0d5f72f966338f5 to your computer and use it in GitHub Desktop.
Save osw4l/b3c0970ae2654d2dc0d5f72f966338f5 to your computer and use it in GitHub Desktop.
package connections
import (
"context"
"errors"
"reflect"
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
type mongoDB struct {
db *mongo.Database
}
func MongoDB(connStr, database string) (*mongoDB, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
client, err := mongo.Connect(ctx, options.Client().ApplyURI(connStr))
if err != nil {
return nil, err
}
// Force a connection to verify our connection string
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
err = client.Ping(ctx, readpref.Primary())
if err != nil {
return nil, err
}
return &mongoDB{db: client.Database(database)}, nil
}
func (mdb *mongoDB) GetMany(typ reflect.Type, filter map[string]interface{}) (interface{}, error) {
ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second)
defer cancel()
i := 0
arr := reflect.New(reflect.SliceOf(typ)).Elem()
if IDs, hasIDs := filter["ids"]; hasIDs {
delete(filter, "ids")
strings := IDs.([]string)
objIDs := make([]primitive.ObjectID, len(strings))
for _, v := range strings {
objID, err := primitive.ObjectIDFromHex(v)
if err != nil {
continue
}
objIDs[i] = objID
i++
}
if i == 0 {
arr.Set(reflect.MakeSlice(reflect.SliceOf(typ), 0, 0))
return arr.Addr().Interface(), errors.New("Empty list of ids.")
}
filter["_id"] = map[string][]primitive.ObjectID{
"$in": objIDs[:i],
}
}
if specieID, hasSpecieID := filter["specie_id"]; hasSpecieID {
objID, err := primitive.ObjectIDFromHex(specieID.(string))
if err != nil {
arr.Set(reflect.MakeSlice(reflect.SliceOf(typ), 0, 0))
return arr.Addr().Interface(), err
}
filter["specie_id"] = objID
}
coll := ToSnakeCase(typ.Name())
cur, err := mdb.db.Collection(coll).Find(ctx, filter)
if err != nil {
arr.Set(reflect.MakeSlice(reflect.SliceOf(typ), 0, 0))
return arr.Addr().Interface(), err
}
defer cur.Close(ctx)
arr.Set(reflect.MakeSlice(reflect.SliceOf(typ), 0, i))
for cur.Next(ctx) {
field := reflect.New(typ)
if cur.Decode(field.Interface()) != nil {
continue
}
arr.Set(reflect.Append(arr, field.Elem()))
}
return arr.Addr().Interface(), cur.Err()
}
func (mdb *mongoDB) GetOne(typ reflect.Type, filter map[string]interface{}) (interface{}, error) {
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
defer cancel()
if ID, hasID := filter["id"]; hasID {
delete(filter, "id")
objID, err := primitive.ObjectIDFromHex(ID.(string))
if err != nil {
return reflect.Zero(reflect.PtrTo(typ)).Interface(), err
}
filter["_id"] = objID
} else {
panic("Map must include 'id'.")
}
coll := ToSnakeCase(typ.Name())
field := reflect.New(typ).Interface()
err := mdb.db.Collection(coll).FindOne(ctx, filter).Decode(field)
if err != nil {
return reflect.Zero(reflect.PtrTo(typ)).Interface(), err
}
return field, nil
}
func (mdb *mongoDB) Create(typ reflect.Type, data interface{}) error {
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
defer cancel()
coll := ToSnakeCase(typ.Name())
res, err := mdb.db.Collection(coll).InsertOne(ctx, data)
if err != nil {
return err
}
refID := reflect.ValueOf(res.InsertedID)
reflect.ValueOf(data).Elem().FieldByName("ID").Set(refID)
return nil
}
func (mdb *mongoDB) Update(typ reflect.Type, filter map[string]interface{}, data interface{}) error {
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
defer cancel()
if ID, hasID := filter["id"]; hasID {
delete(filter, "id")
objID, err := primitive.ObjectIDFromHex(ID.(string))
if err != nil {
return err
}
filter["_id"] = objID
} else {
panic("Map must include 'id'.")
}
coll := ToSnakeCase(typ.Name())
update := map[string]interface{}{"$set": data}
oldData := reflect.New(typ).Interface()
err := mdb.db.Collection(coll).FindOneAndUpdate(ctx, filter, update).Decode(oldData)
if err != nil {
return err
}
refData := reflect.ValueOf(data).Elem()
refOldData := reflect.ValueOf(oldData).Elem()
for i := 0; i < refData.NumField(); i++ {
field := refData.Field(i)
if !field.IsZero() {
refOldData.Field(i).Set(field)
}
}
refData.Set(refOldData)
return nil
}
func (mdb *mongoDB) Replace(typ reflect.Type, filter map[string]interface{}, data interface{}) error {
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
defer cancel()
if ID, hasID := filter["id"]; hasID {
delete(filter, "id")
objID, err := primitive.ObjectIDFromHex(ID.(string))
if err != nil {
return err
}
filter["_id"] = objID
} else {
panic("Map must include 'id'.")
}
coll := ToSnakeCase(typ.Name())
oldData := reflect.New(typ).Interface()
err := mdb.db.Collection(coll).FindOneAndReplace(ctx, filter, data).Decode(oldData)
if err != nil {
return err
}
refData := reflect.ValueOf(data).Elem()
refOldData := reflect.ValueOf(oldData).Elem()
for i := 0; i < refData.NumField(); i++ {
field := refData.Field(i)
if !field.IsZero() {
refOldData.Field(i).Set(field)
}
}
refData.Set(refOldData)
return nil
}
func (mdb *mongoDB) Delete(typ reflect.Type, filter map[string]interface{}) (interface{}, error) {
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
defer cancel()
if ID, hasID := filter["id"]; hasID {
delete(filter, "id")
objID, err := primitive.ObjectIDFromHex(ID.(string))
if err != nil {
return reflect.Zero(reflect.PtrTo(typ)).Interface(), err
}
filter["_id"] = objID
} else {
panic("Map must include 'id'.")
}
coll := ToSnakeCase(typ.Name())
field := reflect.New(typ).Interface()
err := mdb.db.Collection(coll).FindOneAndDelete(ctx, filter).Decode(field)
if err != nil {
return reflect.Zero(reflect.PtrTo(typ)).Interface(), err
}
return field, nil
}
@osw4l
Copy link
Author

osw4l commented Mar 13, 2021

new structure

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment