MongoDB(二)在Go中使用MongoDB原来这么简单

MongoDB(二)在Go中使用MongoDB原来这么简单

上一节简单介绍了MongoDB,以及MongoDB在shell命令中有一个基本的了解。

那么本篇内容将讲述在Go中如何使用MongoDB。

作者:lomtom

个人网站:lomtom.cn 🔗

个人公众号:博思奥园 🔗

你的支持就是我最大的动力。

MongoDB系列:

  1. MongoDB(一)初识MongoDB
  2. MongoDB(二)在Go中使用MongoDB原来这么简单
  3. MongoDB(三)数据模型

BSON

在使用之前,你必须得明白什么是BSON,以及在Go中如何使用BSON,因为在使用MongoDB,你基本离不开BSON

在第一节中,提到过BSON,BSON是JSON的二进制格式。

BSON支持多种类型:

  • string
  • integer(32或64位)
  • double
  • decimal128
  • date
  • byte array(二进制数组)
  • bool
  • null
  • BSON对象
  • BSON数组
  • JavaScript代码
  • MD5 🔗二进制数据
  • 正则表达式 🔗

Gomongo-driver/bson包下,有一个bson.go,里面描述了MongoDB中关于BSON得几种格式。

type D = primitive.D

type E = primitive.E

type M = primitive.M

type A = primitive.A

第一种bson.D:D(Document)格式代表了一个BSON文档。也是使用比较多的一种格式。

bson.D{
    {"foo", "bar"},
    {"hello", "world"},
    {"pi", 3.14159},
}

每一对键值都需要用大括号括起来使用,括号内逗号前为key,逗号后为value。

第二种bson.E:E(Element)格式代表了一个BSON文档的一个元素,通常在D内进行使用。

type D []E

type E struct {
	Key   string
	Value interface{}
}

点进D的源码进行查看,一个D就是由多个E构成。

所以之前的可以表示为:

bson.D{
  bson.E{Key: "foo", Value: "bar"},
  bson.E{Key: "hello", Value: "world"},
  bson.E{Key: "pi", Value: 3.14159},
}

第三种bson.M:M(Map)格式也代表了一个BSON文档,与D不同的是,D是有序的,M是无序的(可以理解为Map)。

bson.M{
  "foo": "bar",
  "hello": "world", 
  "pi": 3.14159,
}

M格式与D格式区别:

  1. D是有序的,M是无序的。
  2. D的没对键值需要用大括号括起来,M不需要。
  3. D的键值对用,隔开,M使用进行分隔

第四种bson.A:A(Array)格式代表了一个BSON数组。

bson.A{
  "bar",
  "world",
  3.14159,
  bson.D{{"qux", 12345}},
}

MongoDB驱动

安装驱动

如果需要在Go中使用MongoDB,则需要用到连接MongoDB的驱动,而MongoDB刚好提供了关于Go中使用MongoDB驱动 🔗

go get go.mongodb.org/mongo-driver/mongo

在项目中导入包依赖go.mongodb.org/mongo-driver

建立连接

  1. 简单连接

最简单的建立连接就是

// 建立连接
client, err := mongo.Connect(ctx,
                         		 options.Client().
                                 // 连接地址
                                 ApplyURI("mongodb://ip:port"))

就可以获取MongoDB的连接,将其中的ipport换成你自己的地址和端口即可。

  1. 连接时验证身份

如果你需要验证身份(废话),可以使用options.Credential来设置验证参数。

// 建立连接
client, err := mongo.Connect(ctx,
                         		 options.Client().
                                 // 连接地址
                                 ApplyURI("mongodb://ip:port").
                                 // 设置验证参数
                                 SetAuth(
                                    options.Credential{
                                        // 用户名
                                        Username: "root",
                                        // 密码
                                        Password: "123456",
                                    }))

当然,也可以在URI直接拼接验证参数,例如:

// 建立连接
client, err := mongo.Connect(ctx,
                         		 options.Client().
                                 // 连接地址
                             		 ApplyURI("mongodb://root:123456@ip:port"))
  1. 设置连接池

当然,我们也可以设置连接池的大小,即最大连接数。

// 建立连接
client, err := mongo.Connect(ctx,
                             options.Client().
                                 // 连接地址
                                 ApplyURI("mongodb://ip:port").
                                 // 验证参数
                                 SetAuth(
                                    options.Credential{
                                        // 用户名
                                        Username: "root",
                                        // 密码
                                        Password: "123456",
                                    }).
                                // 设置连接数
                                SetMaxPoolSize(20))

全局调用

能建立连接远远是不够的,我们又不可能每次使用都进行一次初始化,所以可以选择将连接当做一个初始化方法,在项目启动时初始化连接,使用的时候调用即可。

首先,建立全局变量,可以根据包访问权限对连接进行控制。

var client *mongo.Client

使用init方法,对client进行初始化。

在初始化后,可以测试连接是否正常。

// 测试连接
err = conn.Ping(ctx, readpref.Primary())
if err != nil {
    log.Println(err)
    return
}

最终,init方法是这样的。

func init() {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    // 建立连接
    conn, err := mongo.Connect(ctx,
                               options.Client().
                               // 连接地址
                               ApplyURI("mongodb://ip:port").
                               // 设置验证参数
                               SetAuth(
                                 options.Credential{
                                   // 用户名
                                   Username: "root",
                                   // 密码
                                   Password: "123456",
                                 }).
                               // 设置连接数
                               SetMaxPoolSize(20))
    if err != nil {
        log.Println(err)
        return
    }
    // 测试连接
    err = conn.Ping(ctx, readpref.Primary())
    if err != nil {
        log.Println(err)
        return
    }
    log.Println("connect success!!!")
    client = conn
}

接下来,就可以愉快在Go中玩耍啦。

当然,你也可以参考 Go(三)Go配置文件 采用配置文件的方式,进行MongoDB的连接参数的配置。

Go中的CURD

上一节已经介绍MongoDB 关于CURD的一些细节,那么,本节将着重于代码层面。

MongoDB驱动提供的方式与shell命令基本保持一致,所以在Go中使用非常简单。

在这之前,我们需要明白怎么选择数据库与集合的:

// 获取 test 数据库下 demo 集合的处理器
collection := client.Database("test").Collection("demo")

操作与shell不同的是需要将上下文(即context.Contxet)作为参数传入,当然这是Go后来规定的规则。

插入操作

插入单条

type Demo struct {
	Id   string `json:"id" bson:"_id"`
	Name string `json:"name" bson:"name"`
}

func TestInsertOne(t *testing.T) {
	// 延迟关闭连接
	defer func(client *mongo.Client, ctx context.Context) {
		err := client.Disconnect(ctx)
		if err != nil {
			log.Println(err)
		}
	}(client, context.Background())
	// 获取 test 数据库下 demo 集合的处理器
	collection := client.Database("test").Collection("demo")
	// 插入单条
	one, err := collection.InsertOne(context.Background(), &Demo{
		Id:   "1",
		Name: "lomtom",
	})
	if err != nil {
		return
	}
	log.Println(one.InsertedID)
}

第一次,先贴上完整的函数,后续只需要将collection操作进行更换即可。

其中的&Demo可以用bson.Dbson.M代替

bson.D{
		{"_id",   "1"},
		{"name","lomtom"},
	}

bson.M{
		"_id": "1",
		"name":"lomtom",
	}

插入多条

insertMany, err := collection.InsertMany(context.Background(), []interface{}{
  Demo{
    Id:   "2",
    Name: "lomtom",
  },
  Demo{
    Id:   "3",
    Name: "lomtom",
  },
}

在这里需要注意[]intrface{}的用法,区别:任意类型的切片/切片内可以存放任意值,当然也可以使用bson.A来代替:

bson.A{
  Demo{
    Id:   "2",
    Name: "lomtom",
  },
  Demo{
    Id:   "3",
    Name: "lomtom",
  },
}

更新操作

更新单条

update, err := collection.UpdateOne(context.Background(), bson.D{
		{"name", "lomtom"},
	}, bson.D{
		{"$set", bson.D{
			{"name", "lomtom1"},
		}},
	})

上一节有说过,updateOneupdateManyreplaceOne的区别,就是需不需要使用 更新运算符 🔗的区别。

所以说如果没有加$set,而被你写成了这样:

	update, err := collection.UpdateOne(context.Background(), bson.D{
		{"name", "lomtom"},
	}, bson.D{
		{"name", "lomtom1"},
	})

那么,就会提示:

update document must contain key beginning with '$'

意思就是让你必须使用更新运算符。

当然,上一节还提过使用更新操作来达到插入的操作,即设置参数options.Update().SetUpsert(true)

	update, err := collection.UpdateOne(context.Background(), bson.D{
		{"name", "lomtom"},
	}, bson.D{
		{"$set", bson.D{
			{"name", "lomtom1"},
		}},
	},options.Update().SetUpsert(true))

更新多条

update, err := collection.UpdateMany(context.Background(), bson.D{
		{"name", "lomtom"},
	}, bson.D{
		{"$set", bson.D{
			{"name", "lomtom1"},
		}},
	})

替换单条

update, err := collection.ReplaceOne(context.Background(), bson.D{
		{"name", "lomtom"},
	}, bson.D{
		{"name", "lomtom1"},
	})

删除操作

删除单条

	update, err := collection.DeleteOne(context.Background(), bson.D{
		{"name", bson.D{
			{"$in", []string{"lomtom", "lomtom1"}},
		}}})

删除多条

	delete, err := collection.DeleteMany(context.Background(), bson.D{
		{"name", bson.D{
			{"$in", []string{"lomtom", "lomtom1"}},
		}}})

查询操作

// 建立查询 (无查询条件)
find, err := collection.Find(context.Background(), bson.D{})
if err != nil {
  log.Println(err)
	return
}
// 遍历查询结果
for find.Next(context.Background()) {
  var demo Demo
  // 解码绑定数据
  err = find.Decode(&demo)
  if err != nil {
    log.Println(err)
		return
  }
  log.Printf("%v", demo)
}

遍历查询操作是,有Next方法判断当前文档后是否有下一个文档,Decode方法是对当前文档进行解码与绑定操作。

其他的操作与shell命令基本都大同小异,只需要注意一些细节即可。

lomtom

标题:MongoDB(二)在Go中使用MongoDB原来这么简单

作者:lomtom

链接:https://lomtom.cn/d83d54c7