-
Notifications
You must be signed in to change notification settings - Fork 0
/
repository.go
172 lines (128 loc) · 4.94 KB
/
repository.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package friendlymongo
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
)
var _ MongoRepository[Model] = &BaseRepository[Model]{}
// MongoRepository is an interface that defines the methods for interacting with a MongoDB collection.
type MongoRepository[T Model] interface {
// InsertOne inserts a single document into the collection.
InsertOne(ctx context.Context, document Model) error
// InsertMany inserts multiple documents into the collection.
InsertMany(ctx context.Context, documents []Model) error
// FindOne finds a single document in the collection.
FindOne(ctx context.Context, filter interface{}) (Model, error)
// Find finds multiple documents in the collection.
Find(ctx context.Context, filter interface{}) ([]Model, error)
// UpdateOne finds a single document and updates it.
UpdateOne(ctx context.Context, filters interface{}, update interface{}) (Model, error)
// Delete deletes multiple documents from the collection.
Delete(ctx context.Context, filter interface{}) (int64, error)
// ReplaceOne replaces a single document in the collection.
ReplaceOne(ctx context.Context, filter interface{}, replacement T) error
// Aggregate runs an aggregation framework pipeline on the collection.
Aggregate(ctx context.Context, pipeline mongo.Pipeline, result interface{}) error
}
// BaseRepository is a base implementation of the MongoRepository interface.
type BaseRepository[T Model] struct {
collection *mongo.Collection
}
// NewBaseRepository creates a new instance of BaseRepository.
func NewBaseRepository[T Model](db *mongo.Database, collectionName string, t T) *BaseRepository[T] {
return &BaseRepository[T]{
collection: db.Collection(collectionName),
}
}
// InsertOne inserts a single document into the collection.
//
// The document parameter must be a pointer to a struct that implements the Model interface.
func (r *BaseRepository[T]) InsertOne(ctx context.Context, document T) error {
document.OnCreate()
_, err := r.collection.InsertOne(ctx, document)
return err
}
// InsertMany inserts multiple documents into the collection.
func (r *BaseRepository[T]) InsertMany(ctx context.Context, documents []T) error {
var interfaceSlice = make([]interface{}, len(documents))
for i, d := range documents {
d.OnCreate()
interfaceSlice[i] = d
}
_, err := r.collection.InsertMany(ctx, interfaceSlice)
return err
}
// FindOne finds a single document in the collection.
func (r *BaseRepository[T]) FindOne(ctx context.Context, filter interface{}) (T, error) {
var document T
err := r.collection.FindOne(ctx, filter).Decode(&document)
return document, err
}
// Find finds multiple documents in the collection.
func (r *BaseRepository[T]) Find(ctx context.Context, filter interface{}) ([]T, error) {
var documents []T
cursor, err := r.collection.Find(ctx, filter)
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var document T
if err := cursor.Decode(&document); err != nil {
return nil, err
}
documents = append(documents, document)
}
return documents, nil
}
// UpdateOne finds a single document and updates it.
// The update parameter must be a bson.M or a struct that implements the Model interface.
func (r *BaseRepository[T]) UpdateOne(ctx context.Context, filters interface{}, update interface{}) (T, error) {
var document T
var updateQuery bson.M
switch u := update.(type) {
case T:
u.OnUpdate()
updateQuery = bson.M{"$set": u}
case bson.M:
u["$currentDate"] = bson.M{"updatedAt": true}
updateQuery = u
default:
return document, fmt.Errorf("update parameter must be a bson.M or a Model")
}
singleRes := r.collection.FindOneAndUpdate(ctx, filters, updateQuery)
if singleRes.Err() != nil {
return document, singleRes.Err()
}
err := singleRes.Decode(&document)
return document, err
}
// Delete deletes multiple documents from the collection.
func (r *BaseRepository[T]) Delete(ctx context.Context, filter interface{}) (int64, error) {
deleteRes, err := r.collection.DeleteMany(ctx, filter)
if err != nil {
return 0, err
}
return deleteRes.DeletedCount, err
}
// Aggregate runs an aggregation framework pipeline on the collection.
func (r *BaseRepository[T]) Aggregate(ctx context.Context, pipeline mongo.Pipeline, result interface{}) error {
cursor, err := r.collection.Aggregate(ctx, pipeline)
if err != nil {
return err
}
defer cursor.Close(ctx)
return cursor.All(ctx, result)
}
// ReplaceOne replaces a single document in the collection.
// Replaced document must have the same ID as the one being replaced or not have it serializible at all. It is
// strongly suggested to have the ID field with the `omitempty` bson tag in case of structs.
func (r *BaseRepository[Model]) ReplaceOne(ctx context.Context, filter interface{}, replacement Model) error {
replacement.OnReplace()
singleRes := r.collection.FindOneAndReplace(ctx, filter, replacement)
if singleRes.Err() != nil {
return singleRes.Err()
}
return nil
}