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) }