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
Constructores de documentos
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.
Constructor de listas
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.
Funciones de constructor "únicas"
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"));
Constructor básico
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 flujos debe registrar el estado del documento actual, incluyendo el nivel de anidación y el tipo del valor más reciente añadido al generador. El generador de flujos inicial no debe reutilizarse tras un cambio de estado, lo que significa que los valores intermedios deben almacenarse en nuevas variables si se construye un documento con el generador de flujos en varias sentencias. Dado que realizar esto correctamente es difícil y los mensajes de error del compilador pueden ser confusos, se desaconseja el uso del generador de flujos. Recomendamos utilizar el generador básico o las funciones de construcción específicas.
Construyendo matrices en bucles
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 bucle:
// [ 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 crear un array con el generador de flujos, es importante tener en cuenta que el tipo de retorno del operador << no es uniforme. Para crear un array correctamente en un bucle, los valores intermedios devueltos por el generador de flujos deben almacenarse en variables cuando cambia el tipo. Un intento de crear un array a partir de un generador de flujos mediante un bucle podría ser similar al siguiente:
// { "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; }
Poseer documentos BSON
Este tipo representa un documento BSON real, que posee su propio búfer de datos. Estos documentos se pueden construir desde un constructor 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 usando la interfaz del generador de secuencias y el token finalize. finalize devuelve un document::value de un generador de secuencias temporal:
// { "finalize" : "is nifty" } bsoncxx::document::value one_line = bsoncxx::builder::stream::document{} << "finalize" << "is nifty" << bsoncxx::builder::stream::finalize;
Documentos BSON sin propiedad (vistas)
Este tipo es una vista de un propietario bsoncxx::document::value.
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());
Documentos BSON de propiedad opcional (vista o valor)
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);
Estos métodos pueden aceptar un valor document::view o un valor document::value. Si se pasa un valor document::value, debe pasarse por referencia de valor r, de modo que la propiedad del documento se transfiere 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.
Duración del documento BSON
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!!
Impresión de documentos BSON
La biblioteca bsoncxx viene con un método conveniente para convertir documentos BSON en cadenas para una 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.
Cómo extraer campos de documentos BSON
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.
BSON Types
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.
Decimal128
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.