MongoDB数据读写查
mongodb和其他sql不同的地方:
- mongodb的表单叫做Collection(集),
- 它没有严格的字段类型限制,甚至没有字段限制,可以容纳不同字段名,类型,结构,的数据在一个集
- 它没有关系映射,任何数据之间的联系操作都要手动编写代码去完成
- 它的唯一键值ID为对象,如果要记录对象需转为字符串
- 任何要操作某条数据时,如果知道键值ID,需要先转为对象
示范
model.go
package model
import (
"go.mongodb.org/mongo-driver/mongo"
)
// 定义全局表单变量,也就是集的对象
var (
TicketModel *mongo.Collection
FqaModel *mongo.Collection
NewsModel *mongo.Collection
)
//初始化连接程序
func Init(client *mongo.Client) {
db := client.Database("btcinfo") //连接btcinfo数据库
TicketModel = db.Collection("ticket") //把集(表)对象赋予变量
FqaModel = db.Collection("fqa")
NewsModel = db.Collection("news")
}
main.go
package main
import (
model "btcinfo/models" //导入model包
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"context"
"time"
"log"
)
func main() {
// 运行初始化 MongoDB 客户端连接的函数
client, err := initMongoClient()
if err != nil {
log.Fatal("初始化数据库客户端失败:", err)
}
defer client.Disconnect(context.Background())
//执行模型初始化
model.Init(client)
r := router.Router()
r.Run(":80") // 启动HTTP服务器,监听端口80
}
//初始化 MongoDB 客户端连接的函数
func initMongoClient() (*mongo.Client, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
if err != nil {
return nil, err
}
// 尝试 Ping MongoDB 以确保连接成功
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal("数据库ping失败", err)
return nil, err
}
return client, err
}
数据库的操作
注意,官方教程是使用bson.D{{"id", "123"}}的格式,但是这样不会被编辑器识别其格式,会出现警告,所以我采用bson.D{{Key: "id", Value: "123"}}这种格式可避免警告。
controller.go
package controller
import (
model "btcinfo/models"
"fmt"
"log"
"context"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive" //转换对象和字符串需要
)
func(){
//写入一条数据(也叫写入文档)
// 构造要插入的数据
data := bson.D{
{Key: "email", Value: "13322@gmail.com"},
{Key: "subject", Value: "主题"},
{Key: "description", Value: "内容"},
}
// 向 TicketModel集插入数据
result, err2 := model.TicketModel.InsertOne(context.Background(), data)
if err2 != nil {
log.Fatal("写入文档出错", err2)
return
}
// 取刚才插入数据的字符串ID,
insertedID := result.InsertedID.(primitive.ObjectID).Hex()
//.(primitive.ObjectID).Hex()用于将对象转为字符串ID
//修改数据,或者追加字段
//指定被目标条件,这里的result.InsertedID是之前写入返回的对象
filter := bson.D{{Key: "_id", Value:result.InsertedID}}
// 构造更新操作,和内容,$set为插入数据,或者更新
update := bson.D{
{Key: "$set", Value: bson.D{
// 设置要更新的字段和对应的值,这里假设追加了一个is_file字段,值为布尔型
{Key: "is_file", Value: true},
}},
}
// 执行更新操作,注意_id条件必须是对象,而不是字符串id
result2, err3 := model.TicketModel.UpdateOne(context.Background(),filter, update)
if err3 != nil {
log.Fatal("写入附件字段失败:", err3)
return
}
// 检查是否有文档被更新,用来判断是否更新成功
if result2.ModifiedCount == 0 {
log.Println("未更新任何数据,可能是指定的 _id 不存在于数据库中")
return
}
//删除字段
//假设我知道这条数据的id,将字符串ID转为数据对象ID
objectID, err := primitive.ObjectIDFromHex("65edb47e1858a8d104843ef7")
if err != nil {
log.Println("转换ID出错:", err)
return
}
// 构造过滤条件,指定要更新的文档
filter := bson.D{{Key: "_id", Value: objectID}}
// 构造更新文档,使用 $unset 操作符指定要删除的字段,$unset就是删除
update := bson.D{
{Key:"$unset", Value:bson.D{{Key: "is_file", Value: ""}}},
}
// 执行更新操作,删除字段
_, err5 := model.TicketModel.UpdateOne(context.Background(), filter, update)
if err5 != nil {
log.Println("更新文档出错:", err5)
return
}
}
对象ID和字符串ID的互转
//将对象ID转字符串ID,假设result是返回的对象
insertedID := result.InsertedID.(primitive.ObjectID).Hex()
//或者可以试试直接result.Hex()
//将字符串ID转对象ID
objectID, err := primitive.ObjectIDFromHex("65edb47e1858a8d104843ef7")
if err != nil {
log.Println("转换ID出错:", err)
return
}
查询
查询操作,查询一个表的所有文档:
import (
model "btcinfo/models"
"fmt"
"context"
"go.mongodb.org/mongo-driver/bson"
)
// 定义一个文章据库模型
type Article_model struct {
Subject string //主题
Description string //描述
Dalei string //类型
Xiaolei string //类型
CreatedAt time.Time //创建时间
}
//下面的要写在某个函数里
// 定义一个空的切片来存储查询结果
var results []Article_model
// 执行查询操作
cursor, err := FaqModel.Find(context.TODO(), bson.M{})
if err != nil {
c.JSON(500, gin.H{"db_error": err})
return
}
if err := cursor.All(context.TODO(), &results); err != nil {
// 处理解码错误
c.JSON(500, gin.H{"db_error": err})
return
}
//遍历单个结果
for _, result := range results {
fmt.Printf("主题:", result.Subject)
}
按条件查询结果
//查询xiaolei字段值为gift的结果
filter := bson.D{{Key: "xiaolei", Value: "gift"}}
// 执行查询操作
cursor, err := FaqModel.Find(context.TODO(), filter)
if err != nil {
c.JSON(500, gin.H{"db_error": err})
return
}
//其他的就和上面一样解码处理就行
更多查询方法
//查一条数据
// 定义一个空的切片来存储查询结果
var result Article_model
err = coll.FindOne(context.TODO(), bson.D{{Key: "xiaolei", Value: "gift"}}).Decode(&result)//结果保存在result中
//查多条数据,其中$gte 是大于等于的意思,也就是查找大于或等于46的结果
cursor, err := coll.Find(context.TODO(), bson.D{{Key: "age", Value: bson.D{{Key: "$gte", Value:46}}}})
查询结果排除某条结果:
//设置查询结果为3条
findOptions := options.Find().SetLimit(3)
//构建查询条件排除某条
filter = bson.D{{Key:"_id", Value:bson.D{{Key:"$ne", Value:objectID}}}}
排序
//默认是按_id正序排,也就是越晚插入越靠前
options := options.Find().SetSort(bson.D{{"_id", -1}}) //按id倒序
分页查询
page, err := strconv.Atoi(c.Param("page")) //页码需要先转整数
if err != nil {
// 处理转换错误
c.JSON(400, gin.H{"page_error": err})
return
}
pageSize := 6 // 每页显示的文档数量
// 计算要跳过的文档数量
skip := (page - 1) * pageSize
// 设置查询选项,包括分页参数
options := options.Find()
options.SetSkip(int64(skip))
options.SetLimit(int64(pageSize))
查询总共有多少条数据
// 使用 estimatedDocumentCount 方法,这样更快,可能不准确
count, _ := model.ArticleModel.EstimatedDocumentCount(context.TODO())
// 使用 countDocuments 方法,准确但是慢
count, err := model.ArticleModel.CountDocuments(context.TODO())
//按条件查询返回数量
//查询条件
filter = bson.D{{Key: "xiaolei", Value: xiaolei}}
//取当前总页数
zongtiaoshu, _ := model.ArticleModel.CountDocuments(context.TODO(), filter)
fmt.Print("总条数",zongtiaoshu)
聚合管道查询的示范
//指定查询某个小类
filter = bson.D{{Key: "xiaolei", Value: xiaolei}}
// 构造聚合管道
pipeline := bson.A{
bson.M{
"$match": filter,
},
bson.M{
"$project": bson.M{
"description": bson.M{ //指定要截断的字段description
"$substr": bson.A{"$description", 0, 50},
},
// 其他字段投影
// ...
},
},
}
// 执行聚合查询操作
cursor, err := model.ArticleModel.Aggregate(context.TODO(), pipeline)
if err != nil {
c.JSON(500, gin.H{"db_error": err})
return
}
//解码
if err := cursor.All(context.TODO(), &results); err != nil {
// 处理解码错误
c.JSON(500, gin.H{"db_error": err})
return
}
删除一条数据
// 构建删除的过滤条件
filter := bson.M{"_id": objectID}
// 执行删除操作
_, err := model.FaqModel.DeleteOne(context.TODO(), filter)
if err != nil {
c.JSON(400, gin.H{"删除失败": err})
return
}
数据库迁移
备份mongodump --host 127.0.0.1:27017 --db btcinfo --out btcinfo
//也可以指定路径--out /path/to/backup/directory
恢复mongorestore --host 127.0.0.1:27017 --db btcinfo --dir /home/btcinfo/