135 lines
3.3 KiB
Go
135 lines
3.3 KiB
Go
package tongo
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"net/url"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
type Database struct {
|
|
database string
|
|
client *mongo.Client
|
|
}
|
|
|
|
func NewConnection(uri string, database string) (*Database, error) {
|
|
client, err := mongo.Connect(context.Background())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Database{client: client, database: database}, nil
|
|
}
|
|
|
|
func (d *Database) Collection(col string) *mongo.Collection {
|
|
return d.client.Database(d.database).Collection(col)
|
|
}
|
|
|
|
func getFilter(f *bson.D) bson.D {
|
|
if f == nil {
|
|
return bson.D{}
|
|
}
|
|
return *f
|
|
}
|
|
|
|
func QueryFilter(q *url.Values) bson.D {
|
|
filter := bson.D{}
|
|
for k, v := range *q {
|
|
var f bson.E
|
|
q := strings.Split(k, "_")
|
|
if len(q) == 1 { // If param is like "name" or "url" we're using exact matching
|
|
f = bson.E{Key: k, Value: v[0]}
|
|
} else {
|
|
if q[1] == "like" { // If it's like "name_like", we're using regex
|
|
f = bson.E{Key: q[0], Value: bson.D{{Key: "$regex", Value: v[0]}}}
|
|
}
|
|
}
|
|
filter = append(filter, f) // Applying query filters to user filter
|
|
}
|
|
return filter
|
|
}
|
|
|
|
func NewStore[T Collectable](db *Database) Store[T] {
|
|
var item T
|
|
coll := item.Coll()
|
|
return Store[T]{
|
|
Db: db.client,
|
|
Coll: db.Collection(coll),
|
|
}
|
|
}
|
|
|
|
type Store[T Collectable] struct {
|
|
Db *mongo.Client
|
|
Coll *mongo.Collection
|
|
}
|
|
|
|
func (s Store[T]) InsertOne(ctx context.Context, item *T) (primitive.ObjectID, error) {
|
|
result, err := s.Coll.InsertOne(ctx, item)
|
|
if err != nil {
|
|
if e, ok := err.(mongo.WriteError); ok {
|
|
log.Printf("Error writing new item of type %v: %v ", reflect.TypeOf(item), e)
|
|
return primitive.NilObjectID, e
|
|
}
|
|
log.Printf("Error creating new item of type %v: %v ", reflect.TypeOf(item), err)
|
|
return primitive.NilObjectID, err
|
|
}
|
|
rid, _ := result.InsertedID.(primitive.ObjectID)
|
|
return rid, nil
|
|
}
|
|
|
|
func (s Store[T]) GetById(ctx context.Context, id primitive.ObjectID) (*T, error) {
|
|
res := s.Coll.FindOne(ctx, bson.D{{Key: "_id", Value: id}})
|
|
if res.Err() != nil {
|
|
return nil, res.Err()
|
|
}
|
|
var item T
|
|
res.Decode(&item)
|
|
return &item, nil
|
|
}
|
|
|
|
func (s Store[T]) GetOne(ctx context.Context, filter *bson.D) (*T, error) {
|
|
f := getFilter(filter)
|
|
res := s.Coll.FindOne(ctx, f)
|
|
if res.Err() != nil {
|
|
return nil, res.Err()
|
|
}
|
|
var item T
|
|
res.Decode(&item)
|
|
return &item, nil
|
|
}
|
|
|
|
func (s Store[T]) GetMany(ctx context.Context, filter *bson.D) ([]*T, error) {
|
|
f := getFilter(filter)
|
|
cur, err := s.Coll.Find(ctx, f)
|
|
if err != nil {
|
|
log.Println("Error fetching items: ", err)
|
|
return nil, err
|
|
}
|
|
res := []*T{}
|
|
err = cur.All(ctx, &res)
|
|
if err != nil {
|
|
log.Println("Error collecting items to slice: ", err)
|
|
return nil, err
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (s Store[T]) DeleteByID(ctx context.Context, id primitive.ObjectID) error {
|
|
_, err := s.Coll.DeleteOne(ctx, bson.D{{Key: "_id", Value: id}})
|
|
return err
|
|
}
|
|
|
|
func (s Store[T]) ReplaceItem(ctx context.Context, item T, upsert bool) error {
|
|
_, err := s.Coll.ReplaceOne(ctx, bson.D{{Key: "_id", Value: item.GetID()}}, item, options.Replace().SetUpsert(upsert))
|
|
return err
|
|
}
|
|
|
|
func (s Store[T]) Count(ctx context.Context, filter *bson.D) (int64, error) {
|
|
return s.Coll.CountDocuments(ctx, filter)
|
|
}
|