二丫讲梵 二丫讲梵
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)

二丫讲梵

行者常至,为者常成
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)
  • Shell编程

  • Go编程笔记

    • 开发技巧

    • 库包研究

      • 使用gorm进行联合查询的整理总结
      • 一个ftp客户端的封装
      • 使用go-bindata将文件编译进二进制
      • go-gitlab包源码探寻与心得
      • 利用cobra库快速开发类似kubectl一样的命令行工具
      • 使用MongoDB官方go库操作MongoDB
        • 初始化连接
        • 插入
          • 插入单条
          • 插入多条
        • 更新
          • 更新单条
          • 更新多条
        • 查询
          • 查询单条
          • 利用 ID 查询
          • 模糊查询
          • 查询多条
        • 替换
          • 替换单条
        • 删除
          • 删除单条
          • 删除多条
        • 批量
        • 汇总
          • 两种方式
        • 关联
          • 关联查询
        • 字段
          • 添加字段
          • 添加单条
          • 添加多条
          • 删除字段
          • 删除单条
          • 删除多条
      • 再探-利用gorm自身提供的方法实现MySQL中数据关联的能力
      • 使用retry-go给项目添加重试机制
      • go-cache包的使用简析
      • 利用gorm自身提供的方法实现存在更新不存在则创建的能力
      • 近期关于cobra库的一些实践心得总结
    • 个人项目

  • 前端编程笔记

  • Go学习笔记

  • Vue-21年学习笔记

  • Vue-22年重学笔记

  • 编程世界
  • Go编程笔记
  • 库包研究
二丫讲梵
2022-01-30
目录

使用MongoDB官方go库操作MongoDB

文章发布较早,内容可能过时,阅读注意甄别。

官方库更新很及时,兼容性最好,官方提供的文档也比较全,社区比较活跃,因此比较建议使用这个库来进行交互。

  • GitHub 地址 (opens new window)

  • 官方文档 (opens new window)

  • 社区论坛 (opens new window)

  • 网上的文章:

    • mongo-drive 基础使用 (opens new window)
    • mongodb 在 golang 的应用 (opens new window)

# 初始化连接

初始化连接方法如下:

package public

import (
	"context"
	"log"

	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

func InitDb() *mongo.Client {
	uri := "mongodb://root:123456@localhost:27017"
	if uri == "" {
		log.Fatal("You must set your 'MONGODB_URI' environmental variable. See\n\t https://docs.mongodb.com/drivers/go/current/usage-examples/#environment-variable")
	}
	client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(uri))
	if err != nil {
		panic(err)
	}

	defer func() {
		if err := client.Disconnect(context.TODO()); err != nil {
			panic(err)
		}
	}()

	return client
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

此处返回一个客户端对象, 然后通过里边的方法指定库和表。

指定库:

var DB *mongo.Database

DB = public.InitDb().Database("class")  // 指定库名为 class
1
2
3

指定表:

table := DB.Collection("user") // 指定表名为user
1

# 插入

# 插入单条

首次示例提供完整示例,后续仅提供方法。

package main

import (
	"context"
	"learn-mongo/public"

	"go.mongodb.org/mongo-driver/mongo"
)

var DB *mongo.Database
var ctx = context.Background()

func init() {
	// 初始化数据库
	DB = public.InitDb().Database("class")
}

// AddOne 插入单条
func AddOne() {
	coll := DB.Collection("testdata")
	doc := make(map[string]interface{})
	doc["title"] = "test"
	doc["content"] = "this is a test"
	_, err := coll.InsertOne(ctx, doc)
	if err != nil {
		panic(err)
	}
}

func main() {
	AddOne()
	defer func() {
		if err := public.InitDb().Disconnect(context.TODO()); err != nil {
			panic(err)
		}
	}()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

其中一些已经说明过了,暂不多赘述,运行之后,可以在表中看到此条数据:

db.getCollection('testdata').find({"title":"test"})

/* 1 */
{
    "_id" : ObjectId("61f2389ca0cd2ab482132e57"),
    "title" : "test",
    "content" : "this is a test"
}
1
2
3
4
5
6
7
8

# 插入多条

// AddMore 插入多条
func AddMore() {
	coll := DB.Collection("testdata")
	docs := []interface{}{
		bson.D{{"title", "Record of a Shriveled Datum"}, {"text", "No bytes, no problem. Just insert a document, in MongoDB"}},
		bson.D{{"title", "Showcasing a Blossoming Binary"}, {"text", "Binary data, safely stored with GridFS. Bucket the data"}},
	}
	_, err := coll.InsertMany(context.TODO(), docs)
	if err != nil {
		panic(err)
	}
}
1
2
3
4
5
6
7
8
9
10
11
12

# 更新

# 更新单条

// UpdateOne 更新单条
func UpdateOne() {
	coll := DB.Collection("testdata")
	filter := bson.D{{"title", "test"}}
	update := bson.D{{"$set", bson.D{{"avg_rating", 4.5}}}}

	_, err := coll.UpdateOne(context.TODO(), filter, update)
	if err != nil {
		panic(err)
	}
}
1
2
3
4
5
6
7
8
9
10
11

UpdateOne有三个参数,传递一个 context,一个过滤规则拿到匹配的数据,一个 update 表示将要更新的内容。

此处表示在testdata表中查询出title为 test 的这条数据,在匹配的这条记录中添加一个avg_rating的字段,其值为 4.5。

# 更新多条

进行操作之前,我们先准备一些测试数据:

db.testdata.insert([
   { "name" : "小A", "identify": "aaa","age": 1, "group_identify": "ops"},
   { "name" : "小B", "identify": "bbb","age": 2, "group_identify": "ops"},
   { "name" : "小C", "identify": "ccc","age": 3, "group_identify": "ops"},
   { "name" : "小D", "identify": "ddd","age": 4, "group_identify": "test"},
   { "name" : "小E", "identify": "eee","age": 5, "group_identify": "test"},
   { "name" : "小F", "identify": "fff","age": 6, "group_identify": "test"}
])
1
2
3
4
5
6
7
8

我的需求是更新 test 小组同学的年龄为当前的 3 倍:

// UpdateMany 更新多条
func UpdateMany() {
	coll := DB.Collection("testdata")
	filter := bson.D{{"group_identify", "test"}}
	update := bson.D{{"$mul", bson.D{{"age", 3}}}}

	_, err := coll.UpdateMany(context.TODO(), filter, update)
	if err != nil {
		panic(err)
	}
}
1
2
3
4
5
6
7
8
9
10
11

MongoDB 在更新的时候,提供了一些很好用的方法供我们查询,详情参考:[[MongoDB交互时表达式与方法整理汇总#MongoDB中更新时的一些表达式]]

运行这个方法之后,可以看到这三个人的年龄更新了:

$ db.getCollection('testdata').find({"group_identify":"test"})

/* 1 */
{
    "_id" : ObjectId("61f24699c8d32bc297dbecf2"),
    "name" : "小D",
    "identify" : "ddd",
    "age" : 12.0,
    "group_identify" : "test"
}

/* 2 */
{
    "_id" : ObjectId("61f24699c8d32bc297dbecf3"),
    "name" : "小E",
    "identify" : "eee",
    "age" : 15.0,
    "group_identify" : "test"
}

/* 3 */
{
    "_id" : ObjectId("61f24699c8d32bc297dbecf4"),
    "name" : "小F",
    "identify" : "fff",
    "age" : 18.0,
    "group_identify" : "test"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 查询

# 查询单条

// FindOne 查询单条
func FindOnd() {
	var result bson.M
	table := DB.Collection("testdata")
	err := table.FindOne(context.TODO(), bson.M{"title": "test"}).Decode(&result)  //
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	}
	fmt.Printf("%v\n", result)
	v, err := encoder.Encode(result, encoder.SortMapKeys)
	if err != nil {
		fmt.Printf("%v\n", err)
	}
	fmt.Println(string(v))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 利用 ID 查询

// FindOndAsId 基于ID查询单条
func FindOndAsId() {
	var result bson.M
	table := DB.Collection("user")

	objid, err := primitive.ObjectIDFromHex("61f0f6d6c8d32bc297dbecd6")
	if err != nil {
		fmt.Printf("obj id failed: %v\n", err)
	}

	err = table.FindOne(context.TODO(), bson.M{"_id": objid}).Decode(&result)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	}
	fmt.Printf("%v\n", result)
	v, err := encoder.Encode(result, encoder.SortMapKeys)
	if err != nil {
		fmt.Printf("%v\n", err)
	}
	fmt.Println(string(v))
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 模糊查询


type Article struct {
	Title string
	Text  string
}

// FindAsLike 模糊查询
func FindAsLike() {
	table := DB.Collection("testdata")
	findOptions := options.Find()

	filter := bson.D{}
	filter = append(filter, bson.E{
		Key:   "title",
		Value: bson.M{"$regex": primitive.Regex{Pattern: ".*" + "a" + ".*", Options: "i"}}}) //i 表示不区分大小写

	cus, err := table.Find(ctx, filter, findOptions)
	if err != nil {
		fmt.Printf("find failed: %v\n", err)
	}

	defer func(cus *mongo.Cursor, ctx context.Context) {
		err := cus.Close(ctx)
		if err != nil {
			return
		}
	}(cus, ctx)

	list := make([]*Article, 0)
	for cus.Next(ctx) {
		article := new(Article)
		err := cus.Decode(&article)
		if err != nil {
			fmt.Printf("decode failed: %v\n", err)
			// return nil, tools.NewMongoError(err)
		}
		list = append(list, article)
	}

	fmt.Println("results: ", list)
	for _, v := range list {
		fmt.Println(v)
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

# 查询多条

// FindMany 查询多条
func FindMany() {
	/*
		插入测试数据
		db.testdata.insert([
			{ "name" : "小A", "identify": "aaa","age":1},
			{ "name" : "小B", "identify": "bbb","age":2},
			{ "name" : "小C", "identify": "ccc","age":3},
			{ "name" : "小D", "identify": "ddd","age":4},
			{ "name" : "小E", "identify": "eee","age":5},
			{ "name" : "小F", "identify": "fff","age":6},
		])
	*/
	table := DB.Collection("testdata")
	filter := bson.D{{"age", bson.D{{"$lte", 3}}}} // 查询年龄小于等于3的,这里特别有意思,能够使用$lte这种方法,类似这样的,MongoDB还提供了很多其他的查询方法,比如$gt等等
	cursor, err := table.Find(ctx, filter)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	}

	var results []bson.M
	if err = cursor.All(context.TODO(), &results); err != nil {
		panic(err)
	}
	for _, result := range results {
		v, err := encoder.Encode(result, encoder.SortMapKeys)
		if err != nil {
			fmt.Printf("%v\n", err)
		}
		fmt.Println(string(v))
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

就像更新的时候一样,查询的时候也有一些表达式可供使用。更多参考:[[MongoDB交互时表达式与方法整理汇总#mongodb中查询的表达式介绍]]

# 替换

# 替换单条

ReplaceOne() 您可以使用该方法替换集合中的文档。这个方法针对于 cmdb 场景更加合适。

首先添加一条测试数据:

db.testdata.insert([
   { "name" : "小A", "identify": "aaa","content":"this is aaa"},
])
1
2
3

然后基于 identify 完全 replace 这条记录:

// ReplaceOne 替换单条
func ReplaceOne() {
	coll := DB.Collection("testdata")
	filter := bson.D{{"identify", "aaa"}}
	replacement := bson.D{{"name", "小A-r"}, {"identify", "aaa"}, {"content", "this is aaa replace"}}

	_, err := coll.ReplaceOne(context.TODO(), filter, replacement)
	if err != nil {
		panic(err)
	}
}
1
2
3
4
5
6
7
8
9
10
11

# 删除

# 删除单条

// DeleteOne 删除单条
func DeleteOne() {
	coll := DB.Collection("testdata")
	filter := bson.D{{"identify", "aaa"}}
	_, err := coll.DeleteOne(context.TODO(), filter)
	if err != nil {
		panic(err)
	}
}
1
2
3
4
5
6
7
8
9

# 删除多条

同样,在删除的时候,我们调用 Delete 方法,传递过滤条件,这个时候也可以删除指定过滤的条件。

添加测试数据:

db.testdata.insert([
   { "name" : "小A", "identify": "aaa","age": 1, "group_identify": "ops"},
   { "name" : "小B", "identify": "bbb","age": 2, "group_identify": "ops"},
   { "name" : "小C", "identify": "ccc","age": 3, "group_identify": "ops"},
   { "name" : "小D", "identify": "ddd","age": 4, "group_identify": "test"},
   { "name" : "小E", "identify": "eee","age": 5, "group_identify": "test"},
   { "name" : "小F", "identify": "fff","age": 6, "group_identify": "test"}
])
1
2
3
4
5
6
7
8

我们删除年龄大于 3 岁的记录:

// DeleteMany 删除多条
func DeleteMany() {
	coll := DB.Collection("testdata")
	filter := bson.D{{"age", bson.D{{"$gt", 3}}}}
	_, err := coll.DeleteMany(context.TODO(), filter)
	if err != nil {
		panic(err)
	}
}
1
2
3
4
5
6
7
8
9

然后可以看到只剩下前三条了:

$ db.getCollection('testdata').find({})
/* 1 */
{
    "_id" : ObjectId("61f24a4dc8d32bc297dbed02"),
    "name" : "小A",
    "identify" : "aaa",
    "age" : 1.0,
    "group_identify" : "ops"
}

/* 2 */
{
    "_id" : ObjectId("61f24a4dc8d32bc297dbed03"),
    "name" : "小B",
    "identify" : "bbb",
    "age" : 2.0,
    "group_identify" : "ops"
}

/* 3 */
{
    "_id" : ObjectId("61f24a4dc8d32bc297dbed04"),
    "name" : "小C",
    "identify" : "ccc",
    "age" : 3.0,
    "group_identify" : "ops"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 批量

BulkWrite()您可以使用该方法对集合执行批量写入操作 。

官方给的示例如下:

coll := DB.Collection("testdata")
models := []mongo.WriteModel{
	mongo.NewReplaceOneModel().SetFilter(bson.D{{"title", "Record of a Shriveled Datum"}}).
		SetReplacement(bson.D{{"title", "Dodging Greys"}, {"text", "When there're no matches, no longer need to panic. You can use upsert"}}),
	mongo.NewUpdateOneModel().SetFilter(bson.D{{"title", "Dodging Greys"}}).
		SetUpdate(bson.D{{"$set", bson.D{{"title", "Dodge The Greys"}}}}),
}
opts := options.BulkWrite().SetOrdered(true)

results, err := coll.BulkWrite(context.TODO(), models, opts)
1
2
3
4
5
6
7
8
9
10
  • 匹配一个文档,其中title是“收缩基准的记录”并将其替换为新文档
  • 匹配其中title为“Dodging Greys”的文档并将该值更新为'Dodge The Greys'

通常不太会用到这种批量的方式。

# 汇总

# 两种方式

  • EstimatedDocumentCount():获得集合中文档数量的近似值
  • CountDocuments():获得集合中文档的确切数量
// Count 查询数量
func Count() {
	coll := DB.Collection("testdata")
	filter := bson.D{{"group_identify", "test"}}

	estCount, estCountErr := coll.EstimatedDocumentCount(context.TODO())
	if estCountErr != nil {
		panic(estCountErr)
	}
	count, err := coll.CountDocuments(context.TODO(), filter)
	if err != nil {
		panic(err)
	}
	fmt.Println(estCount, count)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 关联

# 关联查询

基于 MongoDB 原始语句的查询操作流程参考:[[MongoDB关联查询的学习以及实践]]

现在 golang 中使用如下方式实现关联查询:

func Aggregate() {
	query := []bson.M{{
		"$lookup": bson.M{
			"from":         "user",
			"localField":   "identify",
			"foreignField": "groupIdentify",
			"as":           "output",
		}}}
	coll := DB.Collection("group")
	cur, err := coll.Aggregate(context.TODO(), query)
	if err != nil {
		fmt.Printf("aggregate failed:%v\n", err)
	}
	defer cur.Close(context.TODO())
	for cur.Next(context.TODO()) {
		// 当数据没有映射到结构体时,可以通过map查询
		one := make(map[string]interface{})
		err := cur.Decode(&one)

		if err != nil {
			fmt.Printf("%v\n", err)
		}
		v, err := encoder.Encode(one, encoder.SortMapKeys)
		if err != nil {
			fmt.Printf("%v\n", err)
		}
		fmt.Println(string(v))
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

运行结果如下:

$ go run main.go | jq
{
  "_id": "61f0f6c2c8d32bc297dbecd2",
  "identify": "yunweizu",
  "name": "运维组",
  "output": [
    {
      "_id": "61f0f6d6c8d32bc297dbecd4",
      "groupIdentify": "yunweizu",
      "identify": "aaa",
      "name": "小A"
    },
    {
      "_id": "61f0f6d6c8d32bc297dbecd5",
      "groupIdentify": "yunweizu",
      "identify": "bbb",
      "name": "小B"
    },
    {
      "_id": "61f0f6d6c8d32bc297dbecd6",
      "groupIdentify": "yunweizu",
      "identify": "ccc",
      "name": "小C"
    }
  ]
}
{
  "_id": "61f0f6c2c8d32bc297dbecd3",
  "identify": "kefuzu",
  "name": "客服组",
  "output": [
    {
      "_id": "61f0f6d6c8d32bc297dbecd7",
      "groupIdentify": "kefuzu",
      "identify": "ddd",
      "name": "小D"
    },
    {
      "_id": "61f0f6d6c8d32bc297dbecd8",
      "groupIdentify": "kefuzu",
      "identify": "eee",
      "name": "小E"
    },
    {
      "_id": "61f0f6d6c8d32bc297dbecd9",
      "groupIdentify": "kefuzu",
      "identify": "fff",
      "name": "小F"
    }
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

如果想要过滤指定分组,则用如下方式:

func Aggregate() {
	query := []bson.M{{
		"$lookup": bson.M{
			"from":         "user",
			"localField":   "identify",
			"foreignField": "groupIdentify",
			"as":           "output",
		}},
		{"$match": bson.M{"identify": "yunweizu"}},
	}
	coll := DB.Collection("group")
	cur, err := coll.Aggregate(context.TODO(), query)
	if err != nil {
		fmt.Printf("aggregate failed:%v\n", err)
	}
	defer cur.Close(context.TODO())
	for cur.Next(context.TODO()) {
		// 当数据没有映射到结构体时,可以通过map查询
		one := make(map[string]interface{})
		err := cur.Decode(&one)

		if err != nil {
			fmt.Printf("%v\n", err)
		}
		v, err := encoder.Encode(one, encoder.SortMapKeys)
		if err != nil {
			fmt.Printf("%v\n", err)
		}
		fmt.Println(string(v))
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

运行结果如下:

$ go run main.go | jq
{
  "_id": "61f0f6c2c8d32bc297dbecd2",
  "identify": "yunweizu",
  "name": "运维组",
  "output": [
    {
      "_id": "61f0f6d6c8d32bc297dbecd4",
      "groupIdentify": "yunweizu",
      "identify": "aaa",
      "name": "小A"
    },
    {
      "_id": "61f0f6d6c8d32bc297dbecd5",
      "groupIdentify": "yunweizu",
      "identify": "bbb",
      "name": "小B"
    },
    {
      "_id": "61f0f6d6c8d32bc297dbecd6",
      "groupIdentify": "yunweizu",
      "identify": "ccc",
      "name": "小C"
    }
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

基本上和写原生语句差不多了。

# 字段

# 添加字段

# 添加单条

单个字段利用 $push指令进行添加:

// UpdateOneField 添加单个字段
func UpdateOneField() {
	coll := DB.Collection("testdata")

	objid, err := primitive.ObjectIDFromHex("62159551120b25bd2c801b09")
	if err != nil {
		fmt.Printf("%v\n", err)
	}
	filter := bson.M{"_id": objid}

	updata := bson.M{"$push": bson.M{"link_data": bson.M{"field_identify": "1", "model_data_id": "5"}}}

	_, err = coll.UpdateOne(ctx, filter, updata)
	if err != nil {
		panic(err)
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

添加之后效果如下:

/* 1 */
{
    "_id" : ObjectId("62159551120b25bd2c801b09"),
    "title" : "Record of a Shriveled Datum",
    "text" : "No bytes, no problem. Just insert a document, in MongoDB",
    "link_data" : [
        {
            "field_identify" : "1",
            "model_data_id" : "5"
        },
        }
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13

注意这里使用$push有一个问题就是,如果一个值重复往一个数组里添加,那么在 MongoDB 中会重复添加,效果如下:

$ db.getCollection('test').find({})
/* 1 */
{
    "_id" : ObjectId("61ef6e9d78a349e08e8f50da"),
    "test" : "test"
}

$ db.test.update({ "test" : "test" },{ $push: { label_list: "1" } })
再运行一次
$ db.test.update({ "test" : "test" },{ $push: { label_list: "1" } })

然后查询

$ db.getCollection('test').find({})
/* 1 */
{
    "_id" : ObjectId("61ef6e9d78a349e08e8f50da"),
    "test" : "test",
    "label_list" : [
        "1",
        "1"
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

此时可以改用$addToSet,功效与$push一致,只不过$addToSet会自动检测,如果要添加的值已在列表,就不会重复添加:

$ db.getCollection('test').find({})
/* 1 */
{
    "_id" : ObjectId("61ef6e9d78a349e08e8f50da"),
    "test" : "test"
}

$ db.test.update({ "test" : "test" },{ $addToSet: { label_list: "1" } })
再运行一次
$ db.test.update({ "test" : "test" },{ $addToSet: { label_list: "1" } })

然后查询

$ db.getCollection('test').find({})
/* 1 */
{
    "_id" : ObjectId("61ef6e9d78a349e08e8f50da"),
    "test" : "test",
    "label_list" : [
        "1"
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

可以看到这里虽然同样执行了两次,但是数据只会插入一次,适合那种进关联一次的场景。

# 添加多条

多个字段利用 $push和$each结合的方式进行添加,each 允许我们添加一个数组的内容:

// UpdateOneField 添加单个字段数据
func UpdateOneField() {
	coll := DB.Collection("testdata")

	objid, err := primitive.ObjectIDFromHex("62159551120b25bd2c801b09")
	if err != nil {
		fmt.Printf("%v\n", err)
	}
	filter := bson.M{"_id": objid}
	linkData := []map[string]string{
		{
			"field_identify": "eryajf_guanliandd",
			"model_data_id":  "6215aaf220ea934fb727096c",
		},
		{
			"field_identify": "eryajf_guanliandd",
			"model_data_id":  "6215aaf220ea934fbaaaaaaa",
		},
	}
	updata := bson.M{"$push": bson.M{"link_data": bson.M{"$each": linkData, "$position": 0}}}
	_, err = coll.UpdateOne(ctx, filter, updata)
	if err != nil {
		panic(err)
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

添加之后效果如下:

/* 1 */
{
    "_id" : ObjectId("62159551120b25bd2c801b09"),
    "title" : "Record of a Shriveled Datum",
    "text" : "No bytes, no problem. Just insert a document, in MongoDB",
    "link_data" : [
        {
            "field_identify" : "eryajf_guanliandd",
            "model_data_id" : "6215aaf220ea934fb727096c"
        },
        {
            "field_identify" : "eryajf_guanliandd",
            "model_data_id" : "6215aaf220ea934fbaaaaaaa"
        }
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

同样,这里使用$push存在一个问题,就是相同的数据,多次添加的时候,会多次存储,因此这里可以借助$addToSet的能力,实现自动去重的效果。

关于$push与$addToSet的异同,详情可参考这篇:golang 使用$push和$addToSet 往数组添加字段的异同 (opens new window)

# 删除字段

# 删除单条

单个字段利用 $pull指令进行删除:

// DeleteOneField 删除单个字段数据
func DeleteOneField() {
	coll := DB.Collection("testdata")

	objid, err := primitive.ObjectIDFromHex("62159551120b25bd2c801b09")
	if err != nil {
		fmt.Printf("%v\n", err)
	}
	filter := bson.M{"_id": objid}

	updata := bson.M{"$pull": bson.M{"link_data": bson.M{"field_identify": "1", "model_data_id": "5"}}}

	_, err = coll.UpdateOne(ctx, filter, updata)
	if err != nil {
		panic(err)
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 删除多条

删除多条记录利用$pullAll指令进行删除:

// DeleteManyField 删除多条记录
func DeleteManyField() {
	coll := DB.Collection("testdata")

	objid, err := primitive.ObjectIDFromHex("62159551120b25bd2c801b09")
	if err != nil {
		fmt.Printf("%v\n", err)
	}
	filter := bson.M{"_id": objid}
	linkData := []map[string]string{
		{
			"field_identify": "eryajf_guanliandd",
			"model_data_id":  "6215aaf220ea934fb727096c",
		},
		{
			"field_identify": "eryajf_guanliandd",
			"model_data_id":  "6215aaf220ea934fbaaaaaaa",
		},
	}
	updata := bson.M{"$pullAll": bson.M{"link_data_testa": linkData}}
	_, err = coll.UpdateOne(ctx, filter, updata)
	if err != nil {
		panic(err)
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
微信 支付宝
#MongoDB
上次更新: 2024/06/13, 22:13:45
利用cobra库快速开发类似kubectl一样的命令行工具
再探-利用gorm自身提供的方法实现MySQL中数据关联的能力

← 利用cobra库快速开发类似kubectl一样的命令行工具 再探-利用gorm自身提供的方法实现MySQL中数据关联的能力→

最近更新
01
记录二五年五一之短暂回归家庭
05-09
02
学习周刊-总第210期-2025年第19周
05-09
03
学习周刊-总第209期-2025年第18周
05-03
更多文章>
Theme by Vdoing | Copyright © 2017-2025 | 点击查看十年之约 | 浙ICP备18057030号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式