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  }