How to overcome the incompatibiliy of `b_null` with other types

ghz 9months ago ⋅ 96 views

How to overcome the incompatibiliy of b_null with other types in the bsoncxx library?

It's my understanding that null is the state of a value, and not the type of an value within the MongoDB ecosystem. That is an element should have a key that maps to a certain type of value, and that it's possible that value is null. But the C++ driver doesn't seem to comply with this.

What is the cleanest way to handle the possibility that an element is null using the bsoncxx library? I would think that all types should be convertible to null, but I cannot figure out if or how that would be the case. This is particularly difficult in converting std::optional types into a comparable BSON type. Below is an example of some code that I would like to work, but I haven't been able to find a reasonable work around.

template<typename T>
static auto ToBsonType(const std::optional<T>& v) {
    if (v.has_value()) {
        return ToBsonType(v.value());
    } else {
        return bsoncxx::nullopt;
    }
}

My serializer works like this, which works well for all types until I get to these nullable types:

static bsoncxx::document::value ToBsonDocument(Class class) {
    auto doc = document{} 
        << "_id" << ToBsonType(class.id())
        << "a" << ToBsonType(class.a())
        << "b" << ToBsonType(class.b())
        << finalize;
    return doc;
}

Any suggestions, or do I need to rewrite the entire API if nullible types cannot be represented as a single type?

Answers

In the MongoDB ecosystem, null is indeed considered a valid value that can be stored for any type. However, handling nullable types in BSON serialization/deserialization with the C++ driver's bsoncxx library can be a bit tricky.

The approach you've taken in your ToBsonType function is a good start, but the issue lies in converting std::optional<T> into BSON. As of my last update, the bsoncxx library doesn't directly support serializing std::optional types to BSON.

One possible approach to handle nullable types is to define your own mapping from std::optional<T> to BSON values. You can create overloaded functions for different types or use template specialization. Here's an example of how you might achieve this:

#include <bsoncxx/types.hpp>
#include <bsoncxx/types/value.hpp>
#include <optional>

bsoncxx::types::b_null ToBsonType(const std::nullopt_t&) {
    return bsoncxx::types::b_null{};
}

template <typename T>
bsoncxx::types::b_value ToBsonType(const std::optional<T>& v) {
    if (v.has_value()) {
        return ToBsonType(v.value());
    } else {
        return bsoncxx::types::b_null{};
    }
}

With this approach, you can handle nullable types like std::optional<int>, std::optional<std::string>, etc., without needing to rewrite your entire API.

Additionally, if you're using a newer version of the mongocxx and bsoncxx libraries, you might want to check their documentation or GitHub repository to see if support for serializing std::optional types has been added since my last update. If so, you may be able to simplify your code by using the built-in support.