-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhasmany.go
More file actions
160 lines (143 loc) · 4.56 KB
/
Copy pathhasmany.go
File metadata and controls
160 lines (143 loc) · 4.56 KB
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
package mgmrel
import (
"github.com/kamva/gutil"
"github.com/kamva/mgm/v3"
f "github.com/kamva/mgm/v3/field"
o "github.com/kamva/mgm/v3/operator"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type HasManyRelation struct {
m mgm.Model
related mgm.Model
// foreignKey uses in filters.
foreignKey string
}
// Get method get the list of related models with provided filter,limit,...
// if not found, returns the Mongo Go driver not found error.
func (r *HasManyRelation) GetWithOptions(results interface{}, options ...*options.FindOptions) error {
return mgm.Coll(r.related).SimpleFind(results, r.filterByRelation(nil), options...)
}
// Get method get the list of related models with provided filter,limit,...
// if not found, returns the Mongo Go driver not found error.
func (r *HasManyRelation) Get(results interface{}, sort string, skip, limit int64) error {
return r.GetWithOptions(results, &options.FindOptions{
Limit: &limit,
Skip: &skip,
Sort: r.sortFieldToBsonD(sort),
})
}
// SimpleGet method get the list of related models
// if not found, returns the Mongo Go driver not found error.
// sort is the sort field. you can sort descending by adding a `-` to the sort field. e.g `-created_at`
func (r *HasManyRelation) SimpleGet(results interface{}, limit int64) error {
return r.Get(results, "-_id", 0, limit)
}
// SyncWithoutRemove method sync the relations without
// removing items that are not in the provided list.
func (r *HasManyRelation) SyncWithoutRemove(docs interface{}) error {
if gutil.IsNil(docs) {
return nil
}
models := r.toModel(gutil.InterfaceToSlice(docs))
if len(models) == 0 {
return nil
}
for _, m := range models {
if err := callToBeforeSyncHooks(m); err != nil {
return err
}
_, err := mgm.Coll(r.related).UpdateOne(mgm.Ctx(), bson.M{f.ID: m.GetID()}, bson.M{o.Set: m}, &options.UpdateOptions{
Upsert: gutil.NewBool(true),
})
if err != nil {
return err
}
if err := callToAfterSyncHooks(m); err != nil {
return err
}
}
return nil
}
// Sync method sync the relations:
// If provided models is nil(or length is zero): it remove the related models in the DB.
// If provided models is not nil and length is not zero: udpate new items, and remove
// items that are not in the provided list.
// Use sync just when your 1-m model contains just few m mdoel. otherwise use SyncWithoutRemove
func (r *HasManyRelation) Sync(docs interface{}) error {
if gutil.IsNil(docs) {
_, err := r.delete(nil)
return err
}
models := r.toModel(gutil.InterfaceToSlice(docs))
if len(models) == 0 {
_, err := r.delete(nil)
return err
}
for _, m := range models {
if err := callToBeforeSyncHooks(m); err != nil {
return err
}
_, err := mgm.Coll(r.related).UpdateOne(mgm.Ctx(), bson.M{f.ID: m.GetID()}, bson.M{o.Set: m}, &options.UpdateOptions{
Upsert: gutil.NewBool(true),
})
if err != nil {
return err
}
if err := callToAfterSyncHooks(m); err != nil {
return err
}
}
// Delete All other models that are not in provided models.
_, err := r.delete(r.extractIDs(models))
return err
}
func (r *HasManyRelation) delete(exceptIDs []interface{}) (*mongo.DeleteResult, error) {
return mgm.Coll(r.related).DeleteMany(mgm.Ctx(), r.filterByRelation(exceptIDs))
}
func (r *HasManyRelation) filterByRelation(exceptIDs []interface{}) bson.M {
filter := bson.M{r.foreignKey: r.m.GetID()}
if len(exceptIDs) != 0 {
filter[f.ID] = bson.M{o.Nin: exceptIDs}
}
return filter
}
func (r *HasManyRelation) toModel(docs []interface{}) []mgm.Model {
models := make([]mgm.Model, len(docs))
for i, doc := range docs {
models[i] = doc.(mgm.Model)
}
return models
}
func (r *HasManyRelation) extractIDs(models []mgm.Model) []interface{} {
ids := make([]interface{}, len(models))
for i, m := range models {
ids[i] = m.GetID()
}
return ids
}
// sortFieldToBsonD converts the string sort field to bson D.
func (r *HasManyRelation) sortFieldToBsonD(field string) bson.D {
// Ascending order
order := 1
if field[0] == '-' {
order = -1
field = field[1:]
}
return bson.D{
{field, order},
}
}
// HasMany returns new instance of the "has many" relation ship.
func HasMany(model mgm.Model, related mgm.Model) *HasManyRelation {
return HasManyWithOptions(model, related, foreignKeyName(model))
}
// HasManyWithOptions gets HasManyRelation options and returns new instance of it.
func HasManyWithOptions(model mgm.Model, related mgm.Model, foreignKey string) *HasManyRelation {
return &HasManyRelation{
m: model,
related: related,
foreignKey: foreignKey,
}
}