Declaratively Building BSON Documents#
amongoc’s BSON library contains a facility for building heirarchies of BSON
objects declaratively in a single-shot. Usage of this facility is recommended
for a few reasons:
It will always “do the right thing” – Building large heirarchies of data by-hand is tedious and error-prone.
The declarative structure mimics the generated data very closely, and is easy to understand and modify.
The declarative building system is extensible.
The built document will allocate exactly the right amount, exactly once: It first assesses the required storage space before performing the build.
- <bson/make.hpp> (header file)#
Contains types used for declarative building BSON document data.
Note
Everything declared in this file lives in the bson::make namespace
High-Level Constructs#
Value Rules#
The rules here can be used to construct element values. The special doc rule
is also used to construct the root document (with doc::build()). These implement
value_rule (i.e. they can be used as the value of an element’s key-value
pair).
-
template<element_rule... Ts>
struct doc# A
value_rulethat constructs a document consisting of key-value pairs. This is also used to construct the root document (See:build()).See also
For a list of element rules that can be used to populate a document, see: Element Rules.
Note
If you pull the
bson::parsenamespace, they both contain adocdeclaration, and you will receive a compiler error about ambiguous name lookup. Pullbson::make::docwith ausing, or use the fully-qualifeid name.-
bson::document build(mlib::allocator<>)#
Construct a new
bson::documentcontaining the elements of this rule.
-
bson::document build(mlib::allocator<>)#
-
template<value_rule... Vs>
struct array# A build rule that constructs an array value within an element. Implements
value_rule.Note
This constructs a fixed-length array of elements. To construct a variable-length arrray, use
range
-
template<std::ranges::forward_range R>
requires value_rule<std::ranges::range_reference_t<R>>
struct range# A
value_rulethat creates a BSON array of values that are pulled from the given range. The range must be a range of objects that satisfyvalue_rule.Note
The given range
Rwill be iterated twice: Once to calculate the expected byte-size of the array, and a second time to actually perform the insertion.
-
template<typename V>
struct optional_value# A special
value_rulethat is used to generate conditional elements. The value must be contextually convertible tobool[1]. If the contained evaluates tofalse, then no value will be appended.The wrapped object must be a
value_ruleitself, or must yield avalue_rulewhen a unary dereference*is applied (e.g. astd::optionalcontaining avalue_rule).
Element Rules#
These build rules are used to specify the contents of a doc value rule. An
element rule can insert any number of elements into a document, but usually it
will either insert only one (i.e. pair) or zero (i.e. optional_pair).
-
template<value_rule V>
struct pair# Unconditionally adds a single key-value pair to a document. The key is given as a
std::string_viewvia the first argument. The value is anyvalue_rulegiven as the second argument.
-
template<value_rule V>
struct optional_pair# A special
element_rulethat inserts a key-value pair if-and-only-if the value of the candidate element evaluates to a truthy value (the value ruleVmust be contextually convertible tobool[1]).
Low-Level Concepts#
-
template<typename T>
concept element_rule# A concept for defining build rules that insert key-value pairs into a document.
See also
Given:
Requires:
-
template<typename T>
concept value_rule# A build rule or value that can be used as the value in a key-value pair for a document or array element.
See also
Any type that can be inserted using
bson_insert()can be used as anvalue_rule.Other types may also satisfy
value_ruleby meeting the below requirements.Given
Requires
value.byte_size() must return the number of bytes that the value will consume. This only includes bytes for the value portion of a potential element, not including the element key or type tag.
value.append_to(out, key) - May append a value to the document
outusing the key stringkey. It is also possible that no element will be inserted at all (e.g. theoptional_valuemay not actually append anything).
Note
Don’t call the above methods directly in generic contexts. Use
value_byte_size()andappend_value()instead.
-
std::size_t value_byte_size(value_rule auto v)#
Obtain the size, in bytes, of the value
vwhen appended to a document.
-
[[1]] void append_value(mutator &doc, std::string_view key, value_rule auto v)#
-
[[2]] void append_value(mutator &doc, std::size_t key, value_rule auto v)#
Append a value
vto the documentdocunder the keykey.[[2]]will automatically construct an integer key string without allocating memory.
Examples#
Create an empty document#
using namespace bson::make;
bson::document d = doc().build(alloc);
Create a document with a single key-value pair#
bson::document d = doc(pair("foo", "bar")).build(alloc);
{
foo: "bar"
}
Create a document with an empty array#
bson::document d =
doc(pair("foo", 123),
pair("bar", array()))
.build(alloc);
{
foo: 123,
bar: []
}
Create a document with an array of strings#
vector<string> strs = { "foo", "bar", "baz" };
auto d =
doc(pair("someStrings", range(strs)))
.build(alloc);
{
someStrings: [
"foo",
"bar",
"baz"
]
}
Create a find Command Document#
This is similar to the document build expression use to implement the find
operation in amongoc, where params is a set of find parameters.
bson::document command = doc(
pair("find", collection_name),
pair("$db", database_name),
pair("filter", filter),
optional_pair("sort", params->sort),
optional_pair("projection", params->projection),
optional_pair("hint", params->hint_doc),
optional_pair("hint", params->hint_str),
optional_pair("skip", params->skip),
optional_pair("limit", params->limit),
optional_pair("batchSize", params->batch_size),
// If the limit is set to a negative value, generate a single batch
pair("singleBatch", params->limit < 0),
optional_pair("comment", params->comment),
optional_pair("maxTimeMS", ::mlib_count_milliseconds(params->max_time)),
optional_pair("max", params->max),
optional_pair("min", params->min),
pair("returnKey", params->return_key),
pair("oplogReplay", params->oplog_replay),
pair("showRecordId", params->show_record_id),
pair("tailable", params->cursor_type != ::amongoc_find_cursor_not_tailable),
pair("noCursorTimeout", params->no_cursor_timeout),
pair("awaitData", bool(params->cursor_type & ::amongoc_find_cursor_tailable_await)),
pair("allowPartialResults", params->allow_partial_results),
optional_pair("collation", params->collation),
pair("allowDiskUse", params->allow_disk_use),
optional_pair("let", params->let))
.build(alloc);
Example output:
{
find: "my-collection",
$db: "mainDb",
filter: { _id: { $gt: 1 } },
batchSize: 2,
singleBatch: false,
returnKey: false,
oplogReplay: false,
showRecordId: false,
tailable: false,
noCursorTimeout: false,
awaitData: false,
allowPartialResults: false,
allowDiskUse: false,
}