github.com/godaddy-x/freego@v1.0.156/ormx/sqld/mongo_manager.go (about) 1 package sqld 2 3 import ( 4 "context" 5 "fmt" 6 "github.com/godaddy-x/freego/cache" 7 DIC "github.com/godaddy-x/freego/common" 8 "github.com/godaddy-x/freego/ormx/sqlc" 9 "github.com/godaddy-x/freego/utils" 10 "github.com/godaddy-x/freego/zlog" 11 "go.mongodb.org/mongo-driver/bson" 12 "go.mongodb.org/mongo-driver/bson/primitive" 13 "go.mongodb.org/mongo-driver/mongo" 14 "go.mongodb.org/mongo-driver/mongo/options" 15 "go.mongodb.org/mongo-driver/mongo/readpref" 16 "go.uber.org/zap" 17 "reflect" 18 "time" 19 ) 20 21 var ( 22 mgoSessions = make(map[string]*MGOManager) 23 mgoSlowlog *zap.Logger 24 ) 25 26 type SortBy struct { 27 Key string 28 Sort int 29 } 30 31 const ( 32 JID = "id" 33 BID = "_id" 34 COUNT_BY = "COUNT_BY" 35 ) 36 37 /********************************** 数据库配置参数 **********************************/ 38 39 // 数据库配置 40 type MGOConfig struct { 41 DBConfig 42 AuthMechanism string 43 Addrs []string 44 Direct bool 45 ConnectTimeout int64 46 SocketTimeout int64 47 Database string 48 Username string 49 Password string 50 PoolLimit int 51 ConnectionURI string 52 } 53 54 type PackContext struct { 55 SessionContext mongo.SessionContext 56 Context context.Context 57 CancelFunc context.CancelFunc 58 } 59 60 func IsNullObjectID(target primitive.ObjectID) bool { 61 // return target.Hex() == "000000000000000000000000" 62 return target.IsZero() 63 } 64 65 // 数据库管理器 66 type MGOManager struct { 67 DBManager 68 Session *mongo.Client 69 PackContext *PackContext 70 } 71 72 func (self *MGOManager) Get(option ...Option) (*MGOManager, error) { 73 if err := self.GetDB(option...); err != nil { 74 return nil, err 75 } 76 return self, nil 77 } 78 79 func NewMongo(option ...Option) (*MGOManager, error) { 80 manager := &MGOManager{} 81 return manager.Get(option...) 82 } 83 84 func UseTransaction(fn func(mgo *MGOManager) error, option ...Option) error { 85 self, err := NewMongo(option...) 86 if err != nil { 87 return err 88 } 89 defer self.Close() 90 return self.Session.UseSession(self.PackContext.Context, func(sessionContext mongo.SessionContext) error { 91 self.PackContext.SessionContext = sessionContext 92 if err := self.PackContext.SessionContext.StartTransaction(); err != nil { 93 return err 94 } 95 if err := fn(self); err != nil { 96 self.PackContext.SessionContext.AbortTransaction(self.PackContext.SessionContext) 97 return err 98 } 99 return self.PackContext.SessionContext.CommitTransaction(self.PackContext.SessionContext) 100 }) 101 } 102 103 // 获取mongo的数据库连接 104 func (self *MGOManager) GetDatabase(tb string) (*mongo.Collection, error) { 105 collection := self.Session.Database(self.Database).Collection(tb) 106 if collection == nil { 107 return nil, self.Error("failed to get Mongo collection") 108 } 109 return collection, nil 110 } 111 112 func (self *MGOManager) GetDB(options ...Option) error { 113 dsName := DIC.MASTER 114 var option Option 115 if len(options) > 0 { 116 option = options[0] 117 if len(option.DsName) > 0 { 118 dsName = option.DsName 119 } else { 120 option.DsName = dsName 121 } 122 } 123 mgo := mgoSessions[dsName] 124 if mgo == nil { 125 return self.Error("mongo session [", dsName, "] not found...") 126 } 127 self.Session = mgo.Session 128 self.DsName = mgo.DsName 129 self.Database = mgo.Database 130 self.Timeout = 60000 131 self.SlowQuery = mgo.SlowQuery 132 self.SlowLogPath = mgo.SlowLogPath 133 self.CacheManager = mgo.CacheManager 134 if len(option.DsName) > 0 { 135 if len(option.DsName) > 0 { 136 self.DsName = option.DsName 137 } 138 if option.OpenTx { 139 self.OpenTx = option.OpenTx 140 } 141 if option.Timeout > 0 { 142 self.Timeout = option.Timeout 143 } 144 if len(option.Database) > 0 { 145 self.Database = option.Database 146 } 147 } 148 ctx, cancel := context.WithTimeout(context.Background(), time.Duration(self.Timeout)*time.Millisecond) 149 self.PackContext = &PackContext{Context: ctx, CancelFunc: cancel} 150 return nil 151 } 152 153 func (self *MGOManager) GetSessionContext() context.Context { 154 if self.PackContext.SessionContext == nil { 155 return self.PackContext.Context 156 } 157 return self.PackContext.SessionContext 158 } 159 160 func (self *MGOManager) InitConfig(input ...MGOConfig) error { 161 return self.buildByConfig(nil, input...) 162 } 163 164 func (self *MGOManager) InitConfigAndCache(manager cache.Cache, input ...MGOConfig) error { 165 return self.buildByConfig(manager, input...) 166 } 167 168 func (self *MGOManager) buildByConfig(manager cache.Cache, input ...MGOConfig) error { 169 for _, v := range input { 170 if len(v.Database) == 0 { 171 panic("mongo database is nil") 172 } 173 dsName := DIC.MASTER 174 if len(v.DsName) > 0 { 175 dsName = v.DsName 176 } 177 if _, b := mgoSessions[dsName]; b { 178 return utils.Error("mongo init failed: [", v.DsName, "] exist") 179 } 180 opts := options.Client() 181 if len(v.ConnectionURI) == 0 { 182 if len(v.AuthMechanism) == 0 { 183 v.AuthMechanism = "SCRAM-SHA-1" 184 } 185 credential := options.Credential{ 186 AuthMechanism: v.AuthMechanism, 187 Username: v.Username, 188 Password: v.Password, 189 AuthSource: v.Database, 190 } 191 opts = options.Client().ApplyURI(fmt.Sprintf("mongodb://%s", v.Addrs[0])) 192 if len(v.Username) > 0 && len(v.Password) > 0 { 193 opts.SetAuth(credential) 194 } 195 } else { 196 opts = options.Client().ApplyURI(v.ConnectionURI) 197 } 198 opts.SetDirect(v.Direct) 199 opts.SetTimeout(time.Second * time.Duration(v.ConnectTimeout)) 200 opts.SetMinPoolSize(100) 201 opts.SetMaxPoolSize(uint64(v.PoolLimit)) 202 opts.SetSocketTimeout(time.Second * time.Duration(v.SocketTimeout)) 203 // 连接数据库 204 session, err := mongo.Connect(context.Background(), opts) 205 if err != nil { 206 panic(err) 207 } 208 // 判断服务是不是可用 209 if err := session.Ping(context.Background(), readpref.Primary()); err != nil { 210 panic(err) 211 } 212 mgo := &MGOManager{} 213 mgo.Session = session 214 mgo.CacheManager = manager 215 mgo.DsName = dsName 216 mgo.Database = v.Database 217 mgo.SlowQuery = v.SlowQuery 218 mgo.SlowLogPath = v.SlowLogPath 219 if v.OpenTx { 220 mgo.OpenTx = v.OpenTx 221 } 222 mgoSessions[mgo.DsName] = mgo 223 // init zlog 224 mgo.initSlowLog() 225 zlog.Printf("mongodb service【%s】has been started successful", mgo.DsName) 226 } 227 if len(mgoSessions) == 0 { 228 return utils.Error("mongo init failed: sessions is nil") 229 } 230 return nil 231 } 232 233 func (self *MGOManager) initSlowLog() { 234 if self.SlowQuery == 0 || len(self.SlowLogPath) == 0 { 235 return 236 } 237 if mgoSlowlog == nil { 238 mgoSlowlog = zlog.InitNewLog(&zlog.ZapConfig{ 239 Level: "warn", 240 Console: false, 241 FileConfig: &zlog.FileConfig{ 242 Compress: true, 243 Filename: self.SlowLogPath, 244 MaxAge: 7, 245 MaxBackups: 7, 246 MaxSize: 512, 247 }}) 248 mgoSlowlog.Info("MGO query monitoring service started successful...") 249 } 250 } 251 252 func (self *MGOManager) getSlowLog() *zap.Logger { 253 return mgoSlowlog 254 } 255 256 func (self *MGOManager) Save(data ...sqlc.Object) error { 257 if data == nil || len(data) == 0 { 258 return self.Error("[Mongo.Save] data is nil") 259 } 260 if len(data) > 2000 { 261 return self.Error("[Mongo.Save] data length > 2000") 262 } 263 d := data[0] 264 if len(self.MGOSyncData) > 0 { 265 d = self.MGOSyncData[0].CacheModel 266 } 267 obv, ok := modelDrivers[d.GetTable()] 268 if !ok { 269 return self.Error("[Mongo.Save] registration object type not found [", d.GetTable(), "]") 270 } 271 db, err := self.GetDatabase(d.GetTable()) 272 if err != nil { 273 return self.Error(err) 274 } 275 if zlog.IsDebug() { 276 defer zlog.Debug("[Mongo.Save]", utils.UnixMilli(), zlog.Any("data", data)) 277 } 278 adds := make([]interface{}, 0, len(data)) 279 for _, v := range data { 280 if obv.PkKind == reflect.Int64 { 281 lastInsertId := utils.GetInt64(utils.GetPtr(v, obv.PkOffset)) 282 if lastInsertId == 0 { 283 lastInsertId = utils.NextIID() 284 utils.SetInt64(utils.GetPtr(v, obv.PkOffset), lastInsertId) 285 } 286 } else if obv.PkKind == reflect.String { 287 lastInsertId := utils.GetString(utils.GetPtr(v, obv.PkOffset)) 288 if len(lastInsertId) == 0 { 289 lastInsertId = utils.NextSID() 290 utils.SetString(utils.GetPtr(v, obv.PkOffset), lastInsertId) 291 } 292 } else if obv.PkType == "primitive.ObjectID" { 293 lastInsertId := utils.GetObjectID(utils.GetPtr(v, obv.PkOffset)) 294 if IsNullObjectID(lastInsertId) { 295 lastInsertId = primitive.NewObjectID() 296 utils.SetObjectID(utils.GetPtr(v, obv.PkOffset), lastInsertId) 297 } 298 } else { 299 return self.Error("only Int64 and string and ObjectID type IDs are supported") 300 } 301 adds = append(adds, v) 302 } 303 res, err := db.InsertMany(self.GetSessionContext(), adds) 304 if err != nil { 305 return self.Error("[Mongo.Save] save failed: ", err) 306 } 307 if len(res.InsertedIDs) != len(adds) { 308 return self.Error("[Mongo.Save] save failed: InsertedIDs length invalid") 309 } 310 return nil 311 } 312 313 func (self *MGOManager) Update(data ...sqlc.Object) error { 314 if data == nil || len(data) == 0 { 315 return self.Error("[Mongo.Update] data is nil") 316 } 317 if len(data) > 2000 { 318 return self.Error("[Mongo.Update] data length > 2000") 319 } 320 d := data[0] 321 if len(self.MGOSyncData) > 0 { 322 d = self.MGOSyncData[0].CacheModel 323 } 324 obv, ok := modelDrivers[d.GetTable()] 325 if !ok { 326 return self.Error("[Mongo.Update] registration object type not found [", d.GetTable(), "]") 327 } 328 db, err := self.GetDatabase(d.GetTable()) 329 if err != nil { 330 return self.Error(err) 331 } 332 if zlog.IsDebug() { 333 defer zlog.Debug("[Mongo.Update]", utils.UnixMilli(), zlog.Any("data", data)) 334 } 335 var lastInsertId interface{} 336 for _, v := range data { 337 if obv.PkKind == reflect.Int64 { 338 pk := utils.GetInt64(utils.GetPtr(v, obv.PkOffset)) 339 if pk == 0 { 340 return self.Error("[Mongo.Update] data object id is nil") 341 } 342 lastInsertId = pk 343 } else if obv.PkKind == reflect.String { 344 pk := utils.GetString(utils.GetPtr(v, obv.PkOffset)) 345 if len(pk) == 0 { 346 return self.Error("[Mongo.Update] data object id is nil") 347 } 348 lastInsertId = pk 349 } else if obv.PkType == "primitive.ObjectID" { 350 pk := utils.GetObjectID(utils.GetPtr(v, obv.PkOffset)) 351 if IsNullObjectID(pk) { 352 return self.Error("[Mongo.Update] data object id is nil") 353 } 354 lastInsertId = pk 355 } else { 356 return self.Error("only Int64 and string and ObjectID type IDs are supported") 357 } 358 res, err := db.ReplaceOne(self.GetSessionContext(), bson.M{"_id": lastInsertId}, v) 359 if err != nil { 360 return self.Error("[Mongo.Update] update failed: ", err) 361 } 362 if res.ModifiedCount == 0 { 363 return self.Error("[Mongo.Update] update failed: ModifiedCount = 0") 364 } 365 } 366 return nil 367 } 368 369 func (self *MGOManager) UpdateByCnd(cnd *sqlc.Cnd) (int64, error) { 370 if cnd.Model == nil { 371 return 0, self.Error("[Mongo.UpdateByCnd] data model is nil") 372 } 373 db, err := self.GetDatabase(cnd.Model.GetTable()) 374 if err != nil { 375 return 0, err 376 } 377 match := buildMongoMatch(cnd) 378 upset := buildMongoUpset(cnd) 379 if match == nil || len(match) == 0 { 380 return 0, self.Error("pipe match is nil") 381 } 382 if upset == nil || len(upset) == 0 { 383 return 0, self.Error("pipe upset is nil") 384 } 385 defer self.writeLog("[Mongo.UpdateByCnd]", utils.UnixMilli(), map[string]interface{}{"match": match, "upset": upset}, nil) 386 res, err := db.UpdateMany(self.GetSessionContext(), match, upset) 387 if err != nil { 388 return 0, self.Error("[Mongo.UpdateByCnd] update failed: ", err) 389 } 390 if res.ModifiedCount == 0 { 391 return 0, self.Error("[Mongo.Update] update failed: ModifiedCount = 0") 392 } 393 return res.ModifiedCount, nil 394 } 395 396 func (self *MGOManager) Delete(data ...sqlc.Object) error { 397 if data == nil || len(data) == 0 { 398 return self.Error("[Mongo.Delete] data is nil") 399 } 400 if len(data) > 2000 { 401 return self.Error("[Mongo.Delete] data length > 2000") 402 } 403 d := data[0] 404 if len(self.MGOSyncData) > 0 { 405 d = self.MGOSyncData[0].CacheModel 406 } 407 obv, ok := modelDrivers[d.GetTable()] 408 if !ok { 409 return self.Error("[Mongo.Delete] registration object type not found [", d.GetTable(), "]") 410 } 411 db, err := self.GetDatabase(d.GetTable()) 412 if err != nil { 413 return self.Error(err) 414 } 415 if zlog.IsDebug() { 416 defer zlog.Debug("[Mongo.Delete]", utils.UnixMilli(), zlog.Any("data", data)) 417 } 418 delIds := make([]interface{}, 0, len(data)) 419 for _, v := range data { 420 if obv.PkKind == reflect.Int64 { 421 lastInsertId := utils.GetInt64(utils.GetPtr(v, obv.PkOffset)) 422 if lastInsertId == 0 { 423 return self.Error("[Mongo.Delete] data object id is nil") 424 } 425 delIds = append(delIds, lastInsertId) 426 } else if obv.PkKind == reflect.String { 427 lastInsertId := utils.GetString(utils.GetPtr(v, obv.PkOffset)) 428 if len(lastInsertId) == 0 { 429 return self.Error("[Mongo.Delete] data object id is nil") 430 } 431 delIds = append(delIds, lastInsertId) 432 } else if obv.PkType == "primitive.ObjectID" { 433 lastInsertId := utils.GetObjectID(utils.GetPtr(v, obv.PkOffset)) 434 if IsNullObjectID(lastInsertId) { 435 return self.Error("[Mongo.Update] data object id is nil") 436 } 437 delIds = append(delIds, lastInsertId) 438 } else { 439 return self.Error("only Int64 and string and ObjectID type IDs are supported") 440 } 441 } 442 if len(delIds) > 0 { 443 if _, err := db.DeleteMany(self.GetSessionContext(), bson.M{"_id": bson.M{"$in": delIds}}); err != nil { 444 return self.Error("[Mongo.Delete] delete failed: ", err) 445 } 446 } 447 return nil 448 } 449 450 func (self *MGOManager) DeleteById(object sqlc.Object, data ...interface{}) (int64, error) { 451 if data == nil || len(data) == 0 { 452 return 0, self.Error("[Mongo.DeleteById] data is nil") 453 } 454 if len(data) > 2000 { 455 return 0, self.Error("[Mongo.DeleteById] data length > 2000") 456 } 457 d := object 458 if len(self.MGOSyncData) > 0 { 459 d = self.MGOSyncData[0].CacheModel 460 } 461 _, ok := modelDrivers[d.GetTable()] 462 if !ok { 463 return 0, self.Error("[Mongo.DeleteById] registration object type not found [", d.GetTable(), "]") 464 } 465 db, err := self.GetDatabase(d.GetTable()) 466 if err != nil { 467 return 0, self.Error(err) 468 } 469 if zlog.IsDebug() { 470 defer zlog.Debug("[Mongo.DeleteById]", utils.UnixMilli(), zlog.Any("data", data)) 471 } 472 if len(data) > 0 { 473 res, err := db.DeleteMany(self.GetSessionContext(), bson.M{"_id": bson.M{"$in": data}}) 474 if err != nil { 475 return 0, self.Error("[Mongo.DeleteById] delete failed: ", err) 476 } 477 return res.DeletedCount, nil 478 } 479 return 0, nil 480 } 481 482 func (self *MGOManager) DeleteByCnd(cnd *sqlc.Cnd) (int64, error) { 483 if cnd.Model == nil { 484 return 0, self.Error("[Mongo.DeleteByCnd] data model is nil") 485 } 486 db, err := self.GetDatabase(cnd.Model.GetTable()) 487 if err != nil { 488 return 0, err 489 } 490 match := buildMongoMatch(cnd) 491 if match == nil || len(match) == 0 { 492 return 0, self.Error("pipe match is nil") 493 } 494 defer self.writeLog("[Mongo.DeleteByCnd]", utils.UnixMilli(), map[string]interface{}{"match": match}, nil) 495 res, err := db.DeleteMany(self.GetSessionContext(), match) 496 if err != nil { 497 return 0, self.Error("[Mongo.DeleteByCnd] delete failed: ", err) 498 } 499 if res.DeletedCount == 0 { 500 return 0, self.Error("[Mongo.DeleteByCnd] delete failed: ModifiedCount = 0") 501 } 502 return res.DeletedCount, nil 503 } 504 505 func (self *MGOManager) Count(cnd *sqlc.Cnd) (int64, error) { 506 if cnd.Model == nil { 507 return 0, self.Error("[Mongo.Count] data model is nil") 508 } 509 db, err := self.GetDatabase(cnd.Model.GetTable()) 510 if err != nil { 511 return 0, self.Error(err) 512 } 513 pipe := buildMongoMatch(cnd) 514 defer self.writeLog("[Mongo.Count]", utils.UnixMilli(), pipe, nil) 515 var pageTotal int64 516 if pipe == nil || len(pipe) == 0 { 517 pageTotal, err = db.EstimatedDocumentCount(self.GetSessionContext()) 518 } else { 519 pageTotal, err = db.CountDocuments(self.GetSessionContext(), pipe) 520 } 521 if err != nil { 522 return 0, self.Error("[Mongo.Count] count failed: ", err) 523 } 524 //pageTotal, err = db.EstimatedDocumentCount(self.GetSessionContext(), pipe) 525 if pageTotal > 0 && cnd.Pagination.PageSize > 0 { 526 var pageCount int64 527 if pageTotal%cnd.Pagination.PageSize == 0 { 528 pageCount = pageTotal / cnd.Pagination.PageSize 529 } else { 530 pageCount = pageTotal/cnd.Pagination.PageSize + 1 531 } 532 cnd.Pagination.PageCount = pageCount 533 } else { 534 cnd.Pagination.PageCount = 0 535 } 536 cnd.Pagination.PageTotal = pageTotal 537 return pageTotal, nil 538 } 539 540 func (self *MGOManager) Exists(cnd *sqlc.Cnd) (bool, error) { 541 check, err := self.Count(cnd) 542 if err != nil { 543 return false, err 544 } 545 return check > 0, nil 546 } 547 548 func (self *MGOManager) FindOne(cnd *sqlc.Cnd, data sqlc.Object) error { 549 if data == nil { 550 return self.Error("[Mongo.FindOne] data is nil") 551 } 552 db, err := self.GetDatabase(data.GetTable()) 553 if err != nil { 554 return self.Error(err) 555 } 556 pipe := buildMongoMatch(cnd) 557 opts := buildQueryOneOptions(cnd) 558 defer self.writeLog("[Mongo.FindOne]", utils.UnixMilli(), pipe, opts) 559 cur := db.FindOne(self.GetSessionContext(), pipe, opts...) 560 if err := cur.Decode(data); err != nil { 561 if err == mongo.ErrNoDocuments { 562 return nil 563 } 564 return self.Error(err) 565 } 566 return nil 567 } 568 569 func (self *MGOManager) FindOneComplex(cnd *sqlc.Cnd, data sqlc.Object) error { 570 return self.FindOne(cnd, data) 571 } 572 573 func (self *MGOManager) FindList(cnd *sqlc.Cnd, data interface{}) error { 574 if data == nil { 575 return self.Error("[Mongo.FindList] data is nil") 576 } 577 if cnd.Model == nil { 578 return self.Error("[Mongo.FindList] data model is nil") 579 } 580 db, err := self.GetDatabase(cnd.Model.GetTable()) 581 if err != nil { 582 return self.Error(err) 583 } 584 if cnd.Pagination.IsFastPage { // 快速分页 585 if cnd.Pagination.FastPageSortCountQ { // 执行总条数统计 586 if _, err := self.Count(cnd); err != nil { 587 return err 588 } 589 } 590 key := cnd.Pagination.FastPageKey 591 sort := cnd.Pagination.FastPageSort 592 size := cnd.Pagination.PageSize 593 prevID := cnd.Pagination.FastPageParam[0] 594 lastID := cnd.Pagination.FastPageParam[1] 595 cnd.ResultSize(size) 596 if prevID == 0 && lastID == 0 { 597 cnd.Orderby(key, sort) 598 cnd.Pagination.FastPageSortParam = sort 599 } 600 if sort == sqlc.DESC_ { 601 if prevID > 0 { 602 cnd.Gt(key, prevID) 603 cnd.Pagination.FastPageSortParam = sqlc.ASC_ 604 } 605 if lastID > 0 { 606 cnd.Lt(key, lastID) 607 cnd.Pagination.FastPageSortParam = sqlc.DESC_ 608 } 609 } else if sort == sqlc.ASC_ { 610 if prevID > 0 { 611 cnd.Lt(key, prevID) 612 cnd.Pagination.FastPageSortParam = sqlc.DESC_ 613 } 614 if lastID > 0 { 615 cnd.Gt(key, lastID) 616 cnd.Pagination.FastPageSortParam = sqlc.ASC_ 617 } 618 } else { 619 panic("sort value invalid") 620 } 621 } 622 if !cnd.Pagination.IsOffset && cnd.Pagination.IsPage { // 常规分页 623 if _, err := self.Count(cnd); err != nil { 624 return err 625 } 626 } 627 pipe := buildMongoMatch(cnd) 628 opts := buildQueryOptions(cnd) 629 defer self.writeLog("[Mongo.FindList]", utils.UnixMilli(), pipe, opts) 630 cur, err := db.Find(self.GetSessionContext(), pipe, opts...) 631 if err != nil { 632 return self.Error("[Mongo.FindList] query failed: ", err) 633 } 634 if err := cur.All(self.GetSessionContext(), data); err != nil { 635 if err == mongo.ErrNoDocuments { 636 return nil 637 } 638 return self.Error(err) 639 } 640 return nil 641 } 642 643 func (self *MGOManager) FindListComplex(cnd *sqlc.Cnd, data interface{}) error { 644 return self.FindList(cnd, data) 645 } 646 647 func (self *MGOManager) Close() error { 648 if self.PackContext.Context != nil && self.PackContext.CancelFunc != nil { 649 self.PackContext.CancelFunc() 650 } 651 return nil 652 } 653 654 func (self *MGOManager) GetCollectionObject(o sqlc.Object) (*mongo.Collection, error) { 655 if o == nil { 656 return nil, self.Error("[Mongo.GetDBObject] model is nil") 657 } 658 db, err := self.GetDatabase(o.GetTable()) 659 if err != nil { 660 return nil, self.Error(err) 661 } 662 return db, nil 663 } 664 665 func buildQueryOneOptions(cnd *sqlc.Cnd) []*options.FindOneOptions { 666 var optsArr []*options.FindOneOptions 667 project := buildMongoProject(cnd) 668 if project != nil && len(project) > 0 { 669 projectOpts := &options.FindOneOptions{} 670 projectOpts.SetProjection(project) 671 optsArr = append(optsArr, projectOpts) 672 } 673 sortBy := buildMongoSortBy(cnd) 674 if sortBy != nil && len(sortBy) > 0 { 675 d := bson.D{} 676 for _, v := range sortBy { 677 d = append(d, bson.E{Key: v.Key, Value: v.Sort}) 678 } 679 sortByOpts := &options.FindOneOptions{} 680 if cnd.CollationConfig != nil { 681 sortByOpts.SetCollation(&options.Collation{ 682 Locale: cnd.CollationConfig.Locale, 683 CaseLevel: cnd.CollationConfig.CaseLevel, 684 CaseFirst: cnd.CollationConfig.CaseFirst, 685 Strength: cnd.CollationConfig.Strength, 686 NumericOrdering: cnd.CollationConfig.NumericOrdering, 687 Alternate: cnd.CollationConfig.Alternate, 688 MaxVariable: cnd.CollationConfig.MaxVariable, 689 Normalization: cnd.CollationConfig.Normalization, 690 Backwards: cnd.CollationConfig.Backwards, 691 }) 692 } 693 sortByOpts.SetSort(d) 694 optsArr = append(optsArr, sortByOpts) 695 } 696 return optsArr 697 } 698 699 func buildQueryOptions(cnd *sqlc.Cnd) []*options.FindOptions { 700 var optsArr []*options.FindOptions 701 project := buildMongoProject(cnd) 702 if project != nil && len(project) > 0 { 703 projectOpts := &options.FindOptions{} 704 projectOpts.SetProjection(project) 705 optsArr = append(optsArr, projectOpts) 706 } 707 sortBy := buildMongoSortBy(cnd) 708 if sortBy != nil && len(sortBy) > 0 { 709 d := bson.D{} 710 for _, v := range sortBy { 711 d = append(d, bson.E{Key: v.Key, Value: v.Sort}) 712 } 713 sortByOpts := &options.FindOptions{} 714 if cnd.CollationConfig != nil { 715 sortByOpts.SetCollation(&options.Collation{ 716 Locale: cnd.CollationConfig.Locale, 717 CaseLevel: cnd.CollationConfig.CaseLevel, 718 CaseFirst: cnd.CollationConfig.CaseFirst, 719 Strength: cnd.CollationConfig.Strength, 720 NumericOrdering: cnd.CollationConfig.NumericOrdering, 721 Alternate: cnd.CollationConfig.Alternate, 722 MaxVariable: cnd.CollationConfig.MaxVariable, 723 Normalization: cnd.CollationConfig.Normalization, 724 Backwards: cnd.CollationConfig.Backwards, 725 }) 726 } 727 sortByOpts.SetSort(d) 728 optsArr = append(optsArr, sortByOpts) 729 } 730 offset, limit := buildMongoLimit(cnd) 731 if offset > 0 || limit > 0 { 732 pageOpts := &options.FindOptions{} 733 if offset > 0 { 734 pageOpts.SetSkip(offset) 735 } 736 if limit > 0 { 737 pageOpts.SetLimit(limit) 738 } 739 optsArr = append(optsArr, pageOpts) 740 } 741 return optsArr 742 } 743 744 // 获取最终pipe条件集合,包含$match $project $sort $skip $limit 745 //func (self *MGOManager) buildPipeCondition(cnd *sqlc.Cnd, countBy bool) ([]interface{}, error) { 746 // match := buildMongoMatch(cnd) 747 // upset := buildMongoUpset(cnd) 748 // project := buildMongoProject(cnd) 749 // aggregate := buildMongoAggregate(cnd) 750 // sortBy := buildMongoSortBy(cnd) 751 // sample := buildMongoSample(cnd) 752 // pageInfo := buildMongoLimit(cnd) 753 // pipe := make([]interface{}, 0, 10) 754 // if match != nil && len(match) > 0 { 755 // pipe = append(pipe, map[string]interface{}{"$match": match}) 756 // } 757 // if upset != nil && len(upset) > 0 { 758 // pipe = append(pipe, map[string]interface{}{"$set": upset}) 759 // } 760 // if project != nil && len(project) > 0 { 761 // pipe = append(pipe, map[string]interface{}{"$project": project}) 762 // } 763 // if aggregate != nil && len(aggregate) > 0 { 764 // for _, v := range aggregate { 765 // if len(v) == 0 { 766 // continue 767 // } 768 // pipe = append(pipe, v) 769 // } 770 // } 771 // if sample != nil { 772 // pipe = append(pipe, sample) 773 // } 774 // if !countBy && sortBy != nil && len(sortBy) > 0 { 775 // pipe = append(pipe, map[string]interface{}{"$sort": sortBy}) 776 // } 777 // if !countBy && cnd.LimitSize > 0 { 778 // pipe = append(pipe, map[string]interface{}{"$limit": cnd.LimitSize}) 779 // } 780 // if !countBy && pageInfo != nil && len(pageInfo) == 2 { 781 // pipe = append(pipe, map[string]interface{}{"$skip": pageInfo[0]}) 782 // pipe = append(pipe, map[string]interface{}{"$limit": pageInfo[1]}) 783 // if !cnd.CacheConfig.Open && !cnd.Pagination.IsOffset { 784 // pageTotal, err := self.Count(cnd) 785 // if err != nil { 786 // return nil, err 787 // } 788 // var pageCount int64 789 // if pageTotal%cnd.Pagination.PageSize == 0 { 790 // pageCount = pageTotal / cnd.Pagination.PageSize 791 // } else { 792 // pageCount = pageTotal/cnd.Pagination.PageSize + 1 793 // } 794 // cnd.Pagination.PageTotal = pageTotal 795 // cnd.Pagination.PageCount = pageCount 796 // } 797 // } 798 // if countBy { 799 // pipe = append(pipe, map[string]interface{}{"$count": COUNT_BY}) 800 // } 801 // return pipe, nil 802 //} 803 804 // 构建mongo逻辑条件命令 805 func buildMongoMatch(cnd *sqlc.Cnd) bson.M { 806 if len(cnd.Conditions) == 0 { 807 return nil 808 } 809 query := bson.M{} 810 for _, v := range cnd.Conditions { 811 key := v.Key 812 if key == JID { 813 key = BID 814 } 815 value := v.Value 816 values := v.Values 817 switch v.Logic { 818 // case condition 819 case sqlc.EQ_: 820 query[key] = value 821 case sqlc.NOT_EQ_: 822 query[key] = bson.M{"$ne": value} 823 case sqlc.LT_: 824 query[key] = bson.M{"$lt": value} 825 case sqlc.LTE_: 826 query[key] = bson.M{"$lte": value} 827 case sqlc.GT_: 828 query[key] = bson.M{"$gt": value} 829 case sqlc.GTE_: 830 query[key] = bson.M{"$gte": value} 831 case sqlc.IS_NULL_: 832 query[key] = nil 833 case sqlc.IS_NOT_NULL_: 834 // unsupported 835 case sqlc.BETWEEN_: 836 query[key] = bson.M{"$gte": values[0], "$lte": values[1]} 837 case sqlc.BETWEEN2_: 838 query[key] = bson.M{"$gte": values[0], "$lt": values[1]} 839 case sqlc.NOT_BETWEEN_: 840 // unsupported 841 case sqlc.IN_: 842 query[key] = bson.M{"$in": values} 843 case sqlc.NOT_IN_: 844 query[key] = bson.M{"$nin": values} 845 case sqlc.LIKE_: 846 query[key] = bson.M{"$regex": value} 847 case sqlc.NOT_LIKE_: 848 // unsupported 849 case sqlc.OR_: 850 if values == nil || len(values) == 0 { 851 continue 852 } 853 var array []interface{} 854 for _, v := range values { 855 cnd, ok := v.(*sqlc.Cnd) 856 if !ok { 857 continue 858 } 859 array = append(array, buildMongoMatch(cnd)) 860 } 861 query["$or"] = array 862 } 863 } 864 return query 865 } 866 867 // 构建mongo字段筛选命令 868 func buildMongoProject(cnd *sqlc.Cnd) bson.M { 869 if len(cnd.AnyFields) == 0 && len(cnd.AnyNotFields) == 0 { 870 return nil 871 } 872 project := bson.M{} 873 for _, v := range cnd.AnyFields { 874 if v == JID { 875 project[BID] = 1 876 } else { 877 project[v] = 1 878 } 879 } 880 for _, v := range cnd.AnyNotFields { 881 if v == JID { 882 project[BID] = 0 883 } else { 884 project[v] = 0 885 } 886 } 887 return project 888 } 889 890 func buildMongoAggregate(cnd *sqlc.Cnd) []map[string]interface{} { 891 if len(cnd.Groupbys) == 0 && len(cnd.Aggregates) == 0 { 892 return nil 893 } 894 group := make(map[string]interface{}, 5) 895 group2 := make(map[string]interface{}, 5) 896 project := make(map[string]interface{}, 10) 897 _idMap := make(map[string]interface{}, 5) 898 _idMap2 := make(map[string]interface{}, 5) 899 project[BID] = 0 900 if len(cnd.Groupbys) > 0 { 901 for _, v := range cnd.Groupbys { 902 if v == JID { 903 _idMap[JID] = utils.AddStr("$_id") 904 _idMap2[JID] = utils.AddStr("$_id.id") 905 project[BID] = utils.AddStr("$_id.id") 906 project[BID] = utils.AddStr("$_id.id") 907 } else { 908 _idMap[v] = utils.AddStr("$", v) 909 _idMap2[v] = utils.AddStr("$_id.", v) 910 project[v] = utils.AddStr("$_id.", v) 911 } 912 } 913 group[BID] = _idMap 914 } 915 if len(cnd.Aggregates) > 0 { 916 if _, b := group[BID]; !b { 917 group[BID] = 0 918 } 919 for _, v := range cnd.Aggregates { 920 k := v.Key 921 if k == JID { 922 if v.Logic == sqlc.SUM_ { 923 group[k] = map[string]string{"$sum": "$_id"} 924 } else if v.Logic == sqlc.MAX_ { 925 group[k] = map[string]string{"$max": "$_id"} 926 } else if v.Logic == sqlc.MIN_ { 927 group[k] = map[string]string{"$min": "$_id"} 928 } else if v.Logic == sqlc.AVG_ { 929 group[k] = map[string]string{"$avg": "$_id"} 930 } 931 project[BID] = utils.AddStr("$", k) 932 } else { 933 if v.Logic == sqlc.SUM_ { 934 group[k] = map[string]string{"$sum": utils.AddStr("$", k)} 935 } else if v.Logic == sqlc.MAX_ { 936 group[k] = map[string]string{"$max": utils.AddStr("$", k)} 937 } else if v.Logic == sqlc.MIN_ { 938 group[k] = map[string]string{"$min": utils.AddStr("$", k)} 939 } else if v.Logic == sqlc.AVG_ { 940 group[k] = map[string]string{"$avg": utils.AddStr("$", k)} 941 } else if v.Logic == sqlc.CNT_ { 942 if _, b := _idMap2[k]; b { 943 delete(_idMap2, k) 944 } 945 group2[v.Alias] = map[string]interface{}{"$sum": 1} 946 project[v.Alias] = 1 947 continue 948 } 949 project[v.Alias] = utils.AddStr("$", k) 950 } 951 } 952 } 953 var result []map[string]interface{} 954 if len(group) > 0 { 955 result = append(result, map[string]interface{}{"$group": group}) 956 } 957 if len(group2) > 0 { 958 group2[BID] = _idMap2 959 result = append(result, map[string]interface{}{"$group": group2}) 960 } 961 if len(project) > 0 { 962 result = append(result, map[string]interface{}{"$project": project}) 963 } 964 return result 965 } 966 967 // 构建mongo字段更新命令 968 func buildMongoUpset(cnd *sqlc.Cnd) bson.M { 969 if len(cnd.Upsets) == 0 { 970 return nil 971 } 972 upset := bson.M{} 973 for k, v := range cnd.Upsets { 974 if k == JID || k == BID { 975 continue 976 } 977 upset[k] = v 978 } 979 if len(upset) == 0 { 980 return nil 981 } 982 return bson.M{"$set": upset} 983 } 984 985 // 构建mongo排序命令 986 func buildMongoSortBy(cnd *sqlc.Cnd) []SortBy { 987 var sortBys []SortBy 988 if cnd.Pagination.IsFastPage { 989 if cnd.Pagination.FastPageSortParam == sqlc.DESC_ { 990 sortBys = append(sortBys, SortBy{Key: getKey(cnd.Pagination.FastPageKey), Sort: -1}) 991 } else { 992 sortBys = append(sortBys, SortBy{Key: getKey(cnd.Pagination.FastPageKey), Sort: 1}) 993 } 994 } 995 for _, v := range cnd.Orderbys { 996 key := getKey(v.Key) 997 if key == getKey(cnd.Pagination.FastPageKey) { 998 continue 999 } 1000 if v.Value == sqlc.DESC_ { 1001 sortBys = append(sortBys, SortBy{Key: key, Sort: -1}) 1002 } else { 1003 sortBys = append(sortBys, SortBy{Key: key, Sort: 1}) 1004 } 1005 } 1006 return sortBys 1007 } 1008 1009 func getKey(key string) string { 1010 if key == JID { 1011 return BID 1012 } 1013 return key 1014 } 1015 1016 // 构建mongo随机选取命令 1017 func buildMongoSample(cnd *sqlc.Cnd) bson.M { 1018 if cnd.SampleSize == 0 { 1019 return nil 1020 } 1021 return bson.M{"$sample": bson.M{"size": cnd.SampleSize}} 1022 } 1023 1024 // 构建mongo分页命令 1025 func buildMongoLimit(cnd *sqlc.Cnd) (int64, int64) { 1026 if cnd.LimitSize > 0 { // 优先resultSize截取 1027 return 0, cnd.LimitSize 1028 } 1029 pg := cnd.Pagination 1030 if pg.PageNo == 0 && pg.PageSize == 0 { 1031 return 0, 0 1032 } 1033 if pg.PageSize <= 0 { 1034 pg.PageSize = 10 1035 } 1036 if pg.IsOffset { 1037 return pg.PageNo, pg.PageSize 1038 } 1039 pageNo := pg.PageNo 1040 pageSize := pg.PageSize 1041 return (pageNo - 1) * pageSize, pageSize 1042 } 1043 1044 func (self *MGOManager) writeLog(title string, start int64, pipe, opts interface{}) { 1045 cost := utils.UnixMilli() - start 1046 if self.SlowQuery > 0 && cost > self.SlowQuery { 1047 l := self.getSlowLog() 1048 if l != nil { 1049 if opts == nil { 1050 opts = &options.FindOptions{} 1051 } 1052 l.Warn(title, zlog.Int64("cost", cost), zlog.Any("pipe", pipe), zlog.Any("opts", opts)) 1053 } 1054 } 1055 if zlog.IsDebug() { 1056 pipeStr, _ := utils.JsonMarshal(pipe) 1057 defer zlog.Debug(title, start, zlog.String("pipe", utils.Bytes2Str(pipeStr)), zlog.Any("opts", opts)) 1058 } 1059 }