| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- #pragma once
- #include <string>
- #include <vector>
- #include <chrono>
- #include <optional>
- #include <nlohmann/json.hpp>
- namespace smartbotic::database {
- /**
- * Represents a document stored in a collection.
- * Documents are JSON objects with metadata for versioning, TTL, and encryption.
- * Named Document to avoid conflict with proto-generated Document message.
- */
- struct Document {
- std::string id; // Unique document ID within collection
- std::string collection; // Collection name
- nlohmann::json data; // Document content (JSON)
- uint64_t version = 1; // Monotonic version for optimistic locking
- uint64_t createdAt = 0; // Creation timestamp (ms since epoch)
- uint64_t updatedAt = 0; // Last modification timestamp (ms since epoch)
- uint64_t expiresAt = 0; // TTL expiration (0 = no expiration)
- std::string nodeId; // Origin node for replication
- bool encrypted = false; // Whether sensitive fields are encrypted
- std::vector<std::string> encryptedFields; // List of encrypted field paths
- std::string createdBy; // User ID who created the document
- std::string updatedBy; // User ID who last updated the document
- // Check if document has expired
- [[nodiscard]] bool isExpired() const {
- if (expiresAt == 0) return false;
- auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::system_clock::now().time_since_epoch()
- ).count();
- return static_cast<uint64_t>(now) >= expiresAt;
- }
- // Set TTL in seconds from now
- void setTtlSeconds(uint32_t seconds) {
- if (seconds == 0) {
- expiresAt = 0;
- } else {
- auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::system_clock::now().time_since_epoch()
- ).count();
- expiresAt = static_cast<uint64_t>(now) + (seconds * 1000ULL);
- }
- }
- // Get remaining TTL in seconds (0 if expired or no TTL)
- [[nodiscard]] uint32_t getRemainingTtlSeconds() const {
- if (expiresAt == 0) return 0;
- auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::system_clock::now().time_since_epoch()
- ).count();
- if (static_cast<uint64_t>(now) >= expiresAt) return 0;
- return static_cast<uint32_t>((expiresAt - static_cast<uint64_t>(now)) / 1000);
- }
- // JSON serialization
- [[nodiscard]] nlohmann::json toJson() const {
- nlohmann::json j;
- j["id"] = id;
- j["collection"] = collection;
- j["data"] = data;
- j["version"] = version;
- j["createdAt"] = createdAt;
- j["updatedAt"] = updatedAt;
- j["expiresAt"] = expiresAt;
- j["nodeId"] = nodeId;
- j["encrypted"] = encrypted;
- j["encryptedFields"] = encryptedFields;
- j["createdBy"] = createdBy;
- j["updatedBy"] = updatedBy;
- return j;
- }
- // JSON deserialization
- static Document fromJson(const nlohmann::json& j) {
- Document doc;
- doc.id = j.value("id", "");
- doc.collection = j.value("collection", "");
- doc.data = j.value("data", nlohmann::json::object());
- doc.version = j.value("version", 1ULL);
- doc.createdAt = j.value("createdAt", 0ULL);
- doc.updatedAt = j.value("updatedAt", 0ULL);
- doc.expiresAt = j.value("expiresAt", 0ULL);
- doc.nodeId = j.value("nodeId", "");
- doc.encrypted = j.value("encrypted", false);
- doc.encryptedFields = j.value("encryptedFields", std::vector<std::string>{});
- doc.createdBy = j.value("createdBy", "");
- doc.updatedBy = j.value("updatedBy", "");
- return doc;
- }
- };
- /**
- * Represents a historical snapshot of a document at a specific version.
- * Stored in version history when a document is updated or deleted.
- */
- struct DocumentVersion {
- uint64_t version = 0; // Version number
- nlohmann::json data; // Document data at this version
- uint64_t timestamp = 0; // updatedAt when this version was saved
- std::string updatedBy; // Who made this change
- bool encrypted = false; // Whether fields were encrypted
- std::vector<std::string> encryptedFields; // Encrypted field paths at time of save
- uint64_t createdAt = 0; // Original document createdAt (for restore after delete)
- std::string createdBy; // Original document createdBy (for restore after delete)
- [[nodiscard]] nlohmann::json toJson() const {
- nlohmann::json j;
- j["version"] = version;
- j["data"] = data;
- j["timestamp"] = timestamp;
- j["updatedBy"] = updatedBy;
- j["encrypted"] = encrypted;
- j["encryptedFields"] = encryptedFields;
- j["createdAt"] = createdAt;
- j["createdBy"] = createdBy;
- return j;
- }
- static DocumentVersion fromJson(const nlohmann::json& j) {
- DocumentVersion v;
- v.version = j.value("version", 0ULL);
- v.data = j.value("data", nlohmann::json::object());
- v.timestamp = j.value("timestamp", 0ULL);
- v.updatedBy = j.value("updatedBy", "");
- v.encrypted = j.value("encrypted", false);
- v.encryptedFields = j.value("encryptedFields", std::vector<std::string>{});
- v.createdAt = j.value("createdAt", 0ULL);
- v.createdBy = j.value("createdBy", "");
- return v;
- }
- };
- /**
- * Options for creating a collection.
- * Named CollectionOptions to avoid conflict with proto-generated type.
- */
- struct CollectionOptions {
- bool autoCreateId = true; // Auto-generate IDs if not provided
- uint32_t defaultTtlSeconds = 0; // Default TTL for documents (0 = none)
- bool encrypted = false; // Encrypt all documents by default
- std::vector<std::string> sensitiveFields; // Fields to always encrypt
- uint32_t maxVersions = 0; // Max version history per document (0 = unlimited)
- [[nodiscard]] nlohmann::json toJson() const {
- nlohmann::json j;
- j["autoCreateId"] = autoCreateId;
- j["defaultTtlSeconds"] = defaultTtlSeconds;
- j["encrypted"] = encrypted;
- j["sensitiveFields"] = sensitiveFields;
- j["maxVersions"] = maxVersions;
- return j;
- }
- static CollectionOptions fromJson(const nlohmann::json& j) {
- CollectionOptions opts;
- opts.autoCreateId = j.value("autoCreateId", true);
- opts.defaultTtlSeconds = j.value("defaultTtlSeconds", 0U);
- opts.encrypted = j.value("encrypted", false);
- opts.sensitiveFields = j.value("sensitiveFields", std::vector<std::string>{});
- opts.maxVersions = j.value("maxVersions", 0U);
- return opts;
- }
- };
- /**
- * Collection metadata.
- */
- struct CollectionInfo {
- std::string name;
- uint64_t documentCount = 0;
- uint64_t sizeBytes = 0;
- CollectionOptions options;
- uint64_t createdAt = 0;
- uint64_t updatedAt = 0;
- [[nodiscard]] nlohmann::json toJson() const {
- nlohmann::json j;
- j["name"] = name;
- j["documentCount"] = documentCount;
- j["sizeBytes"] = sizeBytes;
- j["options"] = options.toJson();
- j["createdAt"] = createdAt;
- j["updatedAt"] = updatedAt;
- return j;
- }
- };
- /**
- * Filter operator for queries.
- */
- enum class FilterOp {
- EQ, // Equal
- NE, // Not equal
- GT, // Greater than
- GTE, // Greater than or equal
- LT, // Less than
- LTE, // Less than or equal
- IN, // Value in array
- CONTAINS, // Array contains value
- EXISTS, // Field exists
- REGEX, // Regex match
- SEARCH // Full-text search across ID and string fields
- };
- /**
- * Query filter.
- */
- struct Filter {
- std::string field; // JSON path (e.g., "status" or "user.email")
- FilterOp op = FilterOp::EQ;
- nlohmann::json value;
- [[nodiscard]] nlohmann::json toJson() const {
- nlohmann::json j;
- j["field"] = field;
- j["op"] = static_cast<int>(op);
- j["value"] = value;
- return j;
- }
- static Filter fromJson(const nlohmann::json& j) {
- Filter f;
- f.field = j.value("field", "");
- f.op = static_cast<FilterOp>(j.value("op", 0));
- f.value = j.value("value", nlohmann::json());
- return f;
- }
- };
- /**
- * Sort specification.
- */
- struct Sort {
- std::string field;
- bool descending = false;
- [[nodiscard]] nlohmann::json toJson() const {
- nlohmann::json j;
- j["field"] = field;
- j["descending"] = descending;
- return j;
- }
- static Sort fromJson(const nlohmann::json& j) {
- Sort s;
- s.field = j.value("field", "");
- s.descending = j.value("descending", false);
- return s;
- }
- };
- /**
- * Query specification.
- */
- struct Query {
- std::vector<Filter> filters;
- std::optional<Sort> sort;
- uint32_t limit = 100;
- uint32_t offset = 0;
- std::vector<std::string> projection; // Fields to include (empty = all)
- [[nodiscard]] nlohmann::json toJson() const {
- nlohmann::json j;
- j["filters"] = nlohmann::json::array();
- for (const auto& f : filters) {
- j["filters"].push_back(f.toJson());
- }
- if (sort) {
- j["sort"] = sort->toJson();
- }
- j["limit"] = limit;
- j["offset"] = offset;
- j["projection"] = projection;
- return j;
- }
- static Query fromJson(const nlohmann::json& j) {
- Query q;
- if (j.contains("filters") && j["filters"].is_array()) {
- for (const auto& f : j["filters"]) {
- q.filters.push_back(Filter::fromJson(f));
- }
- }
- if (j.contains("sort") && !j["sort"].is_null()) {
- q.sort = Sort::fromJson(j["sort"]);
- }
- q.limit = j.value("limit", 100U);
- q.offset = j.value("offset", 0U);
- q.projection = j.value("projection", std::vector<std::string>{});
- return q;
- }
- };
- /**
- * Result of a find query.
- */
- struct QueryResult {
- std::vector<Document> documents;
- uint64_t totalCount = 0;
- bool hasMore = false;
- };
- /**
- * Storage event types.
- */
- enum class EventType {
- INSERT,
- UPDATE,
- DELETE,
- EXPIRE,
- INVALIDATE
- };
- /**
- * Storage event for pub/sub.
- */
- struct DatabaseEvent {
- EventType type;
- std::string collection;
- std::string documentId;
- uint64_t timestamp = 0;
- std::string nodeId;
- std::optional<nlohmann::json> data; // Document data (if included)
- [[nodiscard]] nlohmann::json toJson() const {
- nlohmann::json j;
- j["type"] = static_cast<int>(type);
- j["collection"] = collection;
- j["documentId"] = documentId;
- j["timestamp"] = timestamp;
- j["nodeId"] = nodeId;
- if (data) {
- j["data"] = *data;
- }
- return j;
- }
- };
- } // namespace smartbotic::database
|