与 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; }
拥有BSON文档
此类型表示实际的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;
非所有者BSON文档(视图)
此类型是针对所有者 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());
可选所有者BSON文档(view_or_value)
许多驱动程序方法都接受一个 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
类型还有助于缓解下一节中讨论的一些生命周期问题。
BSON 文档生命周期
必须保证 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!!
打印 BSON 文档
bsoncxx 库附带一个便利方法,将 BSON 文档转换为字符串以方便检查
bsoncxx::document::value = document{} << "I am" << "a BSON document" << finalize; std::cout << bsoncxx::to_json(doc.view()) << std::endl;
存在一个类似的方法,from_json(),用于从现有的 JSON 字符串构建 document::values。
从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 类型
《BSON 规范》提供支持的类型列表。这些类型在 C++ 中使用 b_xxx 类型封装器表示。
一些 BSON 类型不一定有原生表示形式,而是通过特殊类实现。
十进制128
bsoncxx::decimal128
类表示一个 128 位的 IEEE 754-2008 十进制浮点值。我们期望用户将这些值转换为字符串,反之亦然,但如果用户需要将其转换为本地 decimal128 类型,则提供对高 64 位和低 64 位值的访问。
您可以在这个示例中看到如何使用 bsoncxx::decimal128
。