From 5d3b11f63f99e636ec0c3560801bed8c5677790d Mon Sep 17 00:00:00 2001 From: jiangzhi Date: Fri, 7 Aug 2020 15:40:17 +0800 Subject: [PATCH 1/5] support aggregate --- README.md | 47 ++++--- README_ZH.md | 45 ++++--- aggregate.go | 48 +++++++ aggregate_test.go | 101 +++++++++++++++ base.go | 36 +++++- client.go | 5 - client_test.go | 7 +- collection.go | 22 ++-- collection_test.go | 119 ++++++++--------- cursor.go | 32 ++++- cursor_test.go | 45 ++++--- errors.go | 11 ++ compare_test.go => example/example_test.go | 103 +++++++-------- go.mod | 6 +- go.sum | 25 +++- interface.go | 10 +- query.go | 39 ++---- query_test.go | 141 ++++++++++----------- 18 files changed, 537 insertions(+), 305 deletions(-) create mode 100644 aggregate.go create mode 100644 aggregate_test.go create mode 100644 errors.go rename compare_test.go => example/example_test.go (56%) diff --git a/README.md b/README.md index 5a5d040..87092b4 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ var oneUserInfo = UserInfo{ Create index ```go -cli.EnsureIndexes(ctx, []string{"name"}, []string{"age", "name,weight"}) +cli.EnsureIndexes(ctx, []string{"_id"}, []string{"age", "name,weight"}) ``` - Insert a document @@ -113,36 +113,41 @@ err = cli.Remove(ctx, BsonT{"age": 7}) - Insert multiple data ```go -// batch insert +// multiple insert var batchUserInfoI = []interface{}{ - UserInfo{Name: "wxy", Age: 6, Weight: 20}, - UserInfo{Name: "jZ", Age: 6, Weight: 25}, - UserInfo{Name: "zp", Age: 6, Weight: 30}, - UserInfo{Name: "yxw", Age: 6, Weight: 35}, + UserInfo{Name: "a1", Age: 6, Weight: 20}, + UserInfo{Name: "b2", Age: 6, Weight: 25}, + UserInfo{Name: "c3", Age: 6, Weight: 30}, + UserInfo{Name: "d4", Age: 6, Weight: 35}, + UserInfo{Name: "a1", Age: 7, Weight: 40}, + UserInfo{Name: "a1", Age: 8, Weight: 45}, } result, err = cli.Collection.InsertMany(ctx, batchUserInfoI) ``` - Search all, sort and limit - ```go // find all, sort and limit batch := []UserInfo{} cli.Find(ctx, BsonT{"age": 6}).Sort("weight").Limit(7).All(&batch) ``` - +- Count +````go +count, err := cli.Find(ctx, BsonT{"age": 6}).Count() +```` +- Aggregate +```go +matchStage := qmgo.D{{"$match", []qmgo.E{{"weight", qmgo.D{{"$gt", 30}}}}}} +groupStage := qmgo.D{{"$group", qmgo.D{{"_id", "$name"}, {"total", qmgo.D{{"$sum", "$age"}}}}}} +var showsWithInfo []qmgo.M +err = cli.Aggregate(context.Background(), qmgo.Pipeline{matchStage, groupStage}).All(&showsWithInfo) +``` ## Feature - -- Supported - - CRUD to documents - - Create indexes - - Sort、limit、count -- TODO - - Transaction - - Aggregate - - Options for every operation - - +- CRUD to documents +- Create indexes +- Sort、limit、count、select +- Cursor +- Aggregate ## `qmgo` vs `mgo` vs `go.mongodb.org/mongo-driver` @@ -155,8 +160,8 @@ How do we do in`go.mongodb.org/mongo-driver`: // find all, sort and limit findOptions := options.Find() findOptions.SetLimit(7) // set limit -var sorts bson.D -sorts = append(sorts, bson.E{Key: "weight", Value: 1}) +var sorts D +sorts = append(sorts, E{Key: "weight", Value: 1}) findOptions.SetSort(sorts) // set sort batch := []UserInfo{} diff --git a/README_ZH.md b/README_ZH.md index fbbb791..a4ef89a 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -78,7 +78,7 @@ var oneUserInfo = UserInfo{ 创建索引 ```go -cli.EnsureIndexes(ctx, []string{"name"}, []string{"age", "name,weight"}) +cli.EnsureIndexes(ctx, []string{"_id"}, []string{"age", "name,weight"}) ``` - 插入一个文档 @@ -105,12 +105,14 @@ err = cli.Remove(ctx, BsonT{"age": 7}) - 插入多条数据 ```go -// batch insert +// multiple insert var batchUserInfoI = []interface{}{ - UserInfo{Name: "wxy", Age: 6, Weight: 20}, - UserInfo{Name: "jZ", Age: 6, Weight: 25}, - UserInfo{Name: "zp", Age: 6, Weight: 30}, - UserInfo{Name: "yxw", Age: 6, Weight: 35}, + UserInfo{Name: "a1", Age: 6, Weight: 20}, + UserInfo{Name: "b2", Age: 6, Weight: 25}, + UserInfo{Name: "c3", Age: 6, Weight: 30}, + UserInfo{Name: "d4", Age: 6, Weight: 35}, + UserInfo{Name: "a1", Age: 7, Weight: 40}, + UserInfo{Name: "a1", Age: 8, Weight: 45}, } result, err = cli.Collection.InsertMany(ctx, batchUserInfoI) ``` @@ -123,17 +125,26 @@ batch := []UserInfo{} cli.Find(ctx, BsonT{"age": 6}).Sort("weight").Limit(7).All(&batch) ``` -## 功能 +- Count +````go +count, err := cli.Find(ctx, BsonT{"age": 6}).Count() +```` -- 已经支持 - - 文档的增删改查 - - 索引配置 - - 查询`Sort`、`Limit`、`Count` +- Aggregate +```go +matchStage := qmgo.D{{"$match", []qmgo.E{{"weight", qmgo.D{{"$gt", 30}}}}}} +groupStage := qmgo.D{{"$group", qmgo.D{{"_id", "$name"}, {"total", qmgo.D{{"$sum", "$age"}}}}}} +var showsWithInfo []qmgo.M +err = cli.Aggregate(context.Background(), qmgo.Pipeline{matchStage, groupStage}).All(&showsWithInfo) +``` + +## 功能 +- 文档的增删改查 +- 索引配置 +- `Sort`、`Limit`、`Count`、`Select` +- `Cursor` +- 聚合`Aggregate` -- TODO: - - 事务 - - 聚合`Aggregate` - - 操作支持`Options` ## `qmgo` vs `mgo` vs `go.mongodb.org/mongo-driver` @@ -146,8 +157,8 @@ cli.Find(ctx, BsonT{"age": 6}).Sort("weight").Limit(7).All(&batch) // find all 、sort and limit findOptions := options.Find() findOptions.SetLimit(7) // set limit -var sorts bson.D -sorts = append(sorts, bson.E{Key: "weight", Value: 1}) +var sorts D +sorts = append(sorts, E{Key: "weight", Value: 1}) findOptions.SetSort(sorts) // set sort batch := []UserInfo{} diff --git a/aggregate.go b/aggregate.go new file mode 100644 index 0000000..e8a4251 --- /dev/null +++ b/aggregate.go @@ -0,0 +1,48 @@ +package qmgo + +import ( + "context" + "go.mongodb.org/mongo-driver/mongo" +) + +type Pipeline []D + +type Aggregate struct { + ctx context.Context + pipeline interface{} + collection *mongo.Collection +} + +func (a *Aggregate) All(results interface{}) error { + c, err := a.collection.Aggregate(a.ctx, a.pipeline) + if err != nil { + return err + } + return c.All(a.ctx, results) +} + +func (a *Aggregate) One(result interface{}) error { + c, err := a.collection.Aggregate(a.ctx, a.pipeline) + if err != nil { + return err + } + cr := Cursor{ + ctx: a.ctx, + cursor: c, + err: err, + } + defer cr.Close() + if !cr.Next(result) { + return ERR_NO_SUCH_RECORD + } + return err +} + +func (a *Aggregate) Iter() CursorI { + c, err := a.collection.Aggregate(a.ctx, a.pipeline) + return &Cursor{ + ctx: a.ctx, + cursor: c, + err: err, + } +} diff --git a/aggregate_test.go b/aggregate_test.go new file mode 100644 index 0000000..0525ad6 --- /dev/null +++ b/aggregate_test.go @@ -0,0 +1,101 @@ +package qmgo + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/require" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +func TestAggregate(t *testing.T) { + ast := require.New(t) + + var cli *QmgoClient + + cli = initClient("test") + cli.DropCollection(context.Background()) + cli.EnsureIndexes(context.Background(), nil, []string{"name"}) + + id1 := primitive.NewObjectID() + id2 := primitive.NewObjectID() + id3 := primitive.NewObjectID() + id4 := primitive.NewObjectID() + id5 := primitive.NewObjectID() + docs := []interface{}{ + QueryTestItem{Id: id1, Name: "Alice", Age: 10}, + QueryTestItem{Id: id2, Name: "Alice", Age: 12}, + QueryTestItem{Id: id3, Name: "Lucas", Age: 33}, + QueryTestItem{Id: id4, Name: "Lucas", Age: 22}, + QueryTestItem{Id: id5, Name: "Lucas", Age: 44}, + } + cli.InsertMany(context.Background(), docs) + matchStage := D{{"$match", []E{{"age", D{{"$gt", 11}}}}}} + groupStage := D{{"$group", D{{"_id", "$name"}, {"total", D{{"$sum", "$age"}}}}}} + var showsWithInfo []M + // aggregate ALL() + err := cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}).All(&showsWithInfo) + ast.NoError(err) + ast.Equal(2, len(showsWithInfo)) + for _, v := range showsWithInfo { + if "Alice" == v["_id"] { + ast.Equal(int32(12), v["total"]) + continue + } + if "Lucas" == v["_id"] { + ast.Equal(int32(99), v["total"]) + continue + } + ast.Error(errors.New("error"), "impossible") + } + // Iter() + iter := cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}) + ast.NotNil(iter) + err = iter.All(&showsWithInfo) + ast.NoError(err) + for _, v := range showsWithInfo { + if "Alice" == v["_id"] { + ast.Equal(int32(12), v["total"]) + continue + } + if "Lucas" == v["_id"] { + ast.Equal(int32(99), v["total"]) + continue + } + ast.Error(errors.New("error"), "impossible") + } + // One() + var oneInfo M + + iter = cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}) + ast.NotNil(iter) + + err = iter.One(&oneInfo) + ast.NoError(err) + ast.Equal(true, oneInfo["_id"] == "Alice" || oneInfo["_id"] == "Lucas") + + // iter + iter = cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}) + ast.NotNil(iter) + + i := iter.Iter() + + ct := i.Next(&oneInfo) + ast.Equal(true, oneInfo["_id"] == "Alice" || oneInfo["_id"] == "Lucas") + ast.Equal(true, ct) + ct = i.Next(&oneInfo) + ast.Equal(true, oneInfo["_id"] == "Alice" || oneInfo["_id"] == "Lucas") + ast.Equal(true, ct) + ct = i.Next(&oneInfo) + ast.Equal(false, ct) + + // err + ast.Error(cli.Aggregate(context.Background(), 1).All(&showsWithInfo)) + ast.Error(cli.Aggregate(context.Background(), 1).One(&showsWithInfo)) + ast.Error(cli.Aggregate(context.Background(), 1).Iter().Err()) + matchStage = D{{"$match", []E{{"age", D{{"$gt", 100}}}}}} + groupStage = D{{"$group", D{{"_id", "$name"}, {"total", D{{"$sum", "$age"}}}}}} + ast.Error(cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}).One(&showsWithInfo)) + +} diff --git a/base.go b/base.go index 2e58811..bca8a40 100644 --- a/base.go +++ b/base.go @@ -2,9 +2,35 @@ package qmgo import ( "strings" - "time" ) +// D is an ordered representation of a BSON document. This type should be used when the order of the elements matters, +// such as MongoDB command documents. If the order of the elements does not matter, an M should be used instead. +// +// Example usage: +// D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}} +type D []E + +// E represents a BSON element for a D. It is usually used inside a D. +type E struct { + Key string + Value interface{} +} + +// M is an unordered representation of a BSON document. This type should be used when the order of the elements does not +// matter. This type is handled as a regular map[string]interface{} when encoding and decoding. Elements will be +// serialized in an undefined, random order. If the order of the elements matters, a D should be used instead. +// +// Example usage: +// +// M{"foo": "bar", "hello": "world", "pi": 3.14159}. +type M map[string]interface{} + +// An A is an ordered representation of a BSON array. +// Example usage: +// A{"bar", "world", 3.14159, D{{"qux", 12345}}} +type A []interface{} + // QmgoConfig for initial mongodb instance type Config struct { // URI example: [mongodb://][user:pass@]host1[:port1][,host2[:port2],...][/database][?options] @@ -31,10 +57,10 @@ func IsDup(err error) bool { return strings.Contains(err.Error(), "E11000") } -// Now return Millisecond current time -func Now() time.Time { - return time.Unix(0, time.Now().UnixNano()/1e6*1e6) -} +//// Now return Millisecond current time +//func Now() time.Time { +// return time.Unix(0, time.Now().UnixNano()/1e6*1e6) +//} // SplitSortField handle sort symbol: "+"/"-" in front of field // if "+", return sort as 1 diff --git a/client.go b/client.go index 981022e..1c54da7 100644 --- a/client.go +++ b/client.go @@ -27,11 +27,6 @@ func Open(ctx context.Context, conf *Config) (cli *QmgoClient, err error) { } db := client.Database(conf.Database) - if err != nil { - fmt.Println("new database fail", err) - return - } - coll := db.Collection(conf.Coll) cli = &QmgoClient{ diff --git a/client_test.go b/client_test.go index f2a3af9..fd4d6f2 100644 --- a/client_test.go +++ b/client_test.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "go.mongodb.org/mongo-driver/bson" ) func initClient(col string) *QmgoClient { @@ -64,7 +63,7 @@ func TestQmgoClient(t *testing.T) { err = cli.Ping(5) ast.NoError(err) - res, err := cli.InsertOne(context.Background(), bson.D{{Key: "x", Value: 1}}) + res, err := cli.InsertOne(context.Background(), D{{Key: "x", Value: 1}}) ast.NoError(err) ast.NotNil(res) @@ -72,7 +71,7 @@ func TestQmgoClient(t *testing.T) { // close Client cli.Close(context.TODO()) - _, err = cli.InsertOne(context.Background(), bson.D{{Key: "x", Value: 1}}) + _, err = cli.InsertOne(context.Background(), D{{Key: "x", Value: 1}}) ast.EqualError(err, "client is disconnected") err = cli.Ping(5) @@ -102,7 +101,7 @@ func TestClient(t *testing.T) { ast.Equal(nil, err) coll := c.Database("mongoxtest").Collection("testopen") - res, err := coll.InsertOne(context.Background(), bson.D{{Key: "x", Value: 1}}) + res, err := coll.InsertOne(context.Background(), D{{Key: "x", Value: 1}}) ast.NoError(err) ast.NotNil(res) coll.DropCollection(context.Background()) diff --git a/collection.go b/collection.go index 73e8242..c1bf672 100644 --- a/collection.go +++ b/collection.go @@ -2,7 +2,6 @@ package qmgo import ( "context" - "errors" "fmt" "strings" @@ -11,10 +10,6 @@ import ( "go.mongodb.org/mongo-driver/x/bsonx" ) -var ( - NoSuchRecordErr = errors.New("no such record") -) - // Collection type Collection struct { collection *mongo.Collection @@ -73,7 +68,7 @@ func (c *Collection) Update(ctx context.Context, filter interface{}, update inte } if res.MatchedCount == 0 { - err = NoSuchRecordErr + err = ERR_NO_SUCH_RECORD } return err @@ -92,7 +87,7 @@ func (c *Collection) UpdateAll(ctx context.Context, filter interface{}, update i } // Remove executes a delete command to delete at most one document from the collection. -// if filter is bson.M{},DeleteOne will delete one document in collection +// if filter is M{},DeleteOne will delete one document in collection // Reference: https://docs.mongodb.com/manual/reference/command/delete/ func (c *Collection) Remove(ctx context.Context, filter interface{}) (err error) { @@ -101,14 +96,14 @@ func (c *Collection) Remove(ctx context.Context, filter interface{}) (err error) return err } if res.DeletedCount == 0 { - err = NoSuchRecordErr + err = ERR_NO_SUCH_RECORD } return err } // DeleteAll executes a delete command to delete documents from the collection. -// If filter is bson.M{},all ducuments in Collection will be deleted +// If filter is M{},all ducuments in Collection will be deleted // Reference: https://docs.mongodb.com/manual/reference/command/delete/ func (c *Collection) DeleteAll(ctx context.Context, filter interface{}) (result *DeleteResult, err error) { @@ -119,6 +114,15 @@ func (c *Collection) DeleteAll(ctx context.Context, filter interface{}) (result return } +// Aggregate executes an aggregate command against the collection and returns a AggregateI to get resulting documents. +func (c *Collection) Aggregate(ctx context.Context, pipeline interface{}) AggregateI { + return &Aggregate{ + ctx: ctx, + collection: c.collection, + pipeline: pipeline, + } +} + // ensureIndex create multiple indexes on the collection and returns the names of // Example:indexes = []string{"idx1", "-idx2", "idx3,idx4"} // Three indexes will be created, index idx1 with ascending order, index idx2 with descending order, idex3 and idex4 are Compound ascending sort index diff --git a/collection_test.go b/collection_test.go index a6f3326..4edd9f8 100644 --- a/collection_test.go +++ b/collection_test.go @@ -3,7 +3,6 @@ package qmgo import ( "context" "github.com/stretchr/testify/require" - "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "testing" ) @@ -24,13 +23,15 @@ func TestCollection_EnsureIndex(t *testing.T) { // check if unique indexs is working var err error - doc := bson.M{ + doc := M{ "id1": 1, } - _, err = cli.InsertOne(context.Background(), doc) ast.NoError(err) - _, err = cli.InsertOne(context.Background(), doc) + + coll, err := cli.CloneCollection() + ast.NoError(err) + _, err = coll.InsertOne(context.Background(), doc) ast.Equal(true, IsDup(err)) } @@ -49,7 +50,7 @@ func TestCollection_EnsureIndexes(t *testing.T) { // check if unique indexs is working var err error - doc := bson.M{ + doc := M{ "id1": 1, } @@ -69,7 +70,7 @@ func TestCollection_Insert(t *testing.T) { cli.EnsureIndexes(context.Background(), []string{"name"}, nil) var err error - doc := bson.M{"_id": primitive.NewObjectID(), "name": "Alice"} + doc := M{"_id": primitive.NewObjectID(), "name": "Alice"} res, err := cli.InsertOne(context.Background(), doc) ast.NoError(err) @@ -93,8 +94,8 @@ func TestCollection_InsertMany(t *testing.T) { var err error docs := []interface{}{ - bson.D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Alice"}}, - bson.D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Lucas"}}, + D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Alice"}}, + D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Lucas"}}, } res, err := cli.InsertMany(context.Background(), docs) ast.NoError(err) @@ -102,14 +103,14 @@ func TestCollection_InsertMany(t *testing.T) { ast.Equal(2, len(res.InsertedIDs)) docs2 := []interface{}{ - bson.D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Alice"}}, - bson.D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Lucas"}}, + D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Alice"}}, + D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Lucas"}}, } res, err = cli.InsertMany(context.Background(), docs2) ast.Equal(true, IsDup(err)) ast.Equal(0, len(res.InsertedIDs)) - docs4 := []bson.M{} + docs4 := []M{} res, err = cli.InsertMany(context.Background(), []interface{}{docs4}) ast.Error(err) ast.Empty(res) @@ -127,18 +128,18 @@ func TestCollection_Upsert(t *testing.T) { id1 := primitive.NewObjectID() id2 := primitive.NewObjectID() docs := []interface{}{ - bson.D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}}, - bson.D{{Key: "_id", Value: id2}, {Key: "name", Value: "Lucas"}}, + D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}}, + D{{Key: "_id", Value: id2}, {Key: "name", Value: "Lucas"}}, } _, _ = cli.InsertMany(context.Background(), docs) var err error // replace already exist - filter1 := bson.M{ + filter1 := M{ "name": "Alice", } - replacement1 := bson.M{ + replacement1 := M{ "name": "Alice1", "age": 18, } @@ -151,10 +152,10 @@ func TestCollection_Upsert(t *testing.T) { ast.Equal(nil, res.UpsertedID) // not exist - filter2 := bson.M{ + filter2 := M{ "name": "Lily", } - replacement2 := bson.M{ + replacement2 := M{ "name": "Lily", "age": 20, } @@ -167,7 +168,7 @@ func TestCollection_Upsert(t *testing.T) { ast.NotNil(res.UpsertedID) // filter is nil or wrong BSON Document format - replacement3 := bson.M{ + replacement3 := M{ "name": "Geek", "age": 21, } @@ -180,7 +181,7 @@ func TestCollection_Upsert(t *testing.T) { ast.Empty(res) // replacement is nil or wrong BSON Document format - filter4 := bson.M{ + filter4 := M{ "name": "Geek", } res, err = cli.Upsert(context.Background(), filter4, nil) @@ -204,18 +205,18 @@ func TestCollection_Update(t *testing.T) { id1 := primitive.NewObjectID() id2 := primitive.NewObjectID() docs := []interface{}{ - bson.D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}}, - bson.D{{Key: "_id", Value: id2}, {Key: "name", Value: "Lucas"}}, + D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}}, + D{{Key: "_id", Value: id2}, {Key: "name", Value: "Lucas"}}, } _, _ = cli.InsertMany(context.Background(), docs) var err error // update already exist record - filter1 := bson.M{ + filter1 := M{ "name": "Alice", } - update1 := bson.M{ - "$set": bson.M{ + update1 := M{ + "$set": M{ "name": "Alice1", "age": 18, }, @@ -224,20 +225,20 @@ func TestCollection_Update(t *testing.T) { ast.NoError(err) // error when not exist - filter2 := bson.M{ + filter2 := M{ "name": "Lily", } - update2 := bson.M{ - "$set": bson.M{ + update2 := M{ + "$set": M{ "name": "Lily", "age": 20, }, } err = cli.Update(context.Background(), filter2, update2) - ast.Equal(err, NoSuchRecordErr) + ast.Equal(err, ERR_NO_SUCH_RECORD) // filter is nil or wrong BSON Document format - update3 := bson.M{ + update3 := M{ "name": "Geek", "age": 21, } @@ -248,7 +249,7 @@ func TestCollection_Update(t *testing.T) { ast.Error(err) // update is nil or wrong BSON Document format - filter4 := bson.M{ + filter4 := M{ "name": "Geek", } err = cli.Update(context.Background(), filter4, nil) @@ -271,19 +272,19 @@ func TestCollection_UpdateAll(t *testing.T) { id2 := primitive.NewObjectID() id3 := primitive.NewObjectID() docs := []interface{}{ - bson.D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, - bson.D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, - bson.D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, + D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, + D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, + D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, } _, _ = cli.InsertMany(context.Background(), docs) var err error // update already exist record - filter1 := bson.M{ + filter1 := M{ "name": "Alice", } - update1 := bson.M{ - "$set": bson.M{ + update1 := M{ + "$set": M{ "age": 33, }, } @@ -296,11 +297,11 @@ func TestCollection_UpdateAll(t *testing.T) { ast.Equal(nil, res.UpsertedID) // if record is not exist,err is nil, MatchedCount in res is 0 - filter2 := bson.M{ + filter2 := M{ "name": "Lily", } - update2 := bson.M{ - "$set": bson.M{ + update2 := M{ + "$set": M{ "age": 22, }, } @@ -310,7 +311,7 @@ func TestCollection_UpdateAll(t *testing.T) { ast.Equal(int64(0), res.MatchedCount) // filter is nil or wrong BSON Document format - update3 := bson.M{ + update3 := M{ "name": "Geek", "age": 21, } @@ -323,7 +324,7 @@ func TestCollection_UpdateAll(t *testing.T) { ast.Nil(res) // update is nil or wrong BSON Document format - filter4 := bson.M{ + filter4 := M{ "name": "Geek", } res, err = cli.UpdateAll(context.Background(), filter4, nil) @@ -348,15 +349,15 @@ func TestCollection_Remove(t *testing.T) { id2 := primitive.NewObjectID() id3 := primitive.NewObjectID() docs := []interface{}{ - bson.D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, - bson.D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, - bson.D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, + D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, + D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, + D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, } _, _ = cli.InsertMany(context.Background(), docs) var err error // delete record: name = "Alice" , after that, expect one name = "Alice" record - filter1 := bson.M{ + filter1 := M{ "name": "Alice", } err = cli.Remove(context.Background(), filter1) @@ -367,14 +368,14 @@ func TestCollection_Remove(t *testing.T) { ast.Equal(int64(1), cnt) // delete not match record , report err - filter2 := bson.M{ + filter2 := M{ "name": "Lily", } err = cli.Remove(context.Background(), filter2) - ast.Equal(err, NoSuchRecordErr) + ast.Equal(err, ERR_NO_SUCH_RECORD) - // filter is bson.M{},delete one document - filter3 := bson.M{} + // filter is M{},delete one document + filter3 := M{} preCnt, err := cli.Find(context.Background(), filter3).Count() ast.Equal(int64(2), preCnt) @@ -406,16 +407,16 @@ func TestCollection_DeleteAll(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - bson.D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, - bson.D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, - bson.D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, - bson.D{{Key: "_id", Value: id4}, {Key: "name", Value: "Rocket"}, {Key: "age", Value: 23}}, + D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, + D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, + D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, + D{{Key: "_id", Value: id4}, {Key: "name", Value: "Rocket"}, {Key: "age", Value: 23}}, } _, _ = cli.InsertMany(context.Background(), docs) var err error // delete record: name = "Alice" ,after that, expect - record : name = "Alice" - filter1 := bson.M{ + filter1 := M{ "name": "Alice", } res, err := cli.DeleteAll(context.Background(), filter1) @@ -428,7 +429,7 @@ func TestCollection_DeleteAll(t *testing.T) { ast.Equal(int64(0), cnt) // delete with not match filter, DeletedCount in res is 0 - filter2 := bson.M{ + filter2 := M{ "name": "Lily", } res, err = cli.DeleteAll(context.Background(), filter2) @@ -436,8 +437,8 @@ func TestCollection_DeleteAll(t *testing.T) { ast.NotNil(res) ast.Equal(int64(0), res.DeletedCount) - // filter is bson.M{},delete all docs - filter3 := bson.M{} + // filter is M{},delete all docs + filter3 := M{} preCnt, err := cli.Find(context.Background(), filter3).Count() ast.NoError(err) ast.Equal(int64(2), preCnt) @@ -478,7 +479,7 @@ func TestCollection_DeleteAll(t *testing.T) { // defer wg.Done() // var err error // var res *mongo.InsertResult -// doc := bson.M{"_id": primitive.NewObjectID(), "name": "Alice_" + strconv.Itoa(i)} +// doc := M{"_id": primitive.NewObjectID(), "name": "Alice_" + strconv.Itoa(i)} // // res, err = cli.Insert(context.Background(), doc) // ast.NoError(err) @@ -488,7 +489,7 @@ func TestCollection_DeleteAll(t *testing.T) { // }(i) // } // wg.Wait() -// count, err := cli.Find(context.Background(), bson.M{}).Count() +// count, err := cli.Find(context.Background(), M{}).Count() // ast.Equal(nil, err) // ast.Equal(dataNum, int(count)) //} diff --git a/cursor.go b/cursor.go index 41f2762..aac6d0c 100644 --- a/cursor.go +++ b/cursor.go @@ -2,38 +2,62 @@ package qmgo import ( "context" - "go.mongodb.org/mongo-driver/mongo" ) -// Cursor +// Cursor struct define type Cursor struct { ctx context.Context cursor *mongo.Cursor + err error } // Next gets the next document for this cursor. It returns true if there were no errors and the cursor has not been // exhausted. func (c *Cursor) Next(result interface{}) bool { + if c.err != nil { + return false + } var err error - if c.cursor.Next(c.ctx) { err = c.cursor.Decode(result) if err == nil { return true } } - return false } +// All iterates the cursor and decodes each document into results. The results parameter must be a pointer to a slice. +// recommend to use All() in struct Query or Aggregate +func (c *Cursor) All(results interface{}) error { + if c.err != nil { + return c.err + } + return c.cursor.All(c.ctx, results) +} + +// ID returns the ID of this cursor, or 0 if the cursor has been closed or exhausted. +//func (c *Cursor) ID() int64 { +// if c.err != nil { +// return 0 +// } +// return c.cursor.ID() +//} + // Close closes this cursor. Next and TryNext must not be called after Close has been called. // When the cursor object is no longer in use, it should be actively closed func (c *Cursor) Close() error { + if c.err != nil { + return c.err + } return c.cursor.Close(c.ctx) } // Err return the last error of Cursor, if no error occurs, return nil func (c *Cursor) Err() error { + if c.err != nil { + return c.err + } return c.cursor.Err() } diff --git a/cursor_test.go b/cursor_test.go index 1e24b6f..095bd90 100644 --- a/cursor_test.go +++ b/cursor_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -23,26 +22,25 @@ func TestCursor(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - bson.M{"_id": id1, "name": "Alice", "age": 18}, - bson.M{"_id": id2, "name": "Alice", "age": 19}, - bson.M{"_id": id3, "name": "Lucas", "age": 20}, - bson.M{"_id": id4, "name": "Lucas", "age": 21}, + M{"_id": id1, "name": "Alice", "age": 18}, + M{"_id": id2, "name": "Alice", "age": 19}, + M{"_id": id3, "name": "Lucas", "age": 20}, + M{"_id": id4, "name": "Lucas", "age": 21}, } - _, _ = cli.InsertMany(context.Background(), docs) + cli.InsertMany(context.Background(), docs) - var err error var res QueryTestItem // if query has 1 record,cursor can run Next one time, Next time return false - filter1 := bson.M{ + filter1 := M{ "name": "Alice", } - projection1 := bson.M{ + projection1 := M{ "name": 0, } - cursor, err := cli.Find(context.Background(), filter1).Select(projection1).Sort("age").Limit(2).Skip(1).Cursor() - ast.NoError(err) + cursor := cli.Find(context.Background(), filter1).Select(projection1).Sort("age").Limit(2).Skip(1).Cursor() + ast.NoError(cursor.Err()) val := cursor.Next(&res) ast.Equal(true, val) @@ -53,13 +51,20 @@ func TestCursor(t *testing.T) { cursor.Close() + // cursor ALL + cursor = cli.Find(context.Background(), filter1).Select(projection1).Sort("age").Limit(2).Cursor() + ast.NoError(cursor.Err()) + + var results []QueryTestItem + cursor.All(&results) + ast.Equal(2, len(results)) // can't match record, cursor run Next and return false - filter2 := bson.M{ + filter2 := M{ "name": "Lily", } - cursor, err = cli.Find(context.Background(), filter2).Cursor() - ast.NoError(err) + cursor = cli.Find(context.Background(), filter2).Cursor() + ast.NoError(cursor.Err()) ast.NotNil(cursor) res = QueryTestItem{} @@ -70,12 +75,20 @@ func TestCursor(t *testing.T) { cursor.Close() // 1 record,after cursor close,Next return false - cursor, err = cli.Find(context.Background(), filter1).Select(projection1).Sort("age").Limit(2).Skip(1).Cursor() - ast.NoError(err) + cursor = cli.Find(context.Background(), filter1).Select(projection1).Sort("age").Limit(2).Skip(1).Cursor() + ast.NoError(cursor.Err()) ast.NotNil(cursor) cursor.Close() ast.Equal(false, cursor.Next(&res)) ast.NoError(cursor.Err()) + + // generate Cursor with err + cursor = cli.Find(context.Background(), 1).Select(projection1).Sort("age").Limit(2).Skip(1).Cursor() + ast.Error(cursor.Err()) + //ast.Equal(int64(0), cursor.ID()) + ast.Error(cursor.All(&res)) + ast.Error(cursor.Close()) + ast.Equal(false, cursor.Next(&res)) } diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..1c372ec --- /dev/null +++ b/errors.go @@ -0,0 +1,11 @@ +package qmgo + +import "errors" + +var ( + ERR_QUERY_NOT_SLICE_POINTER = errors.New("result argument must be a pointer to a slice") + ERR_QUERY_NOT_SLICE_TYPE = errors.New("result argument must be a slice address") + ERR_QUERY_RESULT_TYPE_INCONSISTEN = errors.New("result type is not equal mongodb value type") + ERR_QUERY_RESULT_VAL_CAN_NOT_CHANGE = errors.New("the value of result can not be changed") + ERR_NO_SUCH_RECORD = errors.New("no such record") +) diff --git a/compare_test.go b/example/example_test.go similarity index 56% rename from compare_test.go rename to example/example_test.go index e294d3f..2894ab6 100644 --- a/compare_test.go +++ b/example/example_test.go @@ -1,19 +1,17 @@ -package qmgo +package example import ( "context" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" - "reflect" + "errors" "testing" + "github.com/qiniu/qmgo" "github.com/stretchr/testify/require" - "labix.org/v2/mgo" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" ) const ( - MGO_URI = "localhost:27017" URI = "mongodb://localhost:27017" DATABASE = "class" COLL = "user" @@ -34,47 +32,20 @@ var oneUserInfo = UserInfo{ } var batchUserInfo = []UserInfo{ - {Name: "wxy", Age: 6, Weight: 20}, - {Name: "jZ", Age: 6, Weight: 25}, - {Name: "zp", Age: 6, Weight: 30}, - {Name: "yxw", Age: 6, Weight: 35}, + {Name: "a1", Age: 6, Weight: 20}, + {Name: "b2", Age: 6, Weight: 25}, + {Name: "c3", Age: 6, Weight: 30}, + {Name: "d4", Age: 6, Weight: 35}, + {Name: "a1", Age: 7, Weight: 40}, + {Name: "a1", Age: 8, Weight: 45}, } var batchUserInfoI = []interface{}{ - UserInfo{Name: "wxy", Age: 6, Weight: 20}, - UserInfo{Name: "jZ", Age: 6, Weight: 25}, - UserInfo{Name: "zp", Age: 6, Weight: 30}, - UserInfo{Name: "yxw", Age: 6, Weight: 35}, -} - -func TestMgo(t *testing.T) { - ast := require.New(t) - // create connection - session, err := mgo.Dial(MGO_URI) - ast.Nil(err) - db := session.DB(DATABASE) - defer db.DropDatabase() - coll := db.C(COLL) - - // insert one document - err = coll.Insert(oneUserInfo) - ast.Nil(err) - - // find one document - one := UserInfo{} - coll.Find(BsonT{"name": oneUserInfo.Name}).One(&one) - ast.Nil(err) - ast.Equal(oneUserInfo, one) - - // batch insert - for _, v := range batchUserInfo { - err = coll.Insert(v) - ast.Nil(err) - } - batch := []UserInfo{} - - // find all 、sort and limit - coll.Find(BsonT{"age": 6}).Sort("weight").Limit(7).All(&batch) - ast.Equal(true, reflect.DeepEqual(batchUserInfo, batch)) + UserInfo{Name: "a1", Age: 6, Weight: 20}, + UserInfo{Name: "b2", Age: 6, Weight: 25}, + UserInfo{Name: "c3", Age: 6, Weight: 30}, + UserInfo{Name: "d4", Age: 6, Weight: 35}, + UserInfo{Name: "a1", Age: 7, Weight: 40}, + UserInfo{Name: "a1", Age: 8, Weight: 45}, } func TestQmgo(t *testing.T) { @@ -82,7 +53,7 @@ func TestQmgo(t *testing.T) { ctx := context.Background() // create connect - cli, err := Open(ctx, &Config{Uri: URI, Database: DATABASE, Coll: COLL}) + cli, err := qmgo.Open(ctx, &qmgo.Config{Uri: URI, Database: DATABASE, Coll: COLL}) ast.Nil(err) defer func() { @@ -92,7 +63,7 @@ func TestQmgo(t *testing.T) { }() defer cli.DropDatabase(ctx) - cli.EnsureIndexes(ctx, []string{"name"}, []string{"age", "name,weight"}) + cli.EnsureIndexes(ctx, []string{"_id"}, []string{"age", "name,weight"}) // insert one document _, err = cli.InsertOne(ctx, oneUserInfo) ast.Nil(err) @@ -103,15 +74,36 @@ func TestQmgo(t *testing.T) { ast.Nil(err) ast.Equal(oneUserInfo, one) - // batch insert + // multiple insert _, err = cli.Collection.InsertMany(ctx, batchUserInfoI) ast.Nil(err) // find all 、sort and limit batch := []UserInfo{} cli.Find(ctx, BsonT{"age": 6}).Sort("weight").Limit(7).All(&batch) - ast.Equal(true, reflect.DeepEqual(batchUserInfo, batch)) - + ast.Equal(4, len(batch)) + + count, err := cli.Find(ctx, BsonT{"age": 6}).Count() + ast.NoError(err) + ast.Equal(int64(4), count) + + // aggregate + matchStage := qmgo.D{{"$match", []qmgo.E{{"weight", qmgo.D{{"$gt", 30}}}}}} + groupStage := qmgo.D{{"$group", qmgo.D{{"_id", "$name"}, {"total", qmgo.D{{"$sum", "$age"}}}}}} + var showsWithInfo []qmgo.M + err = cli.Aggregate(context.Background(), qmgo.Pipeline{matchStage, groupStage}).All(&showsWithInfo) + ast.Equal(3, len(showsWithInfo)) + for _, v := range showsWithInfo { + if "a1" == v["_id"] { + ast.Equal(int32(15), v["total"]) + continue + } + if "d4" == v["_id"] { + ast.Equal(int32(6), v["total"]) + continue + } + ast.Error(errors.New("error"), "impossible") + } //remove err = cli.Remove(ctx, BsonT{"age": 7}) ast.Nil(err) @@ -147,14 +139,11 @@ func TestOfficialMongoDriver(t *testing.T) { // find all 、sort and limit findOptions := options.Find() findOptions.SetLimit(7) - var sorts bson.D - sorts = append(sorts, bson.E{Key: "weight", Value: 1}) + var sorts qmgo.D + sorts = append(sorts, qmgo.E{Key: "weight", Value: 1}) findOptions.SetSort(sorts) - batch := []UserInfo{} - cur, err := coll.Find(ctx, BsonT{"age": 6}, findOptions) + _, err = coll.Find(ctx, BsonT{"age": 6}, findOptions) ast.Nil(err) - cur.All(ctx, &batch) - ast.Equal(true, reflect.DeepEqual(batchUserInfo, batch)) } diff --git a/go.mod b/go.mod index 8c55576..1c5e29c 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,6 @@ module github.com/qiniu/qmgo go 1.14 require ( - github.com/stretchr/testify v1.3.0 - go.mongodb.org/mongo-driver v1.3.5 - golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d - labix.org/v2/mgo v0.0.0-20140701140051-000000000287 + github.com/stretchr/testify v1.4.0 + go.mongodb.org/mongo-driver v1.4.0 ) diff --git a/go.sum b/go.sum index f83e073..bd3b5fb 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,10 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/aws/aws-sdk-go v1.29.15 h1:0ms/213murpsujhsnxnNKNeVouW60aJqSd992Ks3mxs= +github.com/aws/aws-sdk-go v1.29.15/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg= 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/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= @@ -30,8 +33,11 @@ github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/V github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= 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.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= @@ -39,8 +45,10 @@ github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzV github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +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/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/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= @@ -49,6 +57,8 @@ github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUr github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -64,13 +74,16 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +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/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo= github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -go.mongodb.org/mongo-driver v1.3.5 h1:S0ZOruh4YGHjD7JoN7mIsTrNjnQbOjrmgrx6l6pZN7I= -go.mongodb.org/mongo-driver v1.3.5/go.mod h1:Ual6Gkco7ZGQw8wE1t4tLnvBsf6yVSM60qW6TgOeJ5c= +go.mongodb.org/mongo-driver v1.4.0 h1:C8rFn1VF4GVEM/rG+dSoMmlm2pyQ9cs2/oRtUATejRU= +go.mongodb.org/mongo-driver v1.4.0/go.mod h1:llVBH2pkj9HywK0Dtdt6lDikOjFLbceHVu/Rc0iMKLs= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= @@ -78,10 +91,14 @@ golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkpre golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -98,8 +115,8 @@ golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -labix.org/v2/mgo v0.0.0-20140701140051-000000000287 h1:L0cnkNl4TfAXzvdrqsYEmxOHOCv2p5I3taaReO8BWFs= -labix.org/v2/mgo v0.0.0-20140701140051-000000000287/go.mod h1:Lg7AYkt1uXJoR9oeSZ3W/8IXLdvOfIITgZnommstyz4= diff --git a/interface.go b/interface.go index d8299e8..276e788 100644 --- a/interface.go +++ b/interface.go @@ -20,6 +20,8 @@ type CursorI interface { Next(result interface{}) bool Close() error Err() error + All(reuslts interface{}) error + //ID() int64 } // QueryI Query interface @@ -32,5 +34,11 @@ type QueryI interface { All(result interface{}) error Count() (n int64, err error) Distinct(key string, result interface{}) error - Cursor() (CursorI, error) + Cursor() CursorI +} + +type AggregateI interface { + All(results interface{}) error + One(result interface{}) error + Iter() CursorI } diff --git a/query.go b/query.go index 7382d0e..76517b4 100644 --- a/query.go +++ b/query.go @@ -2,22 +2,13 @@ package qmgo import ( "context" - "errors" "fmt" "reflect" - "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) -var ( - ERR_QUERY_NOT_SLICE_POINTER = errors.New("result argument must be a pointer to a slice") - ERR_QUERY_NOT_SLICE_TYPE = errors.New("result argument must be a slice address") - ERR_QUERY_RESULT_TYPE_INCONSISTEN = errors.New("result type is not equal mongodb value type") - ERR_QUERY_RESULT_VAL_CAN_NOT_CHANGE = errors.New("the value of result can not be changed") -) - // Query type Query struct { ctx context.Context @@ -34,13 +25,13 @@ type Query struct { // When multiple sort fields are passed in at the same time, they are arranged in the order in which the fields are passed in. // For example, {"age", "-name"}, first sort by age in ascending order, then sort by name in descending order func (q *Query) Sort(fields ...string) QueryI { - var sorts bson.D + var sorts D for _, field := range fields { key, n := SplitSortField(field) if key == "" { panic("Sort: empty field name") } - sorts = append(sorts, bson.E{Key: key, Value: n}) + sorts = append(sorts, E{Key: key, Value: n}) } return &Query{ @@ -55,8 +46,8 @@ func (q *Query) Sort(fields ...string) QueryI { } // Select is used to determine which fields are displayed or not displayed in the returned results -// Format: bson.M{"age": 1} means that only the age field is displayed -// bson.M{"age": 0} means to display other fields except age +// Format: M{"age": 1} means that only the age field is displayed +// M{"age": 0} means to display other fields except age // When _id is not displayed and is set to 0, it will be returned to display func (q *Query) Select(projection interface{}) QueryI { return &Query{ @@ -145,15 +136,12 @@ func (q *Query) All(result interface{}) error { cursor, err = q.collection.Find(q.ctx, q.filter, opt) - if err != nil { - return err - } - if err_ := cursor.Err(); err_ != nil { - return err_ + c := Cursor{ + ctx: q.ctx, + cursor: cursor, + err: err, } - - err = cursor.All(q.ctx, result) - return err + return c.All(result) } // Count count the number of eligible entries @@ -219,7 +207,7 @@ func (q *Query) Distinct(key string, result interface{}) error { // Cursor gets a Cursor object, which can be used to traverse the query result set // After obtaining the CursorI object, you should actively call the Close interface to close the cursor -func (q *Query) Cursor() (CursorI, error) { +func (q *Query) Cursor() CursorI { opt := options.Find() if q.sort != nil { @@ -238,12 +226,9 @@ func (q *Query) Cursor() (CursorI, error) { var err error var cur *mongo.Cursor cur, err = q.collection.Find(q.ctx, q.filter, opt) - if err != nil { - return nil, err - } - return &Cursor{ ctx: q.ctx, cursor: cur, - }, nil + err: err, + } } diff --git a/query_test.go b/query_test.go index 250fc6f..3edd921 100644 --- a/query_test.go +++ b/query_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -32,19 +31,19 @@ func TestQuery_One(t *testing.T) { id2 := primitive.NewObjectID() id3 := primitive.NewObjectID() docs := []interface{}{ - bson.D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, - bson.D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, - bson.D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, + D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, + D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, + D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, } _, _ = cli.InsertMany(context.Background(), docs) var err error var res QueryTestItem - filter1 := bson.M{ + filter1 := M{ "name": "Alice", } - projection1 := bson.M{ + projection1 := M{ "age": 0, } @@ -54,7 +53,7 @@ func TestQuery_One(t *testing.T) { ast.Equal("Alice", res.Name) res = QueryTestItem{} - filter2 := bson.M{ + filter2 := M{ "name": "Lily", } @@ -62,9 +61,9 @@ func TestQuery_One(t *testing.T) { ast.Error(err) ast.Empty(res) - // filter is bson.M{},match all and return one + // filter is M{},match all and return one res = QueryTestItem{} - filter3 := bson.M{} + filter3 := M{} err = cli.Find(context.Background(), filter3).One(&res) ast.NoError(err) @@ -104,20 +103,20 @@ func TestQuery_All(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - bson.M{"_id": id1, "name": "Alice", "age": 18}, - bson.M{"_id": id2, "name": "Alice", "age": 19}, - bson.M{"_id": id3, "name": "Lucas", "age": 20}, - bson.M{"_id": id4, "name": "Lucas", "age": 21}, + M{"_id": id1, "name": "Alice", "age": 18}, + M{"_id": id2, "name": "Alice", "age": 19}, + M{"_id": id3, "name": "Lucas", "age": 20}, + M{"_id": id4, "name": "Lucas", "age": 21}, } _, _ = cli.InsertMany(context.Background(), docs) var err error var res []QueryTestItem - filter1 := bson.M{ + filter1 := M{ "name": "Alice", } - projection1 := bson.M{ + projection1 := M{ "name": 0, } @@ -126,7 +125,7 @@ func TestQuery_All(t *testing.T) { ast.Equal(1, len(res)) res = make([]QueryTestItem, 0) - filter2 := bson.M{ + filter2 := M{ "name": "Lily", } @@ -134,9 +133,9 @@ func TestQuery_All(t *testing.T) { ast.NoError(err) ast.Empty(res) - // filter is bson.M{}, which means to match all, will return all records in the collection + // filter is M{}, which means to match all, will return all records in the collection res = make([]QueryTestItem, 0) - filter3 := bson.M{} + filter3 := M{} err = cli.Find(context.Background(), filter3).All(&res) ast.NoError(err) @@ -178,17 +177,17 @@ func TestQuery_Count(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - bson.M{"_id": id1, "name": "Alice", "age": 18}, - bson.M{"_id": id2, "name": "Alice", "age": 19}, - bson.M{"_id": id3, "name": "Lucas", "age": 20}, - bson.M{"_id": id4, "name": "Lucas", "age": 21}, + M{"_id": id1, "name": "Alice", "age": 18}, + M{"_id": id2, "name": "Alice", "age": 19}, + M{"_id": id3, "name": "Lucas", "age": 20}, + M{"_id": id4, "name": "Lucas", "age": 21}, } _, _ = cli.InsertMany(context.Background(), docs) var err error var cnt int64 - filter1 := bson.M{ + filter1 := M{ "name": "Alice", } @@ -196,7 +195,7 @@ func TestQuery_Count(t *testing.T) { ast.NoError(err) ast.Equal(int64(1), cnt) - filter2 := bson.M{ + filter2 := M{ "name": "Lily", } @@ -204,7 +203,7 @@ func TestQuery_Count(t *testing.T) { ast.NoError(err) ast.Zero(cnt) - filter3 := bson.M{} + filter3 := M{} cnt, err = cli.Find(context.Background(), filter3).Count() ast.NoError(err) @@ -230,10 +229,10 @@ func TestQuery_Skip(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - bson.M{"_id": id1, "name": "Alice", "age": 18}, - bson.M{"_id": id2, "name": "Alice", "age": 19}, - bson.M{"_id": id3, "name": "Lucas", "age": 20}, - bson.M{"_id": id4, "name": "Lucas", "age": 21}, + M{"_id": id1, "name": "Alice", "age": 18}, + M{"_id": id2, "name": "Alice", "age": 19}, + M{"_id": id3, "name": "Lucas", "age": 20}, + M{"_id": id4, "name": "Lucas", "age": 21}, } _, _ = cli.InsertMany(context.Background(), docs) @@ -241,7 +240,7 @@ func TestQuery_Skip(t *testing.T) { var res []QueryTestItem // filter can match records, skip 1 record, and return the remaining records - filter1 := bson.M{ + filter1 := M{ "name": "Alice", } @@ -278,17 +277,17 @@ func TestQuery_Limit(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - bson.M{"_id": id1, "name": "Alice", "age": 18}, - bson.M{"_id": id2, "name": "Alice", "age": 19}, - bson.M{"_id": id3, "name": "Lucas", "age": 20}, - bson.M{"_id": id4, "name": "Lucas", "age": 21}, + M{"_id": id1, "name": "Alice", "age": 18}, + M{"_id": id2, "name": "Alice", "age": 19}, + M{"_id": id3, "name": "Lucas", "age": 20}, + M{"_id": id4, "name": "Lucas", "age": 21}, } _, _ = cli.InsertMany(context.Background(), docs) var err error var res []QueryTestItem - filter1 := bson.M{ + filter1 := M{ "name": "Alice", } @@ -305,8 +304,8 @@ func TestQuery_Limit(t *testing.T) { res = make([]QueryTestItem, 0) var cursor CursorI - cursor, err = cli.Find(context.Background(), filter1).Limit(-2).Cursor() - ast.NoError(err) + cursor = cli.Find(context.Background(), filter1).Limit(-2).Cursor() + ast.NoError(cursor.Err()) ast.NotNil(cursor) } @@ -325,10 +324,10 @@ func TestQuery_Sort(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - bson.M{"_id": id1, "name": "Alice", "age": 18}, - bson.M{"_id": id2, "name": "Alice", "age": 19}, - bson.M{"_id": id3, "name": "Lucas", "age": 18}, - bson.M{"_id": id4, "name": "Lucas", "age": 19}, + M{"_id": id1, "name": "Alice", "age": 18}, + M{"_id": id2, "name": "Alice", "age": 19}, + M{"_id": id3, "name": "Lucas", "age": 18}, + M{"_id": id4, "name": "Lucas", "age": 19}, } _, _ = cli.InsertMany(context.Background(), docs) @@ -336,7 +335,7 @@ func TestQuery_Sort(t *testing.T) { var res []QueryTestItem // Sort a single field in ascending order - filter1 := bson.M{ + filter1 := M{ "name": "Alice", } @@ -354,7 +353,7 @@ func TestQuery_Sort(t *testing.T) { ast.Equal(id1, res[1].Id) // Sort a single field in descending order, and sort the other field in ascending order - err = cli.Find(context.Background(), bson.M{}).Sort("-age", "+name").All(&res) + err = cli.Find(context.Background(), M{}).Sort("-age", "+name").All(&res) ast.NoError(err) ast.Equal(4, len(res)) ast.Equal(id2, res[0].Id) @@ -384,16 +383,16 @@ func TestQuery_Distinct(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - bson.M{"_id": id1, "name": "Alice", "age": 18}, - bson.M{"_id": id2, "name": "Alice", "age": 19}, - bson.M{"_id": id3, "name": "Lucas", "age": 20}, - bson.M{"_id": id4, "name": "Lucas", "age": 21}, + M{"_id": id1, "name": "Alice", "age": 18}, + M{"_id": id2, "name": "Alice", "age": 19}, + M{"_id": id3, "name": "Lucas", "age": 20}, + M{"_id": id4, "name": "Lucas", "age": 21}, } _, _ = cli.InsertMany(context.Background(), docs) var err error - filter1 := bson.M{ + filter1 := M{ "name": "Lily", } var res1 []int32 @@ -402,7 +401,7 @@ func TestQuery_Distinct(t *testing.T) { ast.NoError(err) ast.Equal(0, len(res1)) - filter2 := bson.M{ + filter2 := M{ "name": "Alice", } var res2 []int32 @@ -466,20 +465,20 @@ func TestQuery_Select(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - bson.M{"_id": id1, "name": "Alice", "age": 18}, - bson.M{"_id": id2, "name": "Alice", "age": 19}, - bson.M{"_id": id3, "name": "Lucas", "age": 20}, - bson.M{"_id": id4, "name": "Lucas", "age": 21}, + M{"_id": id1, "name": "Alice", "age": 18}, + M{"_id": id2, "name": "Alice", "age": 19}, + M{"_id": id3, "name": "Lucas", "age": 20}, + M{"_id": id4, "name": "Lucas", "age": 21}, } _, _ = cli.InsertMany(context.Background(), docs) var err error var res QueryTestItem - filter1 := bson.M{ + filter1 := M{ "_id": id1, } - projection1 := bson.M{ + projection1 := M{ "age": 1, } @@ -491,7 +490,7 @@ func TestQuery_Select(t *testing.T) { ast.Equal(id1, res.Id) res = QueryTestItem{} - projection2 := bson.M{ + projection2 := M{ "age": 0, } @@ -503,7 +502,7 @@ func TestQuery_Select(t *testing.T) { ast.Equal(id1, res.Id) res = QueryTestItem{} - projection3 := bson.M{ + projection3 := M{ "_id": 0, } @@ -530,25 +529,24 @@ func TestQuery_Cursor(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - bson.D{{"_id", id1}, {"name", "Alice"}, {"age", 18}}, - bson.D{{"_id", id2}, {"name", "Alice"}, {"age", 19}}, - bson.D{{"_id", id3}, {"name", "Lucas"}, {"age", 20}}, - bson.D{{"_id", id4}, {"name", "Lucas"}, {"age", 21}}, + D{{"_id", id1}, {"name", "Alice"}, {"age", 18}}, + D{{"_id", id2}, {"name", "Alice"}, {"age", 19}}, + D{{"_id", id3}, {"name", "Lucas"}, {"age", 20}}, + D{{"_id", id4}, {"name", "Lucas"}, {"age", 21}}, } _, _ = cli.InsertMany(context.Background(), docs) - var err error var res QueryTestItem - filter1 := bson.M{ + filter1 := M{ "name": "Alice", } - projection1 := bson.M{ + projection1 := M{ "name": 0, } - cursor, err := cli.Find(context.Background(), filter1).Select(projection1).Sort("age").Limit(2).Skip(1).Cursor() - ast.NoError(err) + cursor := cli.Find(context.Background(), filter1).Select(projection1).Sort("age").Limit(2).Skip(1).Cursor() + ast.NoError(cursor.Err()) ast.NotNil(cursor) val := cursor.Next(&res) @@ -558,12 +556,12 @@ func TestQuery_Cursor(t *testing.T) { val = cursor.Next(&res) ast.Equal(false, val) - filter2 := bson.M{ + filter2 := M{ "name": "Lily", } - cursor, err = cli.Find(context.Background(), filter2).Cursor() - ast.NoError(err) + cursor = cli.Find(context.Background(), filter2).Cursor() + ast.NoError(cursor.Err()) ast.NotNil(cursor) res = QueryTestItem{} @@ -573,7 +571,6 @@ func TestQuery_Cursor(t *testing.T) { filter3 := 1 - cursor, err = cli.Find(context.Background(), filter3).Cursor() - ast.Error(err) - ast.Nil(cursor) + cursor = cli.Find(context.Background(), filter3).Cursor() + ast.Error(cursor.Err()) } From 0b177a4350e55ed5398841bc14eb57ef4a72cd74 Mon Sep 17 00:00:00 2001 From: jiangzhi Date: Fri, 7 Aug 2020 16:11:22 +0800 Subject: [PATCH 2/5] support aggregate --- aggregate.go | 7 +- aggregate_test.go | 13 ++- base.go | 27 ----- client_test.go | 7 +- collection.go | 4 +- collection_test.go | 112 +++++++++--------- cursor_test.go | 15 +-- example/example_test.go => example_test.go | 11 +- query.go | 9 +- query_test.go | 129 ++++++++++----------- 10 files changed, 159 insertions(+), 175 deletions(-) rename example/example_test.go => example_test.go (91%) diff --git a/aggregate.go b/aggregate.go index e8a4251..5a23133 100644 --- a/aggregate.go +++ b/aggregate.go @@ -2,10 +2,12 @@ package qmgo import ( "context" + + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" ) -type Pipeline []D +type Pipeline []bson.D type Aggregate struct { ctx context.Context @@ -13,6 +15,7 @@ type Aggregate struct { collection *mongo.Collection } +// All iterates the cursor from aggregate and decodes each document into results. func (a *Aggregate) All(results interface{}) error { c, err := a.collection.Aggregate(a.ctx, a.pipeline) if err != nil { @@ -21,6 +24,7 @@ func (a *Aggregate) All(results interface{}) error { return c.All(a.ctx, results) } +// One iterates the cursor from aggregate and decodes current document into result. func (a *Aggregate) One(result interface{}) error { c, err := a.collection.Aggregate(a.ctx, a.pipeline) if err != nil { @@ -38,6 +42,7 @@ func (a *Aggregate) One(result interface{}) error { return err } +// Iter return the cursor after aggregate func (a *Aggregate) Iter() CursorI { c, err := a.collection.Aggregate(a.ctx, a.pipeline) return &Cursor{ diff --git a/aggregate_test.go b/aggregate_test.go index 0525ad6..8207ebc 100644 --- a/aggregate_test.go +++ b/aggregate_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -31,9 +32,9 @@ func TestAggregate(t *testing.T) { QueryTestItem{Id: id5, Name: "Lucas", Age: 44}, } cli.InsertMany(context.Background(), docs) - matchStage := D{{"$match", []E{{"age", D{{"$gt", 11}}}}}} - groupStage := D{{"$group", D{{"_id", "$name"}, {"total", D{{"$sum", "$age"}}}}}} - var showsWithInfo []M + matchStage := bson.D{{"$match", []bson.E{{"age", bson.D{{"$gt", 11}}}}}} + groupStage := bson.D{{"$group", bson.D{{"_id", "$name"}, {"total", bson.D{{"$sum", "$age"}}}}}} + var showsWithInfo []bson.M // aggregate ALL() err := cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}).All(&showsWithInfo) ast.NoError(err) @@ -66,7 +67,7 @@ func TestAggregate(t *testing.T) { ast.Error(errors.New("error"), "impossible") } // One() - var oneInfo M + var oneInfo bson.M iter = cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}) ast.NotNil(iter) @@ -94,8 +95,8 @@ func TestAggregate(t *testing.T) { ast.Error(cli.Aggregate(context.Background(), 1).All(&showsWithInfo)) ast.Error(cli.Aggregate(context.Background(), 1).One(&showsWithInfo)) ast.Error(cli.Aggregate(context.Background(), 1).Iter().Err()) - matchStage = D{{"$match", []E{{"age", D{{"$gt", 100}}}}}} - groupStage = D{{"$group", D{{"_id", "$name"}, {"total", D{{"$sum", "$age"}}}}}} + matchStage = bson.D{{"$match", []bson.E{{"age", bson.D{{"$gt", 100}}}}}} + groupStage = bson.D{{"$group", bson.D{{"_id", "$name"}, {"total", bson.D{{"$sum", "$age"}}}}}} ast.Error(cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}).One(&showsWithInfo)) } diff --git a/base.go b/base.go index bca8a40..6655998 100644 --- a/base.go +++ b/base.go @@ -4,33 +4,6 @@ import ( "strings" ) -// D is an ordered representation of a BSON document. This type should be used when the order of the elements matters, -// such as MongoDB command documents. If the order of the elements does not matter, an M should be used instead. -// -// Example usage: -// D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}} -type D []E - -// E represents a BSON element for a D. It is usually used inside a D. -type E struct { - Key string - Value interface{} -} - -// M is an unordered representation of a BSON document. This type should be used when the order of the elements does not -// matter. This type is handled as a regular map[string]interface{} when encoding and decoding. Elements will be -// serialized in an undefined, random order. If the order of the elements matters, a D should be used instead. -// -// Example usage: -// -// M{"foo": "bar", "hello": "world", "pi": 3.14159}. -type M map[string]interface{} - -// An A is an ordered representation of a BSON array. -// Example usage: -// A{"bar", "world", 3.14159, D{{"qux", 12345}}} -type A []interface{} - // QmgoConfig for initial mongodb instance type Config struct { // URI example: [mongodb://][user:pass@]host1[:port1][,host2[:port2],...][/database][?options] diff --git a/client_test.go b/client_test.go index fd4d6f2..f2a3af9 100644 --- a/client_test.go +++ b/client_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "go.mongodb.org/mongo-driver/bson" ) func initClient(col string) *QmgoClient { @@ -63,7 +64,7 @@ func TestQmgoClient(t *testing.T) { err = cli.Ping(5) ast.NoError(err) - res, err := cli.InsertOne(context.Background(), D{{Key: "x", Value: 1}}) + res, err := cli.InsertOne(context.Background(), bson.D{{Key: "x", Value: 1}}) ast.NoError(err) ast.NotNil(res) @@ -71,7 +72,7 @@ func TestQmgoClient(t *testing.T) { // close Client cli.Close(context.TODO()) - _, err = cli.InsertOne(context.Background(), D{{Key: "x", Value: 1}}) + _, err = cli.InsertOne(context.Background(), bson.D{{Key: "x", Value: 1}}) ast.EqualError(err, "client is disconnected") err = cli.Ping(5) @@ -101,7 +102,7 @@ func TestClient(t *testing.T) { ast.Equal(nil, err) coll := c.Database("mongoxtest").Collection("testopen") - res, err := coll.InsertOne(context.Background(), D{{Key: "x", Value: 1}}) + res, err := coll.InsertOne(context.Background(), bson.D{{Key: "x", Value: 1}}) ast.NoError(err) ast.NotNil(res) coll.DropCollection(context.Background()) diff --git a/collection.go b/collection.go index c1bf672..0ea64b1 100644 --- a/collection.go +++ b/collection.go @@ -87,7 +87,7 @@ func (c *Collection) UpdateAll(ctx context.Context, filter interface{}, update i } // Remove executes a delete command to delete at most one document from the collection. -// if filter is M{},DeleteOne will delete one document in collection +// if filter is bson.M{},DeleteOne will delete one document in collection // Reference: https://docs.mongodb.com/manual/reference/command/delete/ func (c *Collection) Remove(ctx context.Context, filter interface{}) (err error) { @@ -103,7 +103,7 @@ func (c *Collection) Remove(ctx context.Context, filter interface{}) (err error) } // DeleteAll executes a delete command to delete documents from the collection. -// If filter is M{},all ducuments in Collection will be deleted +// If filter is bson.M{},all ducuments in Collection will be deleted // Reference: https://docs.mongodb.com/manual/reference/command/delete/ func (c *Collection) DeleteAll(ctx context.Context, filter interface{}) (result *DeleteResult, err error) { diff --git a/collection_test.go b/collection_test.go index 4edd9f8..4aae78e 100644 --- a/collection_test.go +++ b/collection_test.go @@ -2,9 +2,11 @@ package qmgo import ( "context" + "testing" + "github.com/stretchr/testify/require" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" - "testing" ) func TestCollection_EnsureIndex(t *testing.T) { @@ -23,7 +25,7 @@ func TestCollection_EnsureIndex(t *testing.T) { // check if unique indexs is working var err error - doc := M{ + doc := bson.M{ "id1": 1, } _, err = cli.InsertOne(context.Background(), doc) @@ -50,7 +52,7 @@ func TestCollection_EnsureIndexes(t *testing.T) { // check if unique indexs is working var err error - doc := M{ + doc := bson.M{ "id1": 1, } @@ -70,7 +72,7 @@ func TestCollection_Insert(t *testing.T) { cli.EnsureIndexes(context.Background(), []string{"name"}, nil) var err error - doc := M{"_id": primitive.NewObjectID(), "name": "Alice"} + doc := bson.M{"_id": primitive.NewObjectID(), "name": "Alice"} res, err := cli.InsertOne(context.Background(), doc) ast.NoError(err) @@ -94,8 +96,8 @@ func TestCollection_InsertMany(t *testing.T) { var err error docs := []interface{}{ - D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Alice"}}, - D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Lucas"}}, + bson.D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Alice"}}, + bson.D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Lucas"}}, } res, err := cli.InsertMany(context.Background(), docs) ast.NoError(err) @@ -103,14 +105,14 @@ func TestCollection_InsertMany(t *testing.T) { ast.Equal(2, len(res.InsertedIDs)) docs2 := []interface{}{ - D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Alice"}}, - D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Lucas"}}, + bson.D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Alice"}}, + bson.D{{Key: "_id", Value: primitive.NewObjectID()}, {Key: "name", Value: "Lucas"}}, } res, err = cli.InsertMany(context.Background(), docs2) ast.Equal(true, IsDup(err)) ast.Equal(0, len(res.InsertedIDs)) - docs4 := []M{} + docs4 := []bson.M{} res, err = cli.InsertMany(context.Background(), []interface{}{docs4}) ast.Error(err) ast.Empty(res) @@ -128,18 +130,18 @@ func TestCollection_Upsert(t *testing.T) { id1 := primitive.NewObjectID() id2 := primitive.NewObjectID() docs := []interface{}{ - D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}}, - D{{Key: "_id", Value: id2}, {Key: "name", Value: "Lucas"}}, + bson.D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}}, + bson.D{{Key: "_id", Value: id2}, {Key: "name", Value: "Lucas"}}, } _, _ = cli.InsertMany(context.Background(), docs) var err error // replace already exist - filter1 := M{ + filter1 := bson.M{ "name": "Alice", } - replacement1 := M{ + replacement1 := bson.M{ "name": "Alice1", "age": 18, } @@ -152,10 +154,10 @@ func TestCollection_Upsert(t *testing.T) { ast.Equal(nil, res.UpsertedID) // not exist - filter2 := M{ + filter2 := bson.M{ "name": "Lily", } - replacement2 := M{ + replacement2 := bson.M{ "name": "Lily", "age": 20, } @@ -168,7 +170,7 @@ func TestCollection_Upsert(t *testing.T) { ast.NotNil(res.UpsertedID) // filter is nil or wrong BSON Document format - replacement3 := M{ + replacement3 := bson.M{ "name": "Geek", "age": 21, } @@ -181,7 +183,7 @@ func TestCollection_Upsert(t *testing.T) { ast.Empty(res) // replacement is nil or wrong BSON Document format - filter4 := M{ + filter4 := bson.M{ "name": "Geek", } res, err = cli.Upsert(context.Background(), filter4, nil) @@ -205,18 +207,18 @@ func TestCollection_Update(t *testing.T) { id1 := primitive.NewObjectID() id2 := primitive.NewObjectID() docs := []interface{}{ - D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}}, - D{{Key: "_id", Value: id2}, {Key: "name", Value: "Lucas"}}, + bson.D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}}, + bson.D{{Key: "_id", Value: id2}, {Key: "name", Value: "Lucas"}}, } _, _ = cli.InsertMany(context.Background(), docs) var err error // update already exist record - filter1 := M{ + filter1 := bson.M{ "name": "Alice", } - update1 := M{ - "$set": M{ + update1 := bson.M{ + "$set": bson.M{ "name": "Alice1", "age": 18, }, @@ -225,11 +227,11 @@ func TestCollection_Update(t *testing.T) { ast.NoError(err) // error when not exist - filter2 := M{ + filter2 := bson.M{ "name": "Lily", } - update2 := M{ - "$set": M{ + update2 := bson.M{ + "$set": bson.M{ "name": "Lily", "age": 20, }, @@ -238,7 +240,7 @@ func TestCollection_Update(t *testing.T) { ast.Equal(err, ERR_NO_SUCH_RECORD) // filter is nil or wrong BSON Document format - update3 := M{ + update3 := bson.M{ "name": "Geek", "age": 21, } @@ -249,7 +251,7 @@ func TestCollection_Update(t *testing.T) { ast.Error(err) // update is nil or wrong BSON Document format - filter4 := M{ + filter4 := bson.M{ "name": "Geek", } err = cli.Update(context.Background(), filter4, nil) @@ -272,19 +274,19 @@ func TestCollection_UpdateAll(t *testing.T) { id2 := primitive.NewObjectID() id3 := primitive.NewObjectID() docs := []interface{}{ - D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, - D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, - D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, + bson.D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, + bson.D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, + bson.D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, } _, _ = cli.InsertMany(context.Background(), docs) var err error // update already exist record - filter1 := M{ + filter1 := bson.M{ "name": "Alice", } - update1 := M{ - "$set": M{ + update1 := bson.M{ + "$set": bson.M{ "age": 33, }, } @@ -297,11 +299,11 @@ func TestCollection_UpdateAll(t *testing.T) { ast.Equal(nil, res.UpsertedID) // if record is not exist,err is nil, MatchedCount in res is 0 - filter2 := M{ + filter2 := bson.M{ "name": "Lily", } - update2 := M{ - "$set": M{ + update2 := bson.M{ + "$set": bson.M{ "age": 22, }, } @@ -311,7 +313,7 @@ func TestCollection_UpdateAll(t *testing.T) { ast.Equal(int64(0), res.MatchedCount) // filter is nil or wrong BSON Document format - update3 := M{ + update3 := bson.M{ "name": "Geek", "age": 21, } @@ -324,7 +326,7 @@ func TestCollection_UpdateAll(t *testing.T) { ast.Nil(res) // update is nil or wrong BSON Document format - filter4 := M{ + filter4 := bson.M{ "name": "Geek", } res, err = cli.UpdateAll(context.Background(), filter4, nil) @@ -349,15 +351,15 @@ func TestCollection_Remove(t *testing.T) { id2 := primitive.NewObjectID() id3 := primitive.NewObjectID() docs := []interface{}{ - D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, - D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, - D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, + bson.D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, + bson.D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, + bson.D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, } _, _ = cli.InsertMany(context.Background(), docs) var err error // delete record: name = "Alice" , after that, expect one name = "Alice" record - filter1 := M{ + filter1 := bson.M{ "name": "Alice", } err = cli.Remove(context.Background(), filter1) @@ -368,14 +370,14 @@ func TestCollection_Remove(t *testing.T) { ast.Equal(int64(1), cnt) // delete not match record , report err - filter2 := M{ + filter2 := bson.M{ "name": "Lily", } err = cli.Remove(context.Background(), filter2) ast.Equal(err, ERR_NO_SUCH_RECORD) - // filter is M{},delete one document - filter3 := M{} + // filter is bson.M{},delete one document + filter3 := bson.M{} preCnt, err := cli.Find(context.Background(), filter3).Count() ast.Equal(int64(2), preCnt) @@ -407,16 +409,16 @@ func TestCollection_DeleteAll(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, - D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, - D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, - D{{Key: "_id", Value: id4}, {Key: "name", Value: "Rocket"}, {Key: "age", Value: 23}}, + bson.D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, + bson.D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, + bson.D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, + bson.D{{Key: "_id", Value: id4}, {Key: "name", Value: "Rocket"}, {Key: "age", Value: 23}}, } _, _ = cli.InsertMany(context.Background(), docs) var err error // delete record: name = "Alice" ,after that, expect - record : name = "Alice" - filter1 := M{ + filter1 := bson.M{ "name": "Alice", } res, err := cli.DeleteAll(context.Background(), filter1) @@ -429,7 +431,7 @@ func TestCollection_DeleteAll(t *testing.T) { ast.Equal(int64(0), cnt) // delete with not match filter, DeletedCount in res is 0 - filter2 := M{ + filter2 := bson.M{ "name": "Lily", } res, err = cli.DeleteAll(context.Background(), filter2) @@ -437,8 +439,8 @@ func TestCollection_DeleteAll(t *testing.T) { ast.NotNil(res) ast.Equal(int64(0), res.DeletedCount) - // filter is M{},delete all docs - filter3 := M{} + // filter is bson.M{},delete all docs + filter3 := bson.M{} preCnt, err := cli.Find(context.Background(), filter3).Count() ast.NoError(err) ast.Equal(int64(2), preCnt) @@ -479,7 +481,7 @@ func TestCollection_DeleteAll(t *testing.T) { // defer wg.Done() // var err error // var res *mongo.InsertResult -// doc := M{"_id": primitive.NewObjectID(), "name": "Alice_" + strconv.Itoa(i)} +// doc := bson.M{"_id": primitive.NewObjectID(), "name": "Alice_" + strconv.Itoa(i)} // // res, err = cli.Insert(context.Background(), doc) // ast.NoError(err) @@ -489,7 +491,7 @@ func TestCollection_DeleteAll(t *testing.T) { // }(i) // } // wg.Wait() -// count, err := cli.Find(context.Background(), M{}).Count() +// count, err := cli.Find(context.Background(), bson.M{}).Count() // ast.Equal(nil, err) // ast.Equal(dataNum, int(count)) //} diff --git a/cursor_test.go b/cursor_test.go index 095bd90..b5bd07e 100644 --- a/cursor_test.go +++ b/cursor_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -22,20 +23,20 @@ func TestCursor(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - M{"_id": id1, "name": "Alice", "age": 18}, - M{"_id": id2, "name": "Alice", "age": 19}, - M{"_id": id3, "name": "Lucas", "age": 20}, - M{"_id": id4, "name": "Lucas", "age": 21}, + bson.M{"_id": id1, "name": "Alice", "age": 18}, + bson.M{"_id": id2, "name": "Alice", "age": 19}, + bson.M{"_id": id3, "name": "Lucas", "age": 20}, + bson.M{"_id": id4, "name": "Lucas", "age": 21}, } cli.InsertMany(context.Background(), docs) var res QueryTestItem // if query has 1 record,cursor can run Next one time, Next time return false - filter1 := M{ + filter1 := bson.M{ "name": "Alice", } - projection1 := M{ + projection1 := bson.M{ "name": 0, } @@ -59,7 +60,7 @@ func TestCursor(t *testing.T) { cursor.All(&results) ast.Equal(2, len(results)) // can't match record, cursor run Next and return false - filter2 := M{ + filter2 := bson.M{ "name": "Lily", } diff --git a/example/example_test.go b/example_test.go similarity index 91% rename from example/example_test.go rename to example_test.go index 2894ab6..a95c18e 100644 --- a/example/example_test.go +++ b/example_test.go @@ -7,6 +7,7 @@ import ( "github.com/qiniu/qmgo" "github.com/stretchr/testify/require" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) @@ -88,9 +89,9 @@ func TestQmgo(t *testing.T) { ast.Equal(int64(4), count) // aggregate - matchStage := qmgo.D{{"$match", []qmgo.E{{"weight", qmgo.D{{"$gt", 30}}}}}} - groupStage := qmgo.D{{"$group", qmgo.D{{"_id", "$name"}, {"total", qmgo.D{{"$sum", "$age"}}}}}} - var showsWithInfo []qmgo.M + matchStage := bson.D{{"$match", []bson.E{{"weight", bson.D{{"$gt", 30}}}}}} + groupStage := bson.D{{"$group", bson.D{{"_id", "$name"}, {"total", bson.D{{"$sum", "$age"}}}}}} + var showsWithInfo []bson.M err = cli.Aggregate(context.Background(), qmgo.Pipeline{matchStage, groupStage}).All(&showsWithInfo) ast.Equal(3, len(showsWithInfo)) for _, v := range showsWithInfo { @@ -139,8 +140,8 @@ func TestOfficialMongoDriver(t *testing.T) { // find all 、sort and limit findOptions := options.Find() findOptions.SetLimit(7) - var sorts qmgo.D - sorts = append(sorts, qmgo.E{Key: "weight", Value: 1}) + var sorts bson.D + sorts = append(sorts, bson.E{Key: "weight", Value: 1}) findOptions.SetSort(sorts) diff --git a/query.go b/query.go index 76517b4..d676f85 100644 --- a/query.go +++ b/query.go @@ -5,6 +5,7 @@ import ( "fmt" "reflect" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) @@ -25,13 +26,13 @@ type Query struct { // When multiple sort fields are passed in at the same time, they are arranged in the order in which the fields are passed in. // For example, {"age", "-name"}, first sort by age in ascending order, then sort by name in descending order func (q *Query) Sort(fields ...string) QueryI { - var sorts D + var sorts bson.D for _, field := range fields { key, n := SplitSortField(field) if key == "" { panic("Sort: empty field name") } - sorts = append(sorts, E{Key: key, Value: n}) + sorts = append(sorts, bson.E{Key: key, Value: n}) } return &Query{ @@ -46,8 +47,8 @@ func (q *Query) Sort(fields ...string) QueryI { } // Select is used to determine which fields are displayed or not displayed in the returned results -// Format: M{"age": 1} means that only the age field is displayed -// M{"age": 0} means to display other fields except age +// Format: bson.M{"age": 1} means that only the age field is displayed +// bson.M{"age": 0} means to display other fields except age // When _id is not displayed and is set to 0, it will be returned to display func (q *Query) Select(projection interface{}) QueryI { return &Query{ diff --git a/query_test.go b/query_test.go index 3edd921..2302a37 100644 --- a/query_test.go +++ b/query_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -31,19 +32,19 @@ func TestQuery_One(t *testing.T) { id2 := primitive.NewObjectID() id3 := primitive.NewObjectID() docs := []interface{}{ - D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, - D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, - D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, + bson.D{{Key: "_id", Value: id1}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 18}}, + bson.D{{Key: "_id", Value: id2}, {Key: "name", Value: "Alice"}, {Key: "age", Value: 19}}, + bson.D{{Key: "_id", Value: id3}, {Key: "name", Value: "Lucas"}, {Key: "age", Value: 20}}, } _, _ = cli.InsertMany(context.Background(), docs) var err error var res QueryTestItem - filter1 := M{ + filter1 := bson.M{ "name": "Alice", } - projection1 := M{ + projection1 := bson.M{ "age": 0, } @@ -53,7 +54,7 @@ func TestQuery_One(t *testing.T) { ast.Equal("Alice", res.Name) res = QueryTestItem{} - filter2 := M{ + filter2 := bson.M{ "name": "Lily", } @@ -61,9 +62,9 @@ func TestQuery_One(t *testing.T) { ast.Error(err) ast.Empty(res) - // filter is M{},match all and return one + // filter is bson.M{},match all and return one res = QueryTestItem{} - filter3 := M{} + filter3 := bson.M{} err = cli.Find(context.Background(), filter3).One(&res) ast.NoError(err) @@ -103,20 +104,20 @@ func TestQuery_All(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - M{"_id": id1, "name": "Alice", "age": 18}, - M{"_id": id2, "name": "Alice", "age": 19}, - M{"_id": id3, "name": "Lucas", "age": 20}, - M{"_id": id4, "name": "Lucas", "age": 21}, + bson.M{"_id": id1, "name": "Alice", "age": 18}, + bson.M{"_id": id2, "name": "Alice", "age": 19}, + bson.M{"_id": id3, "name": "Lucas", "age": 20}, + bson.M{"_id": id4, "name": "Lucas", "age": 21}, } _, _ = cli.InsertMany(context.Background(), docs) var err error var res []QueryTestItem - filter1 := M{ + filter1 := bson.M{ "name": "Alice", } - projection1 := M{ + projection1 := bson.M{ "name": 0, } @@ -125,7 +126,7 @@ func TestQuery_All(t *testing.T) { ast.Equal(1, len(res)) res = make([]QueryTestItem, 0) - filter2 := M{ + filter2 := bson.M{ "name": "Lily", } @@ -133,9 +134,9 @@ func TestQuery_All(t *testing.T) { ast.NoError(err) ast.Empty(res) - // filter is M{}, which means to match all, will return all records in the collection + // filter is bson.M{}, which means to match all, will return all records in the collection res = make([]QueryTestItem, 0) - filter3 := M{} + filter3 := bson.M{} err = cli.Find(context.Background(), filter3).All(&res) ast.NoError(err) @@ -150,10 +151,8 @@ func TestQuery_All(t *testing.T) { ast.Error(err) var tv int - ast.Panics(func() { - cli.Find(context.Background(), filter1).All(&tv) - }) - + err = cli.Find(context.Background(), filter1).All(&tv) + ast.Error(err) // res is a parseable object, but the bson tag is inconsistent with the mongodb record, and no error is reported // The corresponding value will be mapped according to the bson tag of the res data structure, and the tag without the value will be the default value of the corresponding type // The length of res is the number of records filtered by the filter condition @@ -177,17 +176,17 @@ func TestQuery_Count(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - M{"_id": id1, "name": "Alice", "age": 18}, - M{"_id": id2, "name": "Alice", "age": 19}, - M{"_id": id3, "name": "Lucas", "age": 20}, - M{"_id": id4, "name": "Lucas", "age": 21}, + bson.M{"_id": id1, "name": "Alice", "age": 18}, + bson.M{"_id": id2, "name": "Alice", "age": 19}, + bson.M{"_id": id3, "name": "Lucas", "age": 20}, + bson.M{"_id": id4, "name": "Lucas", "age": 21}, } _, _ = cli.InsertMany(context.Background(), docs) var err error var cnt int64 - filter1 := M{ + filter1 := bson.M{ "name": "Alice", } @@ -195,7 +194,7 @@ func TestQuery_Count(t *testing.T) { ast.NoError(err) ast.Equal(int64(1), cnt) - filter2 := M{ + filter2 := bson.M{ "name": "Lily", } @@ -203,7 +202,7 @@ func TestQuery_Count(t *testing.T) { ast.NoError(err) ast.Zero(cnt) - filter3 := M{} + filter3 := bson.M{} cnt, err = cli.Find(context.Background(), filter3).Count() ast.NoError(err) @@ -229,10 +228,10 @@ func TestQuery_Skip(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - M{"_id": id1, "name": "Alice", "age": 18}, - M{"_id": id2, "name": "Alice", "age": 19}, - M{"_id": id3, "name": "Lucas", "age": 20}, - M{"_id": id4, "name": "Lucas", "age": 21}, + bson.M{"_id": id1, "name": "Alice", "age": 18}, + bson.M{"_id": id2, "name": "Alice", "age": 19}, + bson.M{"_id": id3, "name": "Lucas", "age": 20}, + bson.M{"_id": id4, "name": "Lucas", "age": 21}, } _, _ = cli.InsertMany(context.Background(), docs) @@ -240,7 +239,7 @@ func TestQuery_Skip(t *testing.T) { var res []QueryTestItem // filter can match records, skip 1 record, and return the remaining records - filter1 := M{ + filter1 := bson.M{ "name": "Alice", } @@ -277,17 +276,17 @@ func TestQuery_Limit(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - M{"_id": id1, "name": "Alice", "age": 18}, - M{"_id": id2, "name": "Alice", "age": 19}, - M{"_id": id3, "name": "Lucas", "age": 20}, - M{"_id": id4, "name": "Lucas", "age": 21}, + bson.M{"_id": id1, "name": "Alice", "age": 18}, + bson.M{"_id": id2, "name": "Alice", "age": 19}, + bson.M{"_id": id3, "name": "Lucas", "age": 20}, + bson.M{"_id": id4, "name": "Lucas", "age": 21}, } _, _ = cli.InsertMany(context.Background(), docs) var err error var res []QueryTestItem - filter1 := M{ + filter1 := bson.M{ "name": "Alice", } @@ -324,10 +323,10 @@ func TestQuery_Sort(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - M{"_id": id1, "name": "Alice", "age": 18}, - M{"_id": id2, "name": "Alice", "age": 19}, - M{"_id": id3, "name": "Lucas", "age": 18}, - M{"_id": id4, "name": "Lucas", "age": 19}, + bson.M{"_id": id1, "name": "Alice", "age": 18}, + bson.M{"_id": id2, "name": "Alice", "age": 19}, + bson.M{"_id": id3, "name": "Lucas", "age": 18}, + bson.M{"_id": id4, "name": "Lucas", "age": 19}, } _, _ = cli.InsertMany(context.Background(), docs) @@ -335,7 +334,7 @@ func TestQuery_Sort(t *testing.T) { var res []QueryTestItem // Sort a single field in ascending order - filter1 := M{ + filter1 := bson.M{ "name": "Alice", } @@ -353,7 +352,7 @@ func TestQuery_Sort(t *testing.T) { ast.Equal(id1, res[1].Id) // Sort a single field in descending order, and sort the other field in ascending order - err = cli.Find(context.Background(), M{}).Sort("-age", "+name").All(&res) + err = cli.Find(context.Background(), bson.M{}).Sort("-age", "+name").All(&res) ast.NoError(err) ast.Equal(4, len(res)) ast.Equal(id2, res[0].Id) @@ -383,16 +382,16 @@ func TestQuery_Distinct(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - M{"_id": id1, "name": "Alice", "age": 18}, - M{"_id": id2, "name": "Alice", "age": 19}, - M{"_id": id3, "name": "Lucas", "age": 20}, - M{"_id": id4, "name": "Lucas", "age": 21}, + bson.M{"_id": id1, "name": "Alice", "age": 18}, + bson.M{"_id": id2, "name": "Alice", "age": 19}, + bson.M{"_id": id3, "name": "Lucas", "age": 20}, + bson.M{"_id": id4, "name": "Lucas", "age": 21}, } _, _ = cli.InsertMany(context.Background(), docs) var err error - filter1 := M{ + filter1 := bson.M{ "name": "Lily", } var res1 []int32 @@ -401,7 +400,7 @@ func TestQuery_Distinct(t *testing.T) { ast.NoError(err) ast.Equal(0, len(res1)) - filter2 := M{ + filter2 := bson.M{ "name": "Alice", } var res2 []int32 @@ -465,20 +464,20 @@ func TestQuery_Select(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - M{"_id": id1, "name": "Alice", "age": 18}, - M{"_id": id2, "name": "Alice", "age": 19}, - M{"_id": id3, "name": "Lucas", "age": 20}, - M{"_id": id4, "name": "Lucas", "age": 21}, + bson.M{"_id": id1, "name": "Alice", "age": 18}, + bson.M{"_id": id2, "name": "Alice", "age": 19}, + bson.M{"_id": id3, "name": "Lucas", "age": 20}, + bson.M{"_id": id4, "name": "Lucas", "age": 21}, } _, _ = cli.InsertMany(context.Background(), docs) var err error var res QueryTestItem - filter1 := M{ + filter1 := bson.M{ "_id": id1, } - projection1 := M{ + projection1 := bson.M{ "age": 1, } @@ -490,7 +489,7 @@ func TestQuery_Select(t *testing.T) { ast.Equal(id1, res.Id) res = QueryTestItem{} - projection2 := M{ + projection2 := bson.M{ "age": 0, } @@ -502,7 +501,7 @@ func TestQuery_Select(t *testing.T) { ast.Equal(id1, res.Id) res = QueryTestItem{} - projection3 := M{ + projection3 := bson.M{ "_id": 0, } @@ -529,19 +528,19 @@ func TestQuery_Cursor(t *testing.T) { id3 := primitive.NewObjectID() id4 := primitive.NewObjectID() docs := []interface{}{ - D{{"_id", id1}, {"name", "Alice"}, {"age", 18}}, - D{{"_id", id2}, {"name", "Alice"}, {"age", 19}}, - D{{"_id", id3}, {"name", "Lucas"}, {"age", 20}}, - D{{"_id", id4}, {"name", "Lucas"}, {"age", 21}}, + bson.D{{"_id", id1}, {"name", "Alice"}, {"age", 18}}, + bson.D{{"_id", id2}, {"name", "Alice"}, {"age", 19}}, + bson.D{{"_id", id3}, {"name", "Lucas"}, {"age", 20}}, + bson.D{{"_id", id4}, {"name", "Lucas"}, {"age", 21}}, } _, _ = cli.InsertMany(context.Background(), docs) var res QueryTestItem - filter1 := M{ + filter1 := bson.M{ "name": "Alice", } - projection1 := M{ + projection1 := bson.M{ "name": 0, } @@ -556,7 +555,7 @@ func TestQuery_Cursor(t *testing.T) { val = cursor.Next(&res) ast.Equal(false, val) - filter2 := M{ + filter2 := bson.M{ "name": "Lily", } From c386b89291ae3034509cb61bdb47af5a4504a966 Mon Sep 17 00:00:00 2001 From: jiangzhi Date: Fri, 7 Aug 2020 16:11:44 +0800 Subject: [PATCH 3/5] support aggregate --- example_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/example_test.go b/example_test.go index a95c18e..2b15011 100644 --- a/example_test.go +++ b/example_test.go @@ -1,11 +1,10 @@ -package example +package qmgo import ( "context" "errors" "testing" - "github.com/qiniu/qmgo" "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" @@ -54,7 +53,7 @@ func TestQmgo(t *testing.T) { ctx := context.Background() // create connect - cli, err := qmgo.Open(ctx, &qmgo.Config{Uri: URI, Database: DATABASE, Coll: COLL}) + cli, err := Open(ctx, &Config{Uri: URI, Database: DATABASE, Coll: COLL}) ast.Nil(err) defer func() { @@ -92,7 +91,7 @@ func TestQmgo(t *testing.T) { matchStage := bson.D{{"$match", []bson.E{{"weight", bson.D{{"$gt", 30}}}}}} groupStage := bson.D{{"$group", bson.D{{"_id", "$name"}, {"total", bson.D{{"$sum", "$age"}}}}}} var showsWithInfo []bson.M - err = cli.Aggregate(context.Background(), qmgo.Pipeline{matchStage, groupStage}).All(&showsWithInfo) + err = cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}).All(&showsWithInfo) ast.Equal(3, len(showsWithInfo)) for _, v := range showsWithInfo { if "a1" == v["_id"] { From 4e6730ed2a33235db6909ab0dfc0c01f15671b46 Mon Sep 17 00:00:00 2001 From: jiangzhi Date: Fri, 7 Aug 2020 16:24:00 +0800 Subject: [PATCH 4/5] support aggregate --- example_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example_test.go b/example_test.go index 2b15011..5937490 100644 --- a/example_test.go +++ b/example_test.go @@ -63,7 +63,7 @@ func TestQmgo(t *testing.T) { }() defer cli.DropDatabase(ctx) - cli.EnsureIndexes(ctx, []string{"_id"}, []string{"age", "name,weight"}) + cli.EnsureIndexes(ctx, []string{}, []string{"age", "name,weight"}) // insert one document _, err = cli.InsertOne(ctx, oneUserInfo) ast.Nil(err) From 985395dd61ce12ee029aca4cd16e3ffa360bfab4 Mon Sep 17 00:00:00 2001 From: jiangzhi Date: Fri, 7 Aug 2020 16:28:39 +0800 Subject: [PATCH 5/5] update readme --- README.md | 2 +- README_ZH.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 87092b4..cf2dd19 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ var oneUserInfo = UserInfo{ Create index ```go -cli.EnsureIndexes(ctx, []string{"_id"}, []string{"age", "name,weight"}) +cli.EnsureIndexes(ctx, []string{}, []string{"age", "name,weight"}) ``` - Insert a document diff --git a/README_ZH.md b/README_ZH.md index a4ef89a..a09bbfd 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -78,7 +78,7 @@ var oneUserInfo = UserInfo{ 创建索引 ```go -cli.EnsureIndexes(ctx, []string{"_id"}, []string{"age", "name,weight"}) +cli.EnsureIndexes(ctx, []string{}, []string{"age", "name,weight"}) ``` - 插入一个文档