github.com/15mga/kiwi@v0.0.2-0.20240324021231-b95d5c3ac751/util/mgo/mgo.go (about) 1 package mgo 2 3 import ( 4 "context" 5 "errors" 6 "github.com/15mga/kiwi" 7 "github.com/15mga/kiwi/util" 8 "github.com/panjf2000/ants/v2" 9 "go.mongodb.org/mongo-driver/bson" 10 "go.mongodb.org/mongo-driver/mongo" 11 "go.mongodb.org/mongo-driver/mongo/options" 12 "go.mongodb.org/mongo-driver/mongo/readpref" 13 ) 14 15 var ( 16 _Client *mongo.Client 17 _Db *mongo.Database 18 _Coll = make(map[string]*mongo.Collection) 19 _IdxModel = make(map[string]func() []mongo.IndexModel) 20 ) 21 22 func Client() *mongo.Client { 23 return _Client 24 } 25 26 func Db() *mongo.Database { 27 return _Db 28 } 29 30 func Conn(db string, clientOpt *options.ClientOptions, dbOpt *options.DatabaseOptions) *util.Err { 31 client, e := mongo.Connect(context.TODO(), clientOpt) 32 if e != nil { 33 return util.WrapErr(util.EcConnectErr, e) 34 } 35 36 e = client.Ping(context.TODO(), readpref.Primary()) 37 if e != nil { 38 return util.WrapErr(util.EcConnectErr, e) 39 } 40 41 kiwi.BeforeExitFn("mgo", DisConn) 42 43 _Client = client 44 _Db = _Client.Database(db, dbOpt) 45 46 return nil 47 } 48 49 func DisConn() { 50 if _Client == nil { 51 return 52 } 53 _ = _Client.Disconnect(context.Background()) 54 _Client = nil 55 } 56 57 func InitColl(coll string, idxModel func() []mongo.IndexModel, opts ...*options.CollectionOptions) { 58 cl := _Db.Collection(coll, opts...) 59 _Coll[coll] = cl 60 if idxModel != nil { 61 _IdxModel[coll] = idxModel 62 } 63 } 64 65 func CheckColl() *util.Err { 66 names, e := _Db.ListCollectionNames(context.TODO(), nil) 67 if e != nil && !errors.Is(e, mongo.ErrNilDocument) { 68 return util.NewErr(util.EcDbErr, util.M{ 69 "error": e.Error(), 70 }) 71 } 72 m := make(map[string]struct{}, len(names)) 73 for name, coll := range _Coll { 74 if _, ok := m[name]; ok { 75 continue 76 } 77 fn, ok := _IdxModel[name] 78 if !ok { 79 continue 80 } 81 idx := fn() 82 if len(idx) == 0 { 83 continue 84 } 85 _, e = coll.Indexes().CreateMany(context.TODO(), idx) 86 if e != nil { 87 return util.NewErr(util.EcDbErr, util.M{ 88 "error": e.Error(), 89 }) 90 } 91 } 92 return nil 93 } 94 95 func Coll(coll string) *mongo.Collection { 96 return _Coll[coll] 97 } 98 99 func CountDoc(coll string, filter any, opts ...*options.CountOptions) (int64, error) { 100 return Coll(coll).CountDocuments(context.TODO(), filter, opts...) 101 } 102 103 func FindOne(coll string, filter any, item any, opts ...*options.FindOneOptions) error { 104 return Coll(coll).FindOne(context.TODO(), filter, opts...).Decode(item) 105 } 106 107 func AsyncFindOne[T any](coll string, filter any, fn func(*T, error), opts ...*options.FindOneOptions) { 108 e := ants.Submit(func() { 109 item := util.Default[T]() 110 err := FindOne(coll, filter, &item, opts...) 111 if err != nil { 112 fn(&item, err) 113 return 114 } 115 fn(&item, nil) 116 }) 117 if e != nil { 118 item := util.Default[T]() 119 fn(&item, e) 120 } 121 } 122 123 func Find[T any](coll string, filter any, items *[]*T, opts ...*options.FindOptions) error { 124 cursor, e := Coll(coll).Find(context.TODO(), filter, opts...) 125 if e != nil { 126 return e 127 } 128 return cursor.All(context.TODO(), items) 129 } 130 131 func AsyncFind[T any](coll string, filter any, fn func([]*T, error), opts ...*options.FindOptions) { 132 e := ants.Submit(func() { 133 var items []*T 134 err := Find[T](coll, filter, &items, opts...) 135 if err != nil { 136 fn(nil, err) 137 return 138 } 139 fn(items, nil) 140 }) 141 if e != nil { 142 fn(nil, e) 143 } 144 } 145 146 func FindWithTotal[T any](coll string, filter any, fn func(int64, []*T, error), opts ...*options.FindOptions) { 147 total, e := CountDoc(coll, filter) 148 if e != nil { 149 fn(0, nil, e) 150 return 151 } 152 if total == 0 { 153 fn(0, nil, nil) 154 return 155 } 156 AsyncFind[T](coll, filter, func(list []*T, e error) { 157 if e != nil { 158 fn(0, nil, e) 159 return 160 } 161 fn(total, list, nil) 162 }, opts...) 163 } 164 165 func InsertOne(coll string, item any, opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) { 166 return Coll(coll).InsertOne(context.TODO(), item, opts...) 167 } 168 169 func InsertMany(coll string, items []any, opts ...*options.InsertManyOptions) (*mongo.InsertManyResult, error) { 170 if len(items) == 0 { 171 return nil, nil 172 } 173 return Coll(coll).InsertMany(context.TODO(), items, opts...) 174 } 175 176 func UpdateOne(coll string, filter, update any, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) { 177 return Coll(coll).UpdateOne(context.TODO(), filter, update, opts...) 178 } 179 180 func AsyncUpdateOne(coll string, filter, update any, fn func(*mongo.UpdateResult, error), opts ...*options.UpdateOptions) { 181 e := ants.Submit(func() { 182 fn(UpdateOne(coll, filter, update, opts...)) 183 }) 184 if e != nil { 185 fn(nil, e) 186 } 187 } 188 189 func FindOneAndUpdate(coll string, filter, update any, item any, opts ...*options.FindOneAndUpdateOptions) error { 190 return Coll(coll).FindOneAndUpdate(context.TODO(), filter, update, opts...).Decode(item) 191 } 192 193 func AsyncFindOneAndUpdate[T any](coll string, filter, update any, fn func(*T, error), opts ...*options.FindOneAndUpdateOptions) { 194 e := ants.Submit(func() { 195 item := util.Default[T]() 196 e := FindOneAndUpdate(coll, filter, update, &item, opts...) 197 if e != nil { 198 fn(nil, e) 199 return 200 } 201 fn(&item, nil) 202 }) 203 if e != nil { 204 fn(nil, e) 205 } 206 } 207 208 func UpdateMany(coll string, filter, update any, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) { 209 return Coll(coll).UpdateMany(context.TODO(), filter, update, opts...) 210 } 211 212 func ReplaceOne(coll string, filter, replacement any, opts ...*options.ReplaceOptions) (*mongo.UpdateResult, error) { 213 return Coll(coll).ReplaceOne(context.TODO(), filter, replacement, opts...) 214 } 215 216 func DelOne(coll string, filter any, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) { 217 return Coll(coll).DeleteOne(context.TODO(), filter, opts...) 218 } 219 220 func DelMany(coll string, filter any, opts ...*options.DeleteOptions) (int64, *util.Err) { 221 res, e := Coll(coll).DeleteMany(context.TODO(), filter, opts...) 222 if e != nil { 223 return 0, util.NewErr(util.EcDbErr, util.M{ 224 "error": e.Error(), 225 "coll": coll, 226 "filter": filter, 227 }) 228 } 229 return res.DeletedCount, nil 230 } 231 232 func FindOneAndDel(coll string, filter any, item any, opts ...*options.FindOneAndDeleteOptions) error { 233 return Coll(coll).FindOneAndDelete(context.TODO(), filter, opts...).Decode(item) 234 } 235 236 func Tx(sessionOpt *options.SessionOptions, txOpt *options.TransactionOptions, 237 fn util.ToErr) *util.Err { 238 session, e := _Client.StartSession(sessionOpt) 239 if e != nil { 240 return util.WrapErr(util.EcDbErr, e) 241 } 242 defer session.EndSession(context.TODO()) 243 244 e = session.StartTransaction(txOpt) 245 if e != nil { 246 return util.WrapErr(util.EcDbErr, e) 247 } 248 err := fn() 249 if err != nil { 250 _ = session.AbortTransaction(context.TODO()) 251 return err 252 } 253 e = session.CommitTransaction(context.TODO()) 254 if e != nil { 255 return util.WrapErr(util.EcDbErr, e) 256 } 257 return nil 258 } 259 260 func BuildProjectionD(d *bson.D, exclude []string, keys ...string) { 261 if exclude == nil || len(exclude) == 0 { 262 for _, key := range keys { 263 *d = append(*d, bson.E{Key: key, Value: 1}) 264 } 265 return 266 } 267 m := make(map[string]struct{}, len(exclude)) 268 for _, s := range exclude { 269 m[s] = struct{}{} 270 } 271 for _, key := range keys { 272 _, ok := m[key] 273 if ok { 274 continue 275 } 276 *d = append(*d, bson.E{Key: key, Value: 1}) 277 } 278 }