Работали ли вы с NoSQL базами данных в C++ проектах?

«Работали ли вы с NoSQL базами данных в C++ проектах?» — вопрос из категории Базы данных, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, интегрировал различные NoSQL базы в C++ приложения, выбирая решение под конкретные задачи:

1. MongoDB для документоориентированного хранения:

#include <mongocxx/client.hpp>
#include <mongocxx/instance.hpp>
#include <bsoncxx/builder/stream/document.hpp>
#include <bsoncxx/json.hpp>

// Хранение конфигураций игровых объектов
struct GameObjectConfig {
    std::string id;
    std::string type;
    std::unordered_map<std::string, double> properties;
};

class GameConfigRepository {
    mongocxx::instance m_instance;
    mongocxx::client m_client;
    mongocxx::database m_db;

public:
    GameConfigRepository(const std::string& uri) 
        : m_client{mongocxx::uri{uri}}, m_db{m_client["game_db"]} {}

    GameObjectConfig LoadConfig(const std::string& objectId) {
        auto collection = m_db["game_objects"];
        auto filter = bsoncxx::builder::stream::document{}
            << "_id" << objectId << bsoncxx::builder::stream::finalize;

        auto result = collection.find_one(filter.view());
        if (!result) throw std::runtime_error("Config not found");

        auto doc = result->view();
        GameObjectConfig config;
        config.id = doc["_id"].get_string().value.to_string();
        config.type = doc["type"].get_string().value.to_string();

        // Десериализация динамических свойств
        auto props = doc["properties"].get_document().view();
        for (auto&& elem : props) {
            config.properties[elem.key().to_string()] = 
                elem.get_double().value;
        }
        return config;
    }
};

2. Redis для кэширования и pub/sub:

#include <hiredis/hiredis.h>

class SessionCache {
    redisContext* m_context;

public:
    SessionCache(const char* host, int port) {
        m_context = redisConnect(host, port);
        if (m_context == nullptr || m_context->err) {
            throw std::runtime_error("Redis connection failed");
        }
    }

    void CacheUserSession(const std::string& userId, 
                         const std::string& sessionData, 
                         int ttlSeconds) {
        auto key = "session:" + userId;
        redisReply* reply = static_cast<redisReply*>(
            redisCommand(m_context, "SET %s %s EX %d", 
                        key.c_str(), sessionData.c_str(), ttlSeconds)
        );
        freeReplyObject(reply);
    }

    ~SessionCache() { redisFree(m_context); }
};

3. Выбор базы под задачу:

  • MongoDB: Сложные документы с изменяемой схемой, агрегации
  • Redis: Кэш, очереди, счетчики, быстрый lookup
  • RocksDB (встраиваемая): Локальное key-value хранилище с persistence

Интеграционные паттерны:

  • Репозитории с интерфейсами для замены реализации
  • Connection pooling для производительности
  • Сериализация/десериализация через библиотеки вроде nlohmann/json для MongoDB
  • Асинхронные клиенты для неблокирующих операций