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_rule
that 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::parse
namespace, they both contain adoc
declaration, and you will receive a compiler error about ambiguous name lookup. Pullbson::make::doc
with ausing
, or use the fully-qualifeid name.-
bson::document build(mlib::allocator<>)#
Construct a new
bson::document
containing 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_rule
that 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
R
will 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_rule
that 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_rule
itself, or must yield avalue_rule
when a unary dereference*
is applied (e.g. astd::optional
containing 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_view
via the first argument. The value is anyvalue_rule
given as the second argument.
-
template<value_rule V>
struct optional_pair# A special
element_rule
that inserts a key-value pair if-and-only-if the value of the candidate element evaluates to a truthy value (the value ruleV
must 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_rule
by 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
out
using the key stringkey
. It is also possible that no element will be inserted at all (e.g. theoptional_value
may 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
v
when 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
v
to the documentdoc
under 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,
}