mongodb学习笔记(2)go语言操作mongodb
1、安装驱动包
1.1安装mongoDB Go驱动包
go get github.com/mongodb/mongo-go-driverl
1.2通过go连接MongoDB
package main import ( "context" "fmt" "log" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) func main() { // 设置客户端连接配置 clientOptions := options.Client().ApplyURI("mongodb://127.0.0.1:27017") // 连接到MongoDB Client, err := mongo.Connect(context.TODO(), clientOptions) if err != nil { log.Fatal(err) } defer func(){ // 断开连接 err = Client.Disconnect(context.TODO()) if err != nil { log.Fatal(err) } fmt.Println("Connection to MongoDB closed.") }() // 检查连接 err = Client.Ping(context.TODO(), nil) if err != nil { log.Fatal(err) } fmt.Println("Connected to MongoDB!") }
1.3连接池模式
import ( "context" "time" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) func ConnectToDB(uri, name string, timeout time.Duration, num uint64) (*mongo.Database, error) { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() o := options.Client().ApplyURI(uri) o.SetMaxPoolSize(num) client, err := mongo.Connect(ctx, o) if err != nil { return nil, err } return client.Database(name), nil }
2、BSON
2.1概述
MongoDB中的JSON文档存储在名为BSON(二进制编码的JSON)的二进制表示中。与其他将JSON数据存储为简单字符串和数字的数据库不同,BSON编码扩展了JSON表示,使其包含额外的类型,如int、long、date、浮点数和decimal128。这使得应用程序更容易可靠地处理、排序和比较数据。
连接MongoDB的Go驱动程序中有两大类型表示BSON数据:D
和Raw
。
类型D
家族被用来简洁地构建使用本地Go类型的BSON对象。这对于构造传递给MongoDB的命令特别有用。D
家族包括四类:
-
- D:一个BSON文档。这种类型应该在顺序重要的情况下使用,比如MongoDB命令。
- M:一张无序的map。它和D是一样的,只是它不保持顺序。
- A:一个BSON数组。
- E:D里面的一个元素。
2.2导入包
要使用BSON,需要先导入下面的包:
import "go.mongodb.org/mongo-driver/bson"
3、CRUD
3.1概述
下面以user和video这两个结构体来演示mongodb的CRUD,它们的结构如下所示:
type User struct { UserId int64 `bson:"user_id"` Username string `bson:"username"` FollowCount int64 `bson:"follow_count"` // 关注数 FollowerCount int64 `bson:"follower_count"` // 粉丝数 Follows []int64 `bson:"follows"` // 关注列表 Followers []int64 `bson:"followers"` // 粉丝列表 PublishList []int64 `bson:"publish_list"` // 发布视频列表 FavoriteList []int64 `bson:"favorite_list"` // 点赞列表 } type Video struct { VideoId int64 `bson:"video_id"` UserId int64 `bson:"user_id"` PlayUrl string `bson:"play_url"` CoverUrl string `bson:"cover_url"` FavoriteCount int64 `bson:"favorite_count"` Favorites []int64 `bson:"favorites"` CommentCount int64 `bson:"comment_count"` Comments []Comment `bson:"comments, inline"` PublishDate time.Time `bson:"publish_date"` Title string `bson:"title"` }
3.2插入元素
//插入单个数据 func InsertOneVideo(Client *mongo.Client){ coll := Client.Database("tiktok").Collection("video") video:=Video{ VideoId: 10, UserId: 1, PlayUrl: "https://www.w3schools.com/html/movie.mp4", CoverUrl: "https://cdn.pixabay.com/photo/2016/03/27/18/10/bear-1283347_1280.jpg", FavoriteCount: 0, CommentCount: 0, Favorites: []int64{}, Comments: []Comment{}, PublishDate: time.Now(), Title: "video10", } result, err := coll.InsertOne(context.TODO(), video) if err != nil { fmt.Println(err) } fmt.Println(result) } //插入多个数据 func InsertVideos(Client *mongo.Client){ t := time.Now() var vs []interface{} for i:=0;i<10;i++{ mm, _ := time.ParseDuration(strconv.Itoa(i*60)+"m") video:=Video{ VideoId: int64(i), UserId: int64(i), PlayUrl: "https://www.w3schools.com/html/movie.mp4", CoverUrl: "https://cdn.pixabay.com/photo/2016/03/27/18/10/bear-1283347_1280.jpg", FavoriteCount: 0, CommentCount: 0, Favorites: []int64{}, Comments: []Comment{}, PublishDate: t.Add(mm), Title: "video"+strconv.Itoa(i), } vs=append(vs,video) } collection := Client.Database("tiktok").Collection("video") insertManyResult, err := collection.InsertMany(context.TODO(), vs) if err != nil { log.Fatal(err) } fmt.Println("Inserted multiple documents: ", insertManyResult.InsertedIDs) }
//查询到匹配数据则修改,否则就插入 func UpdateOrInsert(client *mongo.Client){ coll := client.Database("tiktok").Collection("user") filter := bson.D{{"username", "Amy"}} update := bson.D{{"$set", bson.D{{"username", Susan}}}} opts := options.Update().SetUpsert(true) result, err := coll.UpdateOne(context.TODO(), filter, update, opts) if err != nil { panic(err) } fmt.Printf("Number of documents updated: %v\n", result.ModifiedCount) fmt.Printf("Number of documents upserted: %v\n", result.UpsertedCount) }
3.3删除元素
//删除一条文档 func DeleteOneVideo(Client *mongo.Client){ coll := Client.Database("tiktok").Collection("video") filter := bson.D{{"VideoId", "10"}} result, err := coll.DeleteOne(context.TODO(), filter) if err != nil { panic(err) } fmt.Println(result) } //删除多条文档 func DeleteVideos(Client *mongo.Client){ coll := Client.Database("tiktok").Collection("video") filter := bson.D{{"VideoId", bson.D{{"$gt", 9}}}} results, err := coll.DeleteMany(context.TODO(), filter) if err != nil { panic(err) } fmt.Println(results) }
3.4更新文档
//更新多条文档 func UpdateManyUsers(client *mongo.Client){ coll := client.Database("tiktok").Collection("user") filter := bson.D{{"UserId", bson.D{{"$gt", 5}}}} update := bson.D{{"$mul", bson.D{{"FollowCount", 1}}}} result, err := coll.UpdateMany(context.TODO(), filter, update) if err != nil { panic(err) } fmt.Println(result) } //查询到匹配数据则修改,否则就插入 func UpdateOrInsert(client *mongo.Client){ coll := client.Database("tiktok").Collection("user") filter := bson.D{{"username", "Amy"}} update := bson.D{{"$set", bson.D{{"username", Susan}}}} opts := options.Update().SetUpsert(true) result, err := coll.UpdateOne(context.TODO(), filter, update, opts) if err != nil { panic(err) } fmt.Printf("Number of documents updated: %v\n", result.ModifiedCount) fmt.Printf("Number of documents upserted: %v\n", result.UpsertedCount) }
//替换文档 func ReplaceVideo(client *mongo.Client){ coll := client.Database("tiktok").Collection("user") filter := bson.D{{"user_id", 10}} replacement := bson.D{{"user_id", "10"}, {"username", "david"}} result, err := coll.ReplaceOne(context.TODO(), filter, replacement) if err!=nil{ panic(err) } fmt.Printf("Documents matched: %v\n", result.MatchedCount) fmt.Printf("Documents replaced: %v\n", result.ModifiedCount) }
//向数组添加元素 func Add(client *mongo.Client, id, videoId int64){ collection := client.Database("tiktok").Collection("user") query := bson.M{"user_id": id} update := bson.M{"$push": bson.M{"favorite_list": videoId}} result, err := collection.UpdateOne(context.TODO(), query, update) if err != nil { panic(err) } fmt.Println("更新用户信息的结果",result) collection = client.Database("tiktok").Collection("video") //更新点赞列表 query = bson.M{"video_id": videoId} update = bson.M{"$push": bson.M{"favorites": id}} result, err = collection.UpdateOne(context.TODO(), query, update) if err != nil { panic(err) } fmt.Println("更新用户信息的结果",result) } //从数组删除指定元素 func Reduce(client *mongo.Client, id, videoId int64){ collection := client.Database("tiktok").Collection("user") query := bson.M{"user_id": id} update := bson.M{"$pull": bson.M{"favorite_list": videoId}} result, err := collection.UpdateOne(context.TODO(), query, update) if err != nil { panic(err) } fmt.Println("更新用户信息的结果",result) collection = client.Database("tiktok").Collection("video") //更新点赞列表 query = bson.M{"video_id": videoId} update = bson.M{"$pull": bson.M{"favorites": id}} result, err = collection.UpdateOne(context.TODO(), query, update) if err != nil { panic(err) } fmt.Println("更新用户信息的结果",result) }
3.5查询文档
//查询的单个文档 func QueryUserByID(client *mongo.Client, id int64) User { collection := client.Database("tiktok").Collection("user") filter := bson.D{{"user_id", id}} var result User err := collection.FindOne(context.TODO(), filter).Decode(&result) if err != nil { log.Println(err) } fmt.Printf("根据Id查询用户的结果: %+v\n", result) return result } //查询多个文档 func QueryVideosByTime(Client *mongo.Client, t time.Time)([]*Video, error){ // 将选项传递给Find() findOptions := options.Find() findOptions.SetLimit(30) //设置一次查询的最大数量 // 把bson.D{{}}作为一个filter来匹配所有文档 collection := Client.Database("tiktok").Collection("video") sort := bson.D{{"publish_date", -1}} findOptions.SetSort(sort) cur, err := collection.Find(context.TODO(), bson.M{"publish_date": bson.M{"$gte": t}}, findOptions) if err != nil { log.Fatal(err) return results,err } // 查找多个文档返回一个光标 // 遍历游标允许我们一次解码一个文档 for cur.Next(context.TODO()) { // 创建一个值,将单个文档解码为该值 var elem Video err := cur.Decode(&elem) if err != nil { log.Fatal(err) return results,err } results = append(results, &elem) } if err := cur.Err(); err != nil { log.Fatal(err) return results,err } // 完成后关闭游标 cur.Close(context.TODO()) //fmt.Printf("Found multiple documents (array of pointers): %#v,%d\n", results,len(results)) for i:=0;i<len(results);i++{ fmt.Println(results[i].PublishDate) } return results,err }
4、批量操作
//批量操作 func Bulk(client *mongo.Client){ coll := client.Database("tiktok").Collection("user") models := []mongo.WriteModel{ mongo.NewInsertOneModel().SetDocument(bson.D{{"user_id", "11"}, {"username", "jack11"}}), mongo.NewInsertOneModel().SetDocument(bson.D{{"user_id", "12"}, {"username", "jack12"}}), mongo.NewReplaceOneModel().SetFilter(bson.D{{"user_id", "11"}}). SetReplacement(bson.D{{"user_id", 11},{"username", "Amy"}}), mongo.NewUpdateManyModel().SetFilter(bson.D{{"user_id", bson.D{{"$lt", 7}}}}). SetUpdate(bson.D{{"$inc", bson.D{{"follows", 3}}}}), mongo.NewDeleteManyModel().SetFilter(bson.D{{"follows", 9}}), } opts := options.BulkWrite().SetOrdered(false)//设置插入的顺序是否按照models的顺序 results, err := coll.BulkWrite(context.TODO(), models, opts) if err != nil { panic(err) } fmt.Printf("Number of documents inserted: %d\n", results.InsertedCount) fmt.Printf("Number of documents replaced or updated: %d\n", results.ModifiedCount) fmt.Printf("Number of documents deleted: %d\n", results.DeletedCount) }
5、复合操作
//复合操作:查找和删除 func FindAndDelete(client *mongo.Client){ coll := client.Database("tiktok").Collection("user") filter := bson.D{{"user_id", 11}} var deletedDoc bson.D err := coll.FindOneAndDelete(context.TODO(), filter).Decode(&deletedDoc) if err != nil { panic(err) } fmt.Println(deletedDoc) } //复合操作:查找和更新 func FindAndUpdate(client *mongo.Client){ coll := client.Database("tiktok").Collection("user") filter := bson.D{{"user_id", 12}} update := bson.D{{"$set", bson.D{{"followers", 9}}}} opts := options.FindOneAndUpdate().SetReturnDocument(options.After) var updatedDoc bson.D err := coll.FindOneAndUpdate(context.TODO(), filter, update, opts).Decode(&updatedDoc) if err != nil { panic(err) } fmt.Println(updatedDoc) } //复合操作:查找和替换 func FindAndReplace(client *mongo.Client){ coll := client.Database("tiktok").Collection("user") filter := bson.D{{"user_id", 12}} replacement := bson.D{{"user_id", 12}, {"username", "bomb"}} var previousDoc bson.D err := coll.FindOneAndReplace(context.TODO(), filter, replacement).Decode(&previousDoc) if err != nil { panic(err) } fmt.Println(previousDoc) }
6、事务
// 取消关注 func UnFollowRelation(ctx context.Context, followee int64, follower int64) error { userColl := MongoCli.Database("tiktok").Collection("user") // 定义事务 callback := func(sessCtx mongo.SessionContext) (interface{}, error) { // 是否关注 err := userColl.FindOne(sessCtx, bson.D{{"user_id", followee}, {"followers", bson.D{{"$all", bson.A{follower}}}}}).Err() if err != nil { if err == mongo.ErrNoDocuments { return nil, errors.New("the user is not followed") } log.Printf("%v\n", err) return nil, err } filter := bson.D{{"user_id", followee}} update := bson.D{ {"$inc", bson.D{{"follower_count", -1}}}, {"$pull", bson.D{{"followers", follower}}}, } if updateResult, err := userColl.UpdateOne(sessCtx, filter, update); err != nil { return nil, err } else if updateResult.MatchedCount == 0 { return nil, errors.New("no followee was found") } filter = bson.D{{"user_id", follower}} update = bson.D{ {"$inc", bson.D{{"follow_count", -1}}}, {"$pull", bson.D{{"follows", followee}}}, } if updateResult, err := userColl.UpdateOne(sessCtx, filter, update); err != nil { return nil, err } else if updateResult.MatchedCount == 0 { return nil, errors.New("no follower was found") } return nil, nil } // 开启会话 session, err := MongoCli.StartSession() if err != nil { log.Printf("ERROR: fail to start mongo session. %v\n", err) return err } defer session.EndSession(ctx) // 执行事务 _, err = session.WithTransaction(ctx, callback) if err != nil { log.Printf("ERROR: fail to UnFollowRelation. %v\n", err) return err } return nil }