From e7038a44e9baee50d0752a319175f01ff0558aba Mon Sep 17 00:00:00 2001 From: xuthus5 Date: Thu, 22 Jun 2023 23:14:51 +0800 Subject: [PATCH] first commit --- README.md | 10 +++ autogen_model_model.go | 35 ++++++++ count.go | 71 +++++++++++++++ drop.go | 25 ++++++ errors.go | 13 +++ gen.sh | 1 + go.mod | 30 +++++++ go.sum | 77 +++++++++++++++++ indexes.go | 26 ++++++ insert.go | 103 ++++++++++++++++++++++ mdbc.go | 111 ++++++++++++++++++++++++ mdbc_test.go | 47 ++++++++++ model.pb.go | 191 +++++++++++++++++++++++++++++++++++++++++ model.proto | 16 ++++ readpref.go | 30 +++++++ scope.go | 87 +++++++++++++++++++ tabler.go | 5 ++ utils.go | 8 ++ 18 files changed, 886 insertions(+) create mode 100644 README.md create mode 100644 autogen_model_model.go create mode 100644 count.go create mode 100644 drop.go create mode 100644 errors.go create mode 100644 gen.sh create mode 100644 go.mod create mode 100644 go.sum create mode 100644 indexes.go create mode 100644 insert.go create mode 100644 mdbc.go create mode 100644 mdbc_test.go create mode 100644 model.pb.go create mode 100644 model.proto create mode 100644 readpref.go create mode 100644 scope.go create mode 100644 tabler.go create mode 100644 utils.go diff --git a/README.md b/README.md new file mode 100644 index 0000000..30a3959 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +## mdbc + +mdbc: mongodb database connector + +项目命名为mdbc 一个mongodb数据库orm,对官方驱动进行封装,将protobuf的Message和mongodb的Collection进行绑定,实现对Message的操作可以同步到数据库中 + +## todo + +- [ ] 完成初始化程序 +- [ ] 封装CRUD \ No newline at end of file diff --git a/autogen_model_model.go b/autogen_model_model.go new file mode 100644 index 0000000..794bcc3 --- /dev/null +++ b/autogen_model_model.go @@ -0,0 +1,35 @@ +// Code generated by protoc-gen-coco. DO NOT EDIT. +// source: model.proto +// generate at: 2023-06-18 18:16:24 + +package mdbc + +const TableNameModelArticles = "articles" + +func (t *ModelArticles) TableName() string { + return "articles" +} + +func (m *ModelArticles) GetIdField() string { + return "_id" +} + +func (m *ModelArticles) GetTitleField() string { + return "title" +} + +func (m *ModelArticles) GetAvatarUrlField() string { + return "avatar_url" +} + +func (m *ModelArticles) GetPhoneField() string { + return "phone" +} + +func (m *ModelArticles) GetCreateTimeField() string { + return "create_time" +} + +func (m *ModelArticles) GetUpdateTimeField() string { + return "update_time" +} diff --git a/count.go b/count.go new file mode 100644 index 0000000..3c2c3ae --- /dev/null +++ b/count.go @@ -0,0 +1,71 @@ +package mdbc + +import ( + "context" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo/options" + "reflect" +) + +type countScope struct { + *scope + *mdbc + ctx context.Context + limit int64 + skip int64 + filter interface{} + opts options.CountOptions +} + +// SetContext set operate context, default timeout 30s +func (cs *countScope) SetContext(ctx context.Context) *countScope { + cs.ctx = ctx + return cs +} + +// SetLimit set filter limit size +func (cs *countScope) SetLimit(limit int64) *countScope { + cs.limit = limit + return cs +} + +// SetSkip set filter skip size +func (cs *countScope) SetSkip(skip int64) *countScope { + cs.skip = skip + return cs +} + +func (cs *countScope) SetFilter(filter interface{}) *countScope { + if filter == nil { + cs.filter = bson.M{} + return cs + } + v := reflect.ValueOf(filter) + if v.Kind() == reflect.Ptr || v.Kind() == reflect.Map || v.Kind() == reflect.Slice { + if v.IsNil() { + cs.filter = bson.M{} + } + } + cs.filter = filter + return cs +} + +func (cs *countScope) mergeOptions() { + if cs.ctx == nil { + cs.ctx = context.Background() + } + if cs.filter == nil { + cs.filter = bson.M{} + } + if cs.skip != 0 { + cs.opts.Skip = &cs.skip + } + if cs.limit != 0 { + cs.opts.Limit = &cs.limit + } +} + +func (cs *countScope) Count() (int64, error) { + cs.mergeOptions() + return cs.database.Collection(cs.tableName).CountDocuments(cs.ctx, cs.filter, &cs.opts) +} diff --git a/drop.go b/drop.go new file mode 100644 index 0000000..60e38d0 --- /dev/null +++ b/drop.go @@ -0,0 +1,25 @@ +package mdbc + +import "context" + +type dropScope struct { + *scope + *mdbc + ctx context.Context +} + +func (ds *dropScope) SetContext(ctx context.Context) *dropScope { + ds.ctx = ctx + return ds +} + +func (ds *dropScope) mergeOptions() { + if ds.ctx == nil { + ds.ctx = context.Background() + } +} + +func (ds *dropScope) Do() error { + ds.mergeOptions() + return ds.database.Collection(ds.tableName).Drop(ds.ctx) +} diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..1ef0469 --- /dev/null +++ b/errors.go @@ -0,0 +1,13 @@ +package mdbc + +import "errors" + +var ( + ErrorRecordNotFound = errors.New("record not found") + ErrorNoInsertDocuments = errors.New("no insert documents") +) + +// IsRecordNotFound is record not found +func IsRecordNotFound(err error) bool { + return errors.Is(err, ErrorRecordNotFound) +} diff --git a/gen.sh b/gen.sh new file mode 100644 index 0000000..6d5babf --- /dev/null +++ b/gen.sh @@ -0,0 +1 @@ +protoc.exe model.proto --coco_out=. --go_out=. \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..419e996 --- /dev/null +++ b/go.mod @@ -0,0 +1,30 @@ +module gitter.top/coco/mdbc + +go 1.20 + +require ( + github.com/sirupsen/logrus v1.9.3 + github.com/stretchr/testify v1.7.0 + gitter.top/coco/lormatter v0.0.0-20230409145644-f9cb43f740dc + gitter.top/common/goref v0.0.0-20230622151024-9b220e13c7cd + go.mongodb.org/mongo-driver v1.11.7 + google.golang.org/protobuf v1.30.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/golang/snappy v0.0.1 // indirect + github.com/klauspost/compress v1.13.6 // indirect + github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.1 // indirect + github.com/xdg-go/stringprep v1.0.3 // indirect + github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect + golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect + golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect + golang.org/x/text v0.3.7 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..75f1fd4 --- /dev/null +++ b/go.sum @@ -0,0 +1,77 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +gitter.top/coco/lormatter v0.0.0-20230409145644-f9cb43f740dc h1:B0bnfX8Y/dETV+zdcZNCwD/L+DLZN2wSf9szo/fxNCw= +gitter.top/coco/lormatter v0.0.0-20230409145644-f9cb43f740dc/go.mod h1:vyfU6MQ56tkoFKSzEzhYSwXjgZHMcu3AViHC3hIyO7I= +gitter.top/common/goref v0.0.0-20230622151024-9b220e13c7cd h1:EeOtSwGexcx0u1AE91wnemiOGedENxaKcJUXxpzcgiE= +gitter.top/common/goref v0.0.0-20230622151024-9b220e13c7cd/go.mod h1:oK6jZQ/ISS8gZ78rvww6p7FuLUzaJ+S5F5UXSqO7Lr0= +go.mongodb.org/mongo-driver v1.11.7 h1:LIwYxASDLGUg/8wOhgOOZhX8tQa/9tgZPgzZoVqJvcs= +go.mongodb.org/mongo-driver v1.11.7/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/indexes.go b/indexes.go new file mode 100644 index 0000000..b5e6f3a --- /dev/null +++ b/indexes.go @@ -0,0 +1,26 @@ +package mdbc + +import "context" + +type indexesScope struct { + *scope + *mdbc + ctx context.Context +} + +func (is *indexesScope) SetContext(ctx context.Context) *indexesScope { + is.ctx = ctx + return is +} + +func (is *indexesScope) Create(ctx context.Context) error { + return nil +} + +func (is *indexesScope) Drop(ctx context.Context) error { + return nil +} + +func (is *indexesScope) List(ctx context.Context) error { + return nil +} diff --git a/insert.go b/insert.go new file mode 100644 index 0000000..be67b00 --- /dev/null +++ b/insert.go @@ -0,0 +1,103 @@ +package mdbc + +import ( + "context" + "gitter.top/common/goref" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type insertScope struct { + *scope + *mdbc + ctx context.Context + opts any + docs any +} + +type insert interface { + insert(docs any) (*InsertResult, error) +} + +type insertOne struct { + *insertScope +} + +func (io *insertOne) insert(docs any) (*InsertResult, error) { + result, err := io.database.Collection(io.tableName).InsertOne(io.ctx, docs, io.opts.(*options.InsertOneOptions)) + if err != nil { + return nil, err + } + return &InsertResult{id: result.InsertedID}, nil +} + +type insertMany struct { + *insertScope +} + +func (im *insertMany) insert(docs any) (*InsertResult, error) { + vals, ok := goref.ToInterfaces(docs) + if !ok { + return nil, ErrorNoInsertDocuments + } + result, err := im.database.Collection(im.tableName).InsertMany(im.ctx, vals, im.opts.(*options.InsertManyOptions)) + if err != nil { + return nil, err + } + return &InsertResult{id: result.InsertedIDs}, nil +} + +type InsertResult struct { + id interface{} +} + +func (ir *InsertResult) GetID() string { + val, ok := goref.ToString(ir.id) + if !ok { + } + return val +} + +func (ir *InsertResult) GetIDs() []string { + vals, ok := goref.ToStrings(ir.id) + if !ok { + } + return vals +} + +func (is *insertScope) SetContext(ctx context.Context) *insertScope { + is.ctx = ctx + return is +} + +func (is *insertScope) SetInsertOneOptions(opts options.InsertOneOptions) *insertScope { + is.opts = opts + return is +} + +func (is *insertScope) SetInsertManyOptions(opts options.InsertManyOptions) *insertScope { + is.opts = opts + return is +} + +func (is *insertScope) mergeOptions(isMany bool) *insertScope { + if is.ctx == nil { + is.ctx = context.Background() + } + if is.opts == nil && isMany { + is.opts = &options.InsertManyOptions{} + } + if is.opts == nil && !isMany { + is.opts = &options.InsertOneOptions{} + } + return is +} + +func (is *insertScope) Insert(docs any) (*InsertResult, error) { + isMany := goref.IsBaseList(docs) + is.mergeOptions(isMany) + var action insert = &insertOne{is} + if isMany { + action = &insertMany{is} + } + return action.insert(docs) +} diff --git a/mdbc.go b/mdbc.go new file mode 100644 index 0000000..496190c --- /dev/null +++ b/mdbc.go @@ -0,0 +1,111 @@ +package mdbc + +import ( + "context" + "github.com/sirupsen/logrus" + "gitter.top/coco/lormatter" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readpref" + "google.golang.org/protobuf/proto" + "reflect" + "time" +) + +func init() { + formatter := &lormatter.Formatter{} + logrus.SetFormatter(formatter) + logrus.SetReportCaller(true) +} + +type Config struct { + uri string + Username string `json:"username" yaml:"username"` + Password string `json:"password" yaml:"password"` + Address string `json:"address" yaml:"address"` + Port int `json:"port" yaml:"port"` + ProcessTimeout int `json:"process_timeout" yaml:"process_timeout"` + DbName string `json:"db_name" yaml:"db_name"` + ReadPref ReadPref `json:"read_pref" yaml:"read_pref"` +} + +func (c *Config) merge() { + if c.Address == "" { + c.Address = "localhost" + } + if c.Port == 0 { + c.Port = 27017 + } + if c.ProcessTimeout == 0 { + c.ProcessTimeout = 30 + } +} + +type mdbc struct { + client *mongo.Client + database *mongo.Database + readpref *readpref.ReadPref +} + +func NewMDBC(config *Config) *mdbc { + var driver = new(mdbc) + var clientOpts = options.Client() + config.merge() + clientOpts.ApplyURI(applyURI(config.Address, config.Port)) + if config.Username != "" && config.Password != "" { + credential := options.Credential{ + Username: config.Username, + Password: config.Password, + } + clientOpts.SetAuth(credential) + } + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(config.ProcessTimeout)*time.Second) + defer cancel() + client, err := mongo.Connect(ctx, clientOpts) + if err != nil { + panic(err) + } + driver.readpref = readPref(config.ReadPref) + if err := client.Ping(context.Background(), driver.readpref); err != nil { + panic(err) + } + driver.client = client + if config.DbName != "" { + driver.database = client.Database(config.DbName) + } + return driver +} + +// GetClient get raw mongo client +func (m *mdbc) GetClient() *mongo.Client { + return m.client +} + +// GetDatabase get raw mongo database +func (m *mdbc) GetDatabase() *mongo.Database { + return m.database +} + +// BindModel mapping protobuf Message to mongo Collection +func (m *mdbc) BindModel(prototype any) Collection { + if prototype == nil { + panic("model can not be nil") + } + if _, impled := prototype.(tabler); !impled { + panic("model does not implement tabler interface") + } + protomsg, impled := prototype.(proto.Message) + if !impled { + panic("model does not implement message interface") + } + scope := &scope{ + &model{ + Type: protomsg, + modelKind: reflect.TypeOf(protomsg), + modelName: string(protomsg.ProtoReflect().Descriptor().FullName()), + tableName: prototype.(tabler).TableName(), + }, + m, + } + return scope +} diff --git a/mdbc_test.go b/mdbc_test.go new file mode 100644 index 0000000..59c7faf --- /dev/null +++ b/mdbc_test.go @@ -0,0 +1,47 @@ +package mdbc + +import ( + "context" + "github.com/stretchr/testify/assert" + "gitter.top/common/goref" + "go.mongodb.org/mongo-driver/bson" + "testing" +) + +func newDriver() *mdbc { + return NewMDBC(&Config{ + Address: "192.168.3.21", + Port: 0, + Username: "admin", + Password: "admin", + ProcessTimeout: 0, + ReadPref: 0, + DbName: "articles", + }) +} + +func TestNewMDBC(t *testing.T) { + driver := newDriver() + databases, err := driver.GetClient().ListDatabaseNames(context.Background(), bson.D{}) + assert.NoError(t, err) + t.Log(databases) +} + +func TestScope_Count(t *testing.T) { + driver := newDriver() + article := driver.BindModel(&ModelArticles{}) + value, err := article.Count().SetContext(context.Background()).Count() + assert.NoError(t, err) + t.Log(value) +} + +func TestDropScope_Do(t *testing.T) { + driver := newDriver() + article := driver.BindModel(&ModelArticles{}) + err := article.Drop().Do() + assert.NoError(t, err) +} + +func TestInsertScope_Insert(t *testing.T) { + +} diff --git a/model.pb.go b/model.pb.go new file mode 100644 index 0000000..03556c3 --- /dev/null +++ b/model.pb.go @@ -0,0 +1,191 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.21.5 +// source: model.proto + +package mdbc + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// @table_name: articles +type ModelArticles struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // 主键ID + Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` // 标题 + AvatarUrl string `protobuf:"bytes,5,opt,name=avatar_url,json=avatarUrl,proto3" json:"avatar_url,omitempty"` // 封面 + Phone string `protobuf:"bytes,6,opt,name=phone,proto3" json:"phone,omitempty"` // 手机号码 + CreateTime int64 `protobuf:"varint,12,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"` // 创建时间 + UpdateTime int64 `protobuf:"varint,13,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"` // 更新时间 +} + +func (x *ModelArticles) Reset() { + *x = ModelArticles{} + if protoimpl.UnsafeEnabled { + mi := &file_model_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ModelArticles) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ModelArticles) ProtoMessage() {} + +func (x *ModelArticles) ProtoReflect() protoreflect.Message { + mi := &file_model_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ModelArticles.ProtoReflect.Descriptor instead. +func (*ModelArticles) Descriptor() ([]byte, []int) { + return file_model_proto_rawDescGZIP(), []int{0} +} + +func (x *ModelArticles) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *ModelArticles) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *ModelArticles) GetAvatarUrl() string { + if x != nil { + return x.AvatarUrl + } + return "" +} + +func (x *ModelArticles) GetPhone() string { + if x != nil { + return x.Phone + } + return "" +} + +func (x *ModelArticles) GetCreateTime() int64 { + if x != nil { + return x.CreateTime + } + return 0 +} + +func (x *ModelArticles) GetUpdateTime() int64 { + if x != nil { + return x.UpdateTime + } + return 0 +} + +var File_model_proto protoreflect.FileDescriptor + +var file_model_proto_rawDesc = []byte{ + 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x6d, + 0x64, 0x62, 0x63, 0x22, 0xac, 0x01, 0x0a, 0x0d, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x41, 0x72, 0x74, + 0x69, 0x63, 0x6c, 0x65, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, + 0x76, 0x61, 0x74, 0x61, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x68, + 0x6f, 0x6e, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x68, 0x6f, 0x6e, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, + 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, + 0x6d, 0x65, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x3b, 0x6d, 0x64, 0x62, 0x63, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_model_proto_rawDescOnce sync.Once + file_model_proto_rawDescData = file_model_proto_rawDesc +) + +func file_model_proto_rawDescGZIP() []byte { + file_model_proto_rawDescOnce.Do(func() { + file_model_proto_rawDescData = protoimpl.X.CompressGZIP(file_model_proto_rawDescData) + }) + return file_model_proto_rawDescData +} + +var file_model_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_model_proto_goTypes = []interface{}{ + (*ModelArticles)(nil), // 0: mdbc.ModelArticles +} +var file_model_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_model_proto_init() } +func file_model_proto_init() { + if File_model_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_model_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ModelArticles); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_model_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_model_proto_goTypes, + DependencyIndexes: file_model_proto_depIdxs, + MessageInfos: file_model_proto_msgTypes, + }.Build() + File_model_proto = out.File + file_model_proto_rawDesc = nil + file_model_proto_goTypes = nil + file_model_proto_depIdxs = nil +} diff --git a/model.proto b/model.proto new file mode 100644 index 0000000..97c3a2f --- /dev/null +++ b/model.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package mdbc; + +option go_package = "./;mdbc"; + + +// @table_name: articles +message ModelArticles { + string id = 1; // 主键ID + string title = 2; // 标题 + string avatar_url = 5; // 封面 + string phone = 6; // 手机号码 + int64 create_time = 12; // 创建时间 + int64 update_time = 13; // 更新时间 +} diff --git a/readpref.go b/readpref.go new file mode 100644 index 0000000..7f60a02 --- /dev/null +++ b/readpref.go @@ -0,0 +1,30 @@ +package mdbc + +import "go.mongodb.org/mongo-driver/mongo/readpref" + +type ReadPref uint8 + +const ( + PrimaryMode ReadPref = iota + 1 + PrimaryPreferredMode + SecondaryMode + SecondaryPreferredMode + NearestMode +) + +func readPref(pref ReadPref) *readpref.ReadPref { + switch pref { + case PrimaryMode: + return readpref.Primary() + case PrimaryPreferredMode: + return readpref.PrimaryPreferred() + case SecondaryMode: + return readpref.Secondary() + case SecondaryPreferredMode: + return readpref.SecondaryPreferred() + case NearestMode: + return readpref.Nearest() + default: + return readpref.Primary() + } +} diff --git a/scope.go b/scope.go new file mode 100644 index 0000000..c78678a --- /dev/null +++ b/scope.go @@ -0,0 +1,87 @@ +package mdbc + +import ( + "google.golang.org/protobuf/proto" + "reflect" +) + +// model props for protobuf Message +type model struct { + Type proto.Message // model prototype + modelKind reflect.Type // model low level type + modelName string // model struct name + tableName string // model mapping table name +} + +// Scope export operation for user +type scope struct { + *model + *mdbc +} + +type Collection interface { + Aggregate() + BulkWrite() + Clone() + Count() *countScope + Delete() + Find() + Drop() *dropScope + Indexes() *indexesScope + Insert() + Update() + Watch() +} + +// Aggregate aggregate operate for mongo +func (s *scope) Aggregate() { + +} + +func (s *scope) BulkWrite() { + +} + +func (s *scope) Clone() { + +} + +func (s *scope) Count() *countScope { + return &countScope{ + scope: s, + mdbc: s.mdbc, + } +} + +func (s *scope) Delete() { + +} + +func (s *scope) Find() { + +} + +func (s *scope) Drop() *dropScope { + return &dropScope{ + scope: s, + mdbc: s.mdbc, + } +} + +func (s *scope) Indexes() *indexesScope { + return &indexesScope{ + scope: s, + mdbc: s.mdbc, + } +} + +func (s *scope) Insert() { + +} + +func (s *scope) Update() { + +} +func (s *scope) Watch() { + +} diff --git a/tabler.go b/tabler.go new file mode 100644 index 0000000..086f45f --- /dev/null +++ b/tabler.go @@ -0,0 +1,5 @@ +package mdbc + +type tabler interface { + TableName() string +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..2ca83c1 --- /dev/null +++ b/utils.go @@ -0,0 +1,8 @@ +package mdbc + +import "fmt" + +// applyURI get mongo uri from address and port +func applyURI(address string, port int) string { + return fmt.Sprintf("mongodb://%s:%d", address, port) +}