This week my focus is on a parser that will build MongoDB documents and support everything MongoDB supports. I think the builder that is best for this is the stream builder. In there documentation they discourage use of it because it can be tricky to get working, but I think it is much closer to being capable of what I want to do than the basic builder. The problem that I was running into when I was trying to implement subarrays and subdocuments was compiler errors telling me I couldn't open an array or document. I tried to do some research and I came across a very helpful thread on the MongoDB JIRA. There I found out that when you stream open_array or open_document into the stream builder it returns a context that expects a relevant value based on the context.
The thread stated the importance of keeping track of the context of the operation like:
auto ctx = builder << "sub_doc" << open_document;
for(int i = 0; i < 3; i++) {
ctx << "key" << i;
}
ctx << close_document;
builder << "another_key" << 42;
auto value = builder << finalize;
// {"sub_doc": {"key": 0, "key": 1, "key": 2}, "another_key": 42}
This was an important block of code and I found myself coming back to this often to make sure I was keeping track of the context right. I had to find a way to take the context and pass it to another build document or build array function to build the subdocument and then close out of the context. This was tricky and required careful handling of the context. I originally thought it would be best to just have another helper function for adding subarrays and subdocuments, but when that would make everything a little convoluted and I wanted a simpler solution. I eventually started working on making the buildDocument function able to call itself and then a buildArray function that could call itself or buildDocument. This would all for nested arrays and documents to be handled since it will build the documents and call buildDocument every time the parser sees an open bracket.
In order to keep track of the context and allow for the buildDocument function to call itself with two different types of builders it had to use a template type. This should have worked pretty simply, but when I added the template types my code stopped compiling. It not only wouldn't compile, but it would run infinitely until I would get kicked off the VM and have to wait for VS code to be able to connect again. So I obviously knew something really bad was going on. It started happening when I tried adding the new context code. I went back to the example again and looked through the mongocxx documentation to try and see if I was just misunderstanding something.
Eventually after trying things multiple different ways I was finally able to get it to compile. The way that I was able to get it to work was anytime I came across a subdocument or subarray I had use a lambda function inside of the stream operators to insert the document or array correctly.
build << "key" << open_document << [](auto &ctx) {
buildDocument(ctx);
} << close_document;
The problem that I was having was that I could not get the proper arguments for the different context, but with this solution since the context gets passed to the buildDocument function and immediately the close_document is passed in it is very safe. It felt very good to be able to get this to work the way that I wanted it. Now I all I had to work on was getting buildPipeline to using buildDocument correctly and I would have a way to build any argument I would need for the MongoDB database operations.
Comments