使用 BSON
Overview
在本指南中,你可以了解 Go 驱动程序如何处理 BSON 和 Go 类型之间的转换。将 GO 类型转换为 BSON 的过程称为编组,反之称为解组。
以下部分解释 Go 驱动程序如何表示 BSON 数据 以及您如何调整默认编组和解组行为。
数据类型
MongoDB 采用名为 BSON 的二进制表示形式存储文档,可以轻松灵活地进行数据处理。
Go 驱动程序提供了四类主要驱动程序来处理 BSON 数据:
D
:BSON 文档的有序表示(切片)M
:BSON 文档的无序表示(映射)A
:BSON 数组的有序表示形式E
:D 类型内部的单个元素
以下示例演示如何使用 bson.D
类型构造查询筛选器,匹配其中 quantity
字段值大于 100 的文档:
filter := bson.D{{"quantity", bson.D{{"$gt", 100}}}}
如需进一步了解 Go 驱动程序如何处理 BSON 数据,请参阅 bson 程序包 API 文档。
结构体标记
在 Go 中,结构体是具有声明数据类型的数据字段的集合。您可以使用结构体标记修改结构体字段的默认编组和解组行为,结构体标记是附加到结构体字段的可选元数据片段。结构体标记最常见的用途是指定 BSON 文档中与结构体字段对应的字段名称。下表给出可以在 Go 驱动程序中使用的其他结构体标记:
结构标记 | 说明 |
---|---|
omitempty | 如果字段设置为与字段类型对应的零值,则不会对字段编组。
|
minsize | 如果该字段的类型为 int64 、uint 、uint32 或 uint64 ,并且该字段的值适合采用有符号 int32 来表示,则该字段将被序列化为 BSON int32 而不是BSON int64 。如果字段值不适合采用有符号 int32 来表示,则忽略此标签。 |
truncate | 如果字段类型是非浮点数字类型,
则解组到该字段中的 BSON double 将在小数点处截断。 |
inline | 如果字段类型是结构或映射字段,则字段在编组时将扁平化,
而在解组时将取消扁平化。 |
如果您不指定结构体标记,则 Go 驱动程序将使用以下规则编组结构体:
驱动程序仅编组和解组已导出的字段。
驱动程序使用相应结构体字段的小写字母生成 BSON 密钥。
此驱动程序会将嵌入的结构字段封送为子文档。每个键 均为该字段的小写类型表示法。
如果指针不为 nil,则驱动程序会将指针字段作为底层类型。 如果指针为 nil,则驱动程序将其编组为 BSON null 值。
解组时,Go 驱动程序会针对
interface{}
类型的字段遵循这些 D/M 类型映射。驱动程序将 BSON 文档解组为D
类型,解组到interface{}
字段中。
以下示例展示了 Go 驱动程序如何使用各种结构体标签对结构体编组:
type Address struct { Street string City string State string } type Student struct { FirstName string `bson:"first_name,omitempty"` LastName string `bson:"last_name,omitempty"` Address Address `bson:"inline"` Age int } coll := client.Database("db").Collection("students") address1 := Address{ "1 Lakewood Way", "Elwood City", "PA" } student1 := Student{ FirstName : "Arthur", Address : address1, Age : 8} _, err = coll.InsertOne(context.TODO(), student1)
相应的 BSON 表示形式如下:
{ "_id" : ObjectId("..."), "first_name" : "Arthur", "street" : "1 Lakewood Way", "city" : "Elwood City", "state" : "PA", "age" : 8 }
本示例中,结构体标签使驱动程序:
设置自定义 BSON 字段名,例如
first_name
省略空
LastName
字段扁平化嵌套结构体,将所有字段提升到顶层
以下示例展示了 Go 驱动程序如何在不使用任何结构体标签的情况下对结构体编组:
type Address struct { Street string City string State string } type Student struct { FirstName string LastName string Address Address Age int } coll := client.Database("db").Collection("students") address1 := Address{ "1 Lakewood Way", "Elwood City", "PA" } student1 := Student{ FirstName : "Arthur", Address : address1, Age : 8} _, err = coll.InsertOne(context.TODO(), student1)
相应的 BSON 表示形式如下:
{ "_id" : ObjectId("..."), "firstname" : "Arthur", "lastname" : "", "address": { "street" : "1 Lakewood Way", "city" : "Elwood City", "state" : "PA" }, "age" : 8 }
如果没有结构体标记,驾驶员将:
将结构体字段的小写字母设置为 BSON 字段名称
包含空的
lastname
字段将
Address
字段存储为嵌套值
BSON 选项
可以指定 BSON 选项来调整 Client
实例的编组和解组行为。要在 Client
上设置 BSON 选项,请创建并配置 BSONOptions
实例。
此示例将执行以下动作:
通过配置以下设置来创建
BSONOptions
实例:将
UseJSONStructTags
字段设置为true
,这指示驱动程序在未指定"bson"
结构标记的情况下使用"json"
结构标记将
NilSliceAsEmpty
字段设置为true
,指示驱动程序将nil
Go 切片编组为空 BSON 数组
将
BSONOptions
实例传递给SetBSONOptions()
辅助方法,以指定ClientOptions
实例创建
Client
以应用指定的 BSON 封送和拆收行为
bsonOpts := &options.BSONOptions { UseJSONStructTags: true, NilSliceAsEmpty: true, } clientOpts := options.Client(). ApplyURI("<connection string>"). SetBSONOptions(bsonOpts) client, err := mongo.Connect(context.TODO(), clientOpts)
提示
如要进一步了解 BSONOptions
类型,请参阅 BSONOptions API 文档。有关指定 BSONOptions
实例并使用这些选项来创建客户端的示例,请参阅 Connect() BSONOptions 示例。
解组
您可以对 FindOne
方法或任何 *mongo.Cursor
实例的结果运用 Decode()
方法来解组 BSON 文档。
Decode()
方法返回 error
类型,其中包含以下值之一:
nil
如果文档与查询相匹配,并且检索和解组该文档时没有出错。如果驱动程序检索了您的文档但无法取消封送结果,
Decode()
方法将返回取消封送错误。如果在执行
FindOne()
方法期间检索文档出错,该错误将传播到Decode()
方法,并且Decode()
方法会返回该错误。
用于 FindOne()
方法返回的 SingleResult
类型时,如果没有文档与查询筛选器匹配,Decode()
也可能会返回 ErrNoDocuments
错误。
以下示例演示了如何使用 Decode()
方法解组并读取简单 FindOne()
操作的结果:
coll := client.Database("db").Collection("students") filter := bson.D{{"age", 8}} var result bson.D err := coll.FindOne(context.TODO(), filter).Decode(&result) fmt.Println(result)
此外,Cursor
类型还会使用 All()
方法,而此方法会将游标中存储的所有文档同时拆收到数组中。
bson
包包括一系列 Marshal()
和 Unmarshal()
方法,这些方法可处理 []byte
类型的 BSON 编码数据。
以下代码演示如何使用 bson
包中的方法将 BSON 解组回用户定义的结构体:
type Item struct { Category string Quantity int32 } doc, err := bson.Marshal(bson.D{{"category", "plate"}, {"quantity", 6}}) var test Item err = bson.Unmarshal(doc, &test) fmt.Printf("Unmarshalled Struct:\n%+v\n", test)
注意
您可以使用Raw
类型从BSON文档字节切片中检索元素,而无需将其解组为Go类型。 如果您需要在不解组整个BSON文档的情况下查找单个元素,这会非常有用。
要了解结合 Cursor
类型使用的编组和解组方法,请参阅游标Cursor API 文档
要了解有关 bson
包中的编组和解组方法的更多信息,请参阅 bson API 文档