Docs Menu
Docs Home
/ /

Trabajar con BSON

El controlador mongocxx incluye una nueva biblioteca, bsoncxx. Este artículo analizará algunos de los diferentes tipos de esta biblioteca y cómo y cuándo usar cada uno. Para obtener más información y ejemplos de código, consulte nuestro ejemplos

  1. Constructores de documentos

  2. Posesión de documentos BSON (valores)

  3. Documentos BSON sin propiedad (vistas)

  4. Documentos BSON de propiedad opcional (vista o valor)

  5. Duración del documento BSON

  6. Impresión de documentos BSON

  7. Cómo extraer campos de documentos BSON

  8. BSON Types

La biblioteca bsoncxx ofrece cuatro interfaces para construir BSON: funciones únicas, un constructor básico, un constructor de listas y un constructor basado en secuencias.

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

Los distintos métodos para crear documentos y matrices BSON son equivalentes. Todas las interfaces proporcionarán los mismos resultados; la elección de cuál usar es completamente estética.

La forma más sencilla de crear un documento o matriz BSON es utilizar el generador de listas similar a JSON:

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

En este ejemplose muestran usos más avanzados del generador de listas.

El constructor "One-off" crea documentos y matrices en una sola llamada. Se puede usar cuando no se requiere lógica adicional (como condicionales o bucles) para crear el objeto:

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();

En este ejemplose muestran usos más avanzados del constructor básico.

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

Otros usos avanzados del generador de streams se muestran en este ejemplo.

Nota

Para añadir correctamente cada nuevo valor, un generador de flujo debe rastrear el estado del documento actual, incluyendo el nivel de anidación y el tipo del valor más reciente agregado al generador. El generador de flujo inicial no debe reutilizarse después de que este estado cambie, lo que significa que los valores intermedios deben almacenarse en nuevas variables si se está creando un documento con el generador de flujo en varias instrucciones. Dado que hacerlo adecuadamente es difícil y los mensajes de error del compilador pueden resultar confusos, se desaconseja usar el generador de flujo. En su lugar, recomendamos utilizar el generador básico o las funciones generadoras puntuales.

A veces es necesario construir una matriz mediante un bucle. Con el constructor básico, se puede construir una matriz de nivel superior simplemente llamando a append dentro de un ciclo:

// [ 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);
}

Para construir una submatriz en un bucle, pase una lambda a append (o como el segundo argumento de kvp si la submatriz está contenida en un documento en lugar de una matriz):

// { "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);
}
}));

Al construir un arreglo con el generador de flujo, es importante tener en cuenta que el tipo de retorno al usar el operador << en un generador de flujo no es uniforme. Para construir un arreglo correctamente en un bucle, los valores intermedios devueltos por el generador de flujo deben almacenarse en variables cuando el tipo cambie. Un intento de construir un arreglo a partir de un generador de flujo usando un bucle podría verse de la siguiente manera:

// { "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;

Nota

El resultado de cualquier operación de flujo debe capturarse. Por lo tanto, si desea dividir la sentencia individual del bucle for anterior en varias sentencias, debe capturar cada resultado intermedio. Además, la última sentencia del cuerpo del bucle debe asignar su resultado al objeto in_array para que el bucle se reinicie en un estado consistente.

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::documento::valor

Este tipo representa un documento BSON real, uno que posee su propio buffer de datos. Estos documentos pueden construirse utilizando un generador llamando a extract():

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

Después de llamar a extract(), el generador se encuentra en un estado movido y no debe utilizarse.

Es posible crear un bsoncxx::document::value en una sola línea utilizando la interfaz del generador de streams y el token finalize. finalize devuelve un document::value de un generador de streams temporales:

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

bsoncxx::documento::vista

Este tipo es una vista en un bsoncxx::document::value propietario.

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

Un document::value también se convierte implícitamente en un document::view:

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

En código crítico para el rendimiento, es preferible pasar vistas a usar valores, ya que evitamos el exceso de copias. Además, pasar una vista de un documento nos permite usarlo varias veces:

// { "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());

Muchos métodos de controlador toman un document::view_or_value parámetro, por ejemplo, run_command:

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

Dichos métodos pueden aceptar un document::view o un document::value. Si se pasa un document::value , debe pasarse por referencia de valor r, por lo que se transfiere la propiedad del documento al método.

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));

No debería ser necesario crear tipos view_or_value directamente para usar el controlador. Se ofrecen como un método práctico para que los métodos del controlador puedan tomar documentos, ya sea de forma propietaria o no. El tipo view_or_value también ayuda a mitigar algunos de los problemas de duración que se describen en la siguiente sección.

Es imperativo que los tipos document::value sobrevivan a cualquier tipo document::view que los use. Si se borra el valor subyacente, la vista quedará con un puntero colgante. Considere un método que devuelve la vista de un documento recién creado:

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!!
}

Este método devuelve una vista colgante que no debe utilizarse:

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

Intentar crear una vista a partir de un generador también creará un objeto de vista peligroso, porque no se captura el valor temporal devuelto desde extract():

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

bsoncxx::a_json()

La librería bsoncxx viene con un método de conveniencia para convertir documentos BSON a cadenas para su fácil inspección:

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

Existe un método análogo, from_json(), para construir document::values ​​a partir de cadenas JSON existentes.

El operador [ ] accede a un documento BSON para recuperar valores:

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

Esto devuelve un bsoncxx::document::element, que contiene el valor real deseado:

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;
}

Esta característica se muestra con más detalle en este ejemplo y este ejemplo.

La especificación BSON proporciona una lista de tipos admitidos. Estos se representan en C++ mediante los contenedores de tipos b_xxx.

Algunos tipos BSON no necesariamente tienen una representación nativa para encapsular y se implementan a través de clases especiales.

La clase bsoncxx::decimal128 representa un valor decimal de punto flotante IEEE 754-2008 de 128bit. Se espera que los usuarios los conviertan a cadenas, pero se proporciona acceso a los valores de 64bits bajo y alto si se necesita convertir a un tipo decimal nativo128.

Puedes ver cómo trabajar con bsoncxx::decimal128 en este ejemplo.

Volver

Datos de Time Series

En esta página