Search code examples
mongodbmongo-cxx-driver

bsoncxx subarray and subdocument builders


I'm attempting to build a document that looks like this:

{ 
  "Name" : "Joe", 
  "Age" : 34, 
  "Some Array" : [ 
    { "index" : 1, "Some Var" : 12, "Flag" : false }, 
    { "index" : 2, "Some Var" : 13, "Flag" : true }, 
    { "index" : 3, "Some Var" : 14, "Flag" : false } 
    ], 
  "Status" : "Employed" 
}

and I've been somewhat successful using the streaming builders as long as I do it all in one shot like this:

using bsoncxx::builder::stream::document;
using bsoncxx::builder::stream::open_document;
using bsoncxx::builder::stream::close_document;
using bsoncxx::builder::stream::open_array;
using bsoncxx::builder::stream::close_array;
using bsoncxx::builder::stream::finalize;

bsoncxx::builder::stream::document ArrayDoc;
ArrayDoc << "Name" << "Joe"
    << "Age" << 34
    << "Some Array" << open_array << open_document
    << "index" << 1 << "Some Var" << 12 << "Flag" << false
    << close_document << open_document
    << "index" << 2 << "Some Var" << 13 << "Flag" << true
    << close_document << open_document
    << "index" << 3 << "Some Var" << 14 << "Flag" << false
    << close_document
    << close_array
    << "Status" << "Employed";

What I really need to do though, is build the subarray with a loop to fetch the various values from other data structures. When I try to break the building process into parts, I get compiler errors on the open_document

   bsoncxx::builder::stream::document ArrayDoc;
ArrayDoc << "Name" << "Joe"
    << "Age" << 34
    << "Some Array" << open_array;

for (int i = 0; i < 4; i++)
{
    ArrayDoc << open_document
        << "index" << i << "Some Var" << 12 << "Flag" << false
        << close_document;
}
ArrayDoc << close_array
    << "Status" << "Employed";

The above gets me:

error C2679: binary '<<': no operator found which takes a right-hand operand of type 'const bsoncxx::v_noabi::builder::stream::open_document_type' (or there is no acceptable conversion)

What am I doing wrong?


Solution

  • Instead of using the document stream builder, I suggest using the basic builder so you can create the different parts of the document as needed. In your case, you have several key/value pairs and an embedded array of value lists. The change in your thinking is to create subdocuments and use those to build the overall document.

    So if you have a two-dimensional array of data that will be presented as an array in your (final) document:

       int values[5][3] =
       {
          {1, 12, 1},
          {2, 13, 0},
          {3, 14, 1},
          {4, 15, 1},
          {5, 16, 0}
       };
    

    then you should build this array as it's own (intermediate) document

       bsoncxx::builder::basic::array some_array;
       for (int i = 0; i < 5; i++)
       {
          auto these_values = bsoncxx::builder::basic::document{};
          these_values.append(kvp("index", values[i][0]));
          these_values.append(kvp("Some Var", values[i][1]));
          these_values.append(kvp("Flag", (values[i][2] == 1 ? bsoncxx::types::b_bool{ true } : bsoncxx::types::b_bool{ false })));
          some_array.append(these_values);
       }
    

    Now you can use some_array as a subdocument in the overall document.

    Here is a self-contained example that runs for me:

    #include <string>
    #include <sstream>
    #include <iostream>
    
    #include <mongocxx/client.hpp>
    #include <mongocxx/instance.hpp>
    #include <bsoncxx/builder/list.hpp>
    #include <bsoncxx/json.hpp>
    #include <bsoncxx/builder/basic/document.hpp>
    #include <bsoncxx/builder/basic/helpers.hpp>
    #include <bsoncxx/builder/basic/kvp.hpp>
    #include <bsoncxx/builder/basic/array.hpp>
    #include <bsoncxx/json.hpp>
    #include <bsoncxx/string/to_string.hpp>
    #include <mongocxx/exception/exception.hpp>
    
    mongocxx::instance instance{};
    
    int main()
    {
       int values[5][3] =
       {
          {1, 12, 1},
          {2, 13, 0},
          {3, 14, 1},
          {4, 15, 1},
          {5, 16, 0}
       };
    
       using bsoncxx::builder::basic::kvp;
       using bsoncxx::builder::basic::make_array;
       using bsoncxx::builder::list;
    
       auto my_doc = bsoncxx::builder::basic::document{};
       my_doc.append(kvp("Name", "Joe"));
       my_doc.append(kvp("Age", 34));
    
       bsoncxx::builder::basic::array some_array;
       for (int i = 0; i < 5; i++)
       {
          auto these_values = bsoncxx::builder::basic::document{};
          these_values.append(kvp("index", values[i][0]));
          these_values.append(kvp("Some Var", values[i][1]));
          these_values.append(kvp("Flag", (values[i][2] == 1 ? bsoncxx::types::b_bool{ true } : bsoncxx::types::b_bool{ false })));
          some_array.append(these_values);
       }
       my_doc.append(kvp("Some Array", some_array));
       my_doc.append(kvp("Status", "Employed"));
    
       std::cout << "my_doc json: " << bsoncxx::to_json(my_doc.view()) << std::endl;
    }
    

    This gives the output

    my_doc json: { "Name" : "Joe", "Age" : 34, "Some Array" : [ { "index" : 1, "Some Var" : 12, "Flag" : true }, { "index" : 2, "Some Var" : 13, "Flag" : false }, { "index" : 3, "Some Var" : 14, "Flag" : true }, { "index" : 4, "Some Var" : 15, "Flag" : true }, { "index" : 5, "Some Var" : 16, "Flag" : false } ], "Status" : "Employed" }