From 5528e8e44d0ed2e283f2f03bb092ef02b6e46dbc Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 22 May 2024 10:54:25 +0800 Subject: [PATCH] new mongo --- pkg/common/listdemo/common.go | 182 ++++++++++++++++++++++++++++ pkg/common/listdemo/friend_model.go | 5 + pkg/common/listdemo/friend_table.go | 86 +++++++++++++ 3 files changed, 273 insertions(+) create mode 100644 pkg/common/listdemo/common.go create mode 100644 pkg/common/listdemo/friend_model.go create mode 100644 pkg/common/listdemo/friend_table.go diff --git a/pkg/common/listdemo/common.go b/pkg/common/listdemo/common.go new file mode 100644 index 000000000..2d99cd8c9 --- /dev/null +++ b/pkg/common/listdemo/common.go @@ -0,0 +1,182 @@ +package listdemo + +import ( + "context" + "github.com/openimsdk/tools/db/mongoutil" + "github.com/openimsdk/tools/db/pagination" + "github.com/pkg/errors" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +var ( + ErrListNotFound = errors.New("list not found") + ErrElemExist = errors.New("elem exist") + ErrNotFound = mongo.ErrNoDocuments +) + +type ListDoc interface { + IDName() string // 外层业务id字段名字 user_id + ElemsName() string // 外层列表名字 friends + VersionName() string // 外层版本号 version + DeleteVersion() string // 删除版本号 + BuildDoc(lid any, e Elem) any // 返回一个组装的doc文档 +} + +type Elem interface { + IDName() string // 业务id名字 friend_user_id + IDValue() any // 业务id值 userID -> "100000000" + VersionName() string // 版本号 + DeletedName() string // 删除字段名字 + ToMap() map[string]any // 把结构体转换为map +} + +type List[D any, E Elem] struct { + coll *mongo.Collection + lf ListDoc +} + +func (l *List[D, E]) zeroE() E { + var t E + return t +} + +func (l *List[D, E]) FindElem(ctx context.Context, lid any, eid any) (E, error) { + res, err := l.FindElems(ctx, lid, []any{eid}) + if err != nil { + return l.zeroE(), err + } + if len(res) == 0 { + return l.zeroE(), ErrNotFound + } + return res[0], nil +} + +// FindElems 查询Elems +func (l *List[D, E]) FindElems(ctx context.Context, lid any, eids []any) ([]E, error) { + //pipeline := []bson.M{ + // { + // "$match": bson.M{ + // l.lf.IDName(): lid, + // l.lf.IDName() + "." + l.lf.ElemsID(): bson.M{ + // "$in": eids, + // }, + // }, + // }, + // { + // "$unwind": "$" + l.lf.ElemsName(), + // }, + // { + // "$match": bson.M{ + // l.lf.IDName() + "." + l.lf.ElemsID(): bson.M{ + // "$in": eids, + // }, + // }, + // }, + //} + panic("todo") +} + +func (l *List[D, E]) Find(ctx context.Context, filter any, opts ...*options.FindOptions) ([]E, error) { + return nil, nil +} + +func (l *List[D, E]) Count(ctx context.Context, filter any, opts ...*options.CountOptions) (int64, error) { + return 0, nil +} + +func (l *List[D, E]) Update(ctx context.Context, lid any, eid any) (*mongo.UpdateResult, error) { + + return nil, nil +} + +func (l *List[D, E]) Delete(ctx context.Context, lid any, eids any) (*mongo.UpdateResult, error) { + + return nil, nil +} + +func (l *List[D, E]) Page(ctx context.Context, filter any, pagination pagination.Pagination, opts ...*options.FindOptions) (int64, []E, error) { + return 0, nil, nil +} + +func (l *List[D, E]) ElemIDs(ctx context.Context, filter any, opts ...*options.FindOptions) ([]E, error) { + + return nil, nil +} + +// InsertElem 插入一个 +func (l *List[D, E]) InsertElem(ctx context.Context, lid any, e Elem) error { + if err := l.insertElem(ctx, lid, e); err == nil { + return nil + } else if !errors.Is(err, ErrListNotFound) { + return err + } + if _, err := l.coll.InsertOne(ctx, l.lf.BuildDoc(lid, e)); err == nil { + return nil + } else if mongo.IsDuplicateKeyError(err) { + return l.insertElem(ctx, lid, e) + } else { + return err + } +} + +func (l *List[D, E]) insertElem(ctx context.Context, lid any, e Elem) error { + data := e.ToMap() + data[e.VersionName()] = "$max_version" + filter := bson.M{ + l.lf.IDName(): lid, + } + pipeline := []bson.M{ + { + "$addFields": bson.M{ + "found_elem": bson.M{ + "$in": bson.A{e.IDValue(), l.lf.ElemsName() + "." + e.IDName()}, + }, + }, + }, + { + "$set": bson.M{ + "max_version": bson.M{ + "$cond": bson.M{ + "if": "$found_elem", + "then": "$max_version", + "else": bson.M{"$add": bson.A{"max_version", 1}}, + }, + }, + }, + }, + { + "$set": bson.M{ + l.lf.ElemsName(): bson.M{ + "$cond": bson.M{ + "if": "$found_elem", + "then": "$" + l.lf.ElemsName(), + "else": bson.M{ + "$concatArrays": bson.A{ + "$" + l.lf.ElemsName(), + bson.A{ + data, + }, + }, + }, + }, + }, + }, + }, + { + "$unset": "found_elem", + }, + } + res, err := mongoutil.UpdateMany(ctx, l.coll, filter, pipeline) + if err != nil { + return err + } + if res.MatchedCount == 0 { + return ErrListNotFound + } + if res.ModifiedCount == 0 { + return ErrElemExist + } + return nil +} diff --git a/pkg/common/listdemo/friend_model.go b/pkg/common/listdemo/friend_model.go new file mode 100644 index 000000000..13507bfa3 --- /dev/null +++ b/pkg/common/listdemo/friend_model.go @@ -0,0 +1,5 @@ +package listdemo + +type friendModel struct { + db *List[*Friend, *FriendElem] +} diff --git a/pkg/common/listdemo/friend_table.go b/pkg/common/listdemo/friend_table.go new file mode 100644 index 000000000..b1ad877a2 --- /dev/null +++ b/pkg/common/listdemo/friend_table.go @@ -0,0 +1,86 @@ +package listdemo + +import ( + "time" +) + +var ( + _ Elem = (*FriendElem)(nil) + _ ListDoc = (*Friend)(nil) +) + +type FriendElem struct { + FriendUserID string `bson:"friend_user_id"` + Nickname string `bson:"nickname"` + FaceURL string `bson:"face_url"` + Remark string `bson:"remark"` + CreateTime time.Time `bson:"create_time"` + AddSource int32 `bson:"add_source"` + OperatorUserID string `bson:"operator_user_id"` + Ex string `bson:"ex"` + IsPinned bool `bson:"is_pinned"` + Version uint `bson:"version"` + DeleteTime *time.Time `bson:"delete_time"` +} + +func (f *FriendElem) IDName() string { + return "friend_user_id" +} + +func (f *FriendElem) IDValue() any { + return f.FriendUserID +} + +func (f *FriendElem) VersionName() string { + return "version" +} + +func (f *FriendElem) DeletedName() string { + return "delete_time" +} + +func (f *FriendElem) ToMap() map[string]any { + return map[string]any{ + "friend_user_id": f.FriendUserID, + "nickname": f.Nickname, + "face_url": f.FaceURL, + "remark": f.Remark, + "create_time": f.CreateTime, + "add_source": f.AddSource, + "operator_user_id": f.OperatorUserID, + "ex": f.Ex, + "is_pinned": f.IsPinned, + "version": f.Version, + "delete_time": f.DeleteTime, + } +} + +type Friend struct { + UserID string `bson:"user_id"` + Friends []*FriendElem `bson:"friends"` + Version uint `bson:"version"` + DeleteVersion uint `bson:"delete_version"` +} + +func (f *Friend) BuildDoc(lid any, e Elem) any { + return &Friend{ + UserID: lid.(string), + Friends: []*FriendElem{e.(*FriendElem)}, + } +} + +func (f *Friend) ElemsID() string { + return "user_id" +} + +func (f *Friend) IDName() string { + return "user_id" +} + +func (f *Friend) ElemsName() string { + return "friends" +} + +func (f *Friend) VersionName() string { + return "version" +}