对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。
Docs 菜单

使用 BSON

mongocxx驱动程序附带一个新的库 bsoncxx。本文将介绍此库中的一些不同类型,以及如何以及何时使用每种类型。有关更多信息和示例代码,请参阅我们的示例。

bsoncxx 库提供了四个用于构建 BSON 的接口:一次性函数、基本构建器、列表构建器和基于流的构建器。

bsoncxx::builder::basic::document bsoncxx::builder::stream::document

创建 BSON 文档和数组的各种方法都是等效的。 所有接口都将提供相同的结果,使用哪个接口的选择完全取决于美观。

创建 BSON 文档或数组的最简单方法是使用类似 JSON 的列表构建器:

// { "hello": "world" }
bsoncxx::builder::list list_builder = {"hello", "world"};
bsoncxx::document::view document = list_builder.view().get_document();

此示例显示了列表生成器的更高级用法。

“一次性”构建器在单次调用中创建文档和数组。 当不需要使用其他逻辑(例如条件或循环)来创建对象时,可以使用这些对象:

using bsoncxx::builder::basic::kvp;
// { "hello": "world" }
bsoncxx::document::value document = bsoncxx::builder::basic::make_document(kvp("hello", "world"));
using bsoncxx::builder::basic::kvp;
// { "hello" : "world" }
bsoncxx::builder::basic::document basic_builder{};
basic_builder.append(kvp("hello", "world"));
bsoncxx::document::value document = basic_builder.extract();

此示例展示了基本构建器的更高级用法。

// { "hello" : "world" }
using bsoncxx::builder::stream;
bsoncxx::document::value document = stream::document{} << "hello" << "world" << stream::finalize;

此示例显示了流构建器的更高级用法。

注意

为了正确附加每个新值,流构建器需要跟踪当前文档的状态,包括嵌套级别和附加到构建器的最新值的类型。 在此状态更改后,不得重复使用初始流构建器,这意味着如果使用流构建器跨多个语句构建文档,则必须将中间值存储在新变量中。 由于正确执行此操作很困难,并且编译器错误消息可能会令人困惑,因此不鼓励使用流构建器。 我们建议改用基本构建器或一次性构建器函数。

有时需要使用循环来构建数组。 使用基本构建器,只需在循环内调用append即可构建顶层数组:

// [ 1, 2, 3 ]
const auto elements = {1, 2, 3};
auto array_builder = bsoncxx::builder::basic::array{};
for (const auto& element : elements) {
array_builder.append(element);
}

要在循环中构建子数组,请将Lambda传递给 append(如果子数组包含在文档而不是数组中,则作为 kvp 的第二个参数):

// { "foo" : [ 1, 2, 3 ] }
using bsoncxx::builder::basic::kvp;
using bsoncxx::builder::basic::sub_array;
const auto elements = {1, 2, 3};
auto doc = bsoncxx::builder::basic::document{};
doc.append(kvp("foo", [&elements](sub_array child) {
for (const auto& element : elements) {
child.append(element);
}
}));

使用流构建器构建数组时,请务必注意在流构建器上使用<<操作符的返回类型并不统一。 要在循环中正确构建数组,当类型更改时,应将流构建器返回的中间值存储在变量中。 使用循环从流构建器构建数组的一次尝试可能如下所示:

// { "subdocs" : [ { "key" : 1 }, { "key" : 2 }, { "key" : 3 } ], "another_key" : 42 }
using namespace bsoncxx;
builder::stream::document builder{};
auto in_array = builder << "subdocs" << builder::stream::open_array;
for (auto&& e : {1, 2, 3}) {
in_array = in_array << builder::stream::open_document << "key" << e
<< builder::stream::close_document;
}
auto after_array = in_array << builder::stream::close_array;
after_array << "another_key" << 42;
document::value doc = after_array << builder::stream::finalize;
std::cout << to_json(doc) << std::endl;

注意

任何流操作的结果都应该被捕获,因此,如果您想将上述 for 循环中的单个语句拆分为多个语句,则必须捕获每个中间结果。 此外,循环体内的最后一条语句应将其结果赋回 in_array 对象,以便循环以一致的状态重新启动:

for (auto && e : {1, 2, 3}) {
auto open_state = in_array << builder::stream::open_document;
auto temp_state = open_state << "key" << e;
in_array = temp_state << builder::stream::close_document;
}

bsoncxx::document::value

该类型表示实际的 BSON 文档,即拥有数据缓冲区的文档。 可以通过调用extract()从构建器构建这些文档:

bsoncxx::document::value basic_doc{basic_builder.extract()};
bsoncxx::document::value stream_doc{stream_builder.extract()};

调用extract()后,该构建器处于移出状态,不应使用。

可以使用流构建器接口和finalize令牌在一行中创建bsoncxx::document::value 。 finalize从临时流构建器返回document::value :

// { "finalize" : "is nifty" }
bsoncxx::document::value one_line = bsoncxx::builder::stream::document{} << "finalize" << "is nifty" << bsoncxx::builder::stream::finalize;

bsoncxx::document::view

该类型是所属bsoncxx::document::value的视图。

bsoncxx::document::view document_view{document_value.view()};

document::value也会隐式转换为document::view :

bsoncxx::document::view document_view{document_value};

在性能关键型代码中,传递视图比使用值更可取,因为可以避免过多的复制。 此外,通过传递文档视图,我们还可以多次使用该文档:

// { "copies" : { "$gt" : 100 } }
auto query_value = document{} << "copies" << open_document << "$gt" << 100 << close_document << finalize;
// Run the same query across different collections
auto collection1 = db["science_fiction"];
auto cursor1 = collection1.find(query_value.view());
auto collection2 = db["cookbooks"];
auto cursor2 = collection2.find(query_value.view());

许多驱动程序方法都采用 document::view_or_value 参数,例如 run_command:

bsoncxx::document::value run_command(bsoncxx::document::view_or_value command);

此类方法可以采用document::view或document::value 。 如果传入document::value ,则必须通过右值引用传递,以便将文档的所有权转移给该方法。

document::value ping = document{} << "ping" << 1 << finalize;
// You can pass a document::view into run_command()
db.run_command(ping.view());
// Or you can move in a document::value
db.run_command(std::move(ping));

您不需要为了使用驱动程序而直接创建view_or_value类型。 它们是作为一种便捷方法提供的,允许驱动程序方法以拥有或非拥有的方式获取文档。 view_or_value类型还有助于缓解下一节中讨论的一些生命周期问题。

document::value类型的寿命必须比使用它们的任何document::view类型的寿命长。 如果底层值被清理,视图将留下一个悬空指针。 考虑一个返回新建文档视图的方法:

bsoncxx::document::view make_a_dangling_view() {
bsoncxx::builder::basic::document builder{};
builder.append(kvp("hello", "world"));
// This creates a document::value on the stack that will disappear when we return.
bsoncxx::document::value stack_value{builder.extract()};
// We're returning a view of the local value
return stack_value.view(); // Bad!!
}

此方法会返回不应使用的悬空视图:

// This view contains a dangling pointer
bsoncxx::document::view dangling_view = make_a_dangling_view(); // Warning!!

尝试通过构建器创建视图同样会创建危险的视图对象,因为不会捕获从extract()返回的临时值:

bsoncxx::builder::stream::document temp_builder{};
temp_builder << "oh" << "no";
bsoncxx::document::view dangling_view = temp_builder.extract().view(); // Bad!!

bsoncxx::to_json()

bsoncxx 库提供了一种将 BSON 文档转换为字符串以便于检查的便捷方法:

bsoncxx::document::value = document{} << "I am" << "a BSON document" << finalize;
std::cout << bsoncxx::to_json(doc.view()) << std::endl;

有一种类似的方法,from_json(),可以从现有JSON字符串构建文档::values。

警告

在将 JSON 转换为 BSON 之前验证不受信任的输入

如果将 JSON string 转换为 BSON 并在查询、更新或命令中使用生成的 BSON,攻击者可以注入操作符或意外的字段值,从而改变操作的含义。当 JSON 来源于用户、API 请求或其他不可信的来源时,这种风险最大。

To learn more about validating input before conversion, see Validate Untrusted Input Before Converting JSON to BSON.

[ ] 操作符进入 BSON 文档以检索值:

// doc_view = { "store" : "Key Foods", "fruits" : [ "apple", "banana" ] }
auto store = doc_view["store"];
auto first_fruit = doc_view["fruits"][0];

此操作会返回一个bsoncxx::document::element ,其中包含实际所需的值:

document::element store_ele{doc_view["store"]};
if (store_ele) {
// this block will only execute if "store" was found in the document
std::cout << "Examining inventory at " << to_json(store_ele.get_value()) << std::endl;
}

本示例和本示例更详细地展示了此功能。

BSON规范提供了受支持类型的列表。这些在C++中使用b_xxx类型包装器表示。

某些BSON types不一定有要包装的原生表示形式,而是通过特殊类实现的。

bsoncxx::decimal128类表示128位 IEEE 754 - 2008十进制浮点值。 我们希望用户能够将这些值与字符串相互转换,但如果用户需要转换为原生十进制128类型,则可以访问低位和高位64位值。

您可以在此示例中了解如何使用 bsoncxx::decimal128。