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 驱动程序中使用的其他结构体标记:
结构标记 | 说明 |
|---|---|
| 如果字段设置为与字段类型对应的零值,则不会对字段编组。 |
| 如果该字段的类型为 |
| 如果字段类型是非浮点数字类型, 则解组到该字段中的 BSON double 将在小数点处截断。 |
| 如果字段类型是结构或映射字段,则字段在编组时将扁平化, 而在解组时将取消扁平化。 |
如果您不指定结构体标记,则 Go 驱动程序将使用以下规则编组结构体:
驱动程序仅编组和解组已导出的字段。
驱动程序使用相应结构体字段的小写字母生成 BSON 密钥。
此驱动程序会将嵌入的结构字段封送为子文档。每个键 均为该字段的小写类型表示法。
如果指针不为 nil,则驱动程序会将指针字段作为底层类型。 如果指针为 nil,则驱动程序将其编组为 BSON null 值。
解组时,Go 驱动程序会针对
interface{}类型的字段遵循这些 D/M 类型映射。驱动程序将 BSON 文档解组为D类型,解组到interface{}字段中。
BSON 选项
可以指定 BSON 选项来调整 Client 实例的编组和解组行为。要在 Client 上设置 BSON 选项,请创建并配置 BSONOptions 实例。
此示例将执行以下动作:
通过配置以下设置来创建
BSONOptions实例:将
UseJSONStructTags字段设置为true,这指示驱动程序在未指定"bson"结构标记的情况下使用"json"结构标记将
NilSliceAsEmpty字段设置为true,指示驱动程序将nilGo 切片编组为空 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(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)
[{_id ObjectID("...")} {first_name Arthur} {street 1 Fern Way} {city Elwood City} {state PA} {age 8}]
此外,Cursor 类型还会使用 All() 方法,而此方法会将游标中存储的所有文档同时拆收到数组中。
bson包包含一系列 Marshal() 和 Unmarshal() 方法,这些方法可处理 []byte 类型的BSON编码数据。bson.Marshal() 和 bson.Unmarshal() 方法需要一个可以编码到BSON文档中的参数,例如 bson.D 类型。如果传递的类型不正确,则会发生 Write 错误。示例,如果将字符串传递给 bson.Marshal(),则会发生 WriteString 错误。
以下代码演示如何使用 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)
Unmarshalled Struct: {Category:plate Quantity:6}
注意
您可以使用 Raw 类型从BSON文档字节切片中检索元素,而无需将其解组为Go类型。此类型允许您查找单个元素,而无需解组整个BSON文档。有关如何使用 Raw 类型的更多信息,请参阅“使用原始BSON数据”部分。
要详细学习;了解用于 Cursor 类型的编组和解组方法,请参阅 Cursor API文档。
要学习;了解有关 bson包中编组和解组方法的详情,请参阅BSON API文档。
使用原始BSON数据
Go驾驶员支持通过 Raw 类型处理原始BSON文档。Raw 类型允许您验证和检索字节切片中的元素,而无需解组整个BSON文档。这在以下情况下非常有用:
在不转换整个文档的情况下对特定字段执行查找
通过避免完整文档解组来减少内存开销
直接处理从数据库收到的二进制BSON数据
Raw 类型提供了验证BSON文档和通过键查找各个元素的方法。以下示例演示了如何验证原始BSON文档并从中检索特定字段:
collection := client.Database("sample_restaurants").Collection("restaurants") findOptions := options.FindOne() var raw bson.Raw err = collection.FindOne( context.TODO(), bson.D{{"name", "Mongo's Pizza"}}, findOptions, ).Decode(&raw) if err != nil { log.Fatalf("Failed to find document: %v", err) } // Print the document type fmt.Printf("Document type: %T\n", raw) // Access a field from the raw document name := raw.Lookup("name").StringValue() fmt.Println("Restaurant name:", name)
Document type: bson.Raw Restaurant name: Mongo's Pizza
要学习;了解有关 Raw 类型系列的详情,请参阅原始BSON API文档。