document.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. #pragma once
  2. #include <string>
  3. #include <vector>
  4. #include <chrono>
  5. #include <optional>
  6. #include <nlohmann/json.hpp>
  7. namespace smartbotic::database {
  8. /**
  9. * Represents a document stored in a collection.
  10. * Documents are JSON objects with metadata for versioning, TTL, and encryption.
  11. * Named Document to avoid conflict with proto-generated Document message.
  12. */
  13. struct Document {
  14. std::string id; // Unique document ID within collection
  15. std::string collection; // Collection name
  16. nlohmann::json data; // Document content (JSON)
  17. uint64_t version = 1; // Monotonic version for optimistic locking
  18. uint64_t createdAt = 0; // Creation timestamp (ms since epoch)
  19. uint64_t updatedAt = 0; // Last modification timestamp (ms since epoch)
  20. uint64_t expiresAt = 0; // TTL expiration (0 = no expiration)
  21. std::string nodeId; // Origin node for replication
  22. bool encrypted = false; // Whether sensitive fields are encrypted
  23. std::vector<std::string> encryptedFields; // List of encrypted field paths
  24. std::string createdBy; // User ID who created the document
  25. std::string updatedBy; // User ID who last updated the document
  26. // Check if document has expired
  27. [[nodiscard]] bool isExpired() const {
  28. if (expiresAt == 0) return false;
  29. auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
  30. std::chrono::system_clock::now().time_since_epoch()
  31. ).count();
  32. return static_cast<uint64_t>(now) >= expiresAt;
  33. }
  34. // Set TTL in seconds from now
  35. void setTtlSeconds(uint32_t seconds) {
  36. if (seconds == 0) {
  37. expiresAt = 0;
  38. } else {
  39. auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
  40. std::chrono::system_clock::now().time_since_epoch()
  41. ).count();
  42. expiresAt = static_cast<uint64_t>(now) + (seconds * 1000ULL);
  43. }
  44. }
  45. // Get remaining TTL in seconds (0 if expired or no TTL)
  46. [[nodiscard]] uint32_t getRemainingTtlSeconds() const {
  47. if (expiresAt == 0) return 0;
  48. auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
  49. std::chrono::system_clock::now().time_since_epoch()
  50. ).count();
  51. if (static_cast<uint64_t>(now) >= expiresAt) return 0;
  52. return static_cast<uint32_t>((expiresAt - static_cast<uint64_t>(now)) / 1000);
  53. }
  54. // JSON serialization
  55. [[nodiscard]] nlohmann::json toJson() const {
  56. nlohmann::json j;
  57. j["id"] = id;
  58. j["collection"] = collection;
  59. j["data"] = data;
  60. j["version"] = version;
  61. j["createdAt"] = createdAt;
  62. j["updatedAt"] = updatedAt;
  63. j["expiresAt"] = expiresAt;
  64. j["nodeId"] = nodeId;
  65. j["encrypted"] = encrypted;
  66. j["encryptedFields"] = encryptedFields;
  67. j["createdBy"] = createdBy;
  68. j["updatedBy"] = updatedBy;
  69. return j;
  70. }
  71. // JSON deserialization
  72. static Document fromJson(const nlohmann::json& j) {
  73. Document doc;
  74. doc.id = j.value("id", "");
  75. doc.collection = j.value("collection", "");
  76. doc.data = j.value("data", nlohmann::json::object());
  77. doc.version = j.value("version", 1ULL);
  78. doc.createdAt = j.value("createdAt", 0ULL);
  79. doc.updatedAt = j.value("updatedAt", 0ULL);
  80. doc.expiresAt = j.value("expiresAt", 0ULL);
  81. doc.nodeId = j.value("nodeId", "");
  82. doc.encrypted = j.value("encrypted", false);
  83. doc.encryptedFields = j.value("encryptedFields", std::vector<std::string>{});
  84. doc.createdBy = j.value("createdBy", "");
  85. doc.updatedBy = j.value("updatedBy", "");
  86. return doc;
  87. }
  88. };
  89. /**
  90. * Represents a historical snapshot of a document at a specific version.
  91. * Stored in version history when a document is updated or deleted.
  92. */
  93. struct DocumentVersion {
  94. uint64_t version = 0; // Version number
  95. nlohmann::json data; // Document data at this version
  96. uint64_t timestamp = 0; // updatedAt when this version was saved
  97. std::string updatedBy; // Who made this change
  98. bool encrypted = false; // Whether fields were encrypted
  99. std::vector<std::string> encryptedFields; // Encrypted field paths at time of save
  100. uint64_t createdAt = 0; // Original document createdAt (for restore after delete)
  101. std::string createdBy; // Original document createdBy (for restore after delete)
  102. [[nodiscard]] nlohmann::json toJson() const {
  103. nlohmann::json j;
  104. j["version"] = version;
  105. j["data"] = data;
  106. j["timestamp"] = timestamp;
  107. j["updatedBy"] = updatedBy;
  108. j["encrypted"] = encrypted;
  109. j["encryptedFields"] = encryptedFields;
  110. j["createdAt"] = createdAt;
  111. j["createdBy"] = createdBy;
  112. return j;
  113. }
  114. static DocumentVersion fromJson(const nlohmann::json& j) {
  115. DocumentVersion v;
  116. v.version = j.value("version", 0ULL);
  117. v.data = j.value("data", nlohmann::json::object());
  118. v.timestamp = j.value("timestamp", 0ULL);
  119. v.updatedBy = j.value("updatedBy", "");
  120. v.encrypted = j.value("encrypted", false);
  121. v.encryptedFields = j.value("encryptedFields", std::vector<std::string>{});
  122. v.createdAt = j.value("createdAt", 0ULL);
  123. v.createdBy = j.value("createdBy", "");
  124. return v;
  125. }
  126. };
  127. /**
  128. * Options for creating a collection.
  129. * Named CollectionOptions to avoid conflict with proto-generated type.
  130. */
  131. struct CollectionOptions {
  132. bool autoCreateId = true; // Auto-generate IDs if not provided
  133. uint32_t defaultTtlSeconds = 0; // Default TTL for documents (0 = none)
  134. bool encrypted = false; // Encrypt all documents by default
  135. std::vector<std::string> sensitiveFields; // Fields to always encrypt
  136. uint32_t maxVersions = 0; // Max version history per document (0 = unlimited)
  137. [[nodiscard]] nlohmann::json toJson() const {
  138. nlohmann::json j;
  139. j["autoCreateId"] = autoCreateId;
  140. j["defaultTtlSeconds"] = defaultTtlSeconds;
  141. j["encrypted"] = encrypted;
  142. j["sensitiveFields"] = sensitiveFields;
  143. j["maxVersions"] = maxVersions;
  144. return j;
  145. }
  146. static CollectionOptions fromJson(const nlohmann::json& j) {
  147. CollectionOptions opts;
  148. opts.autoCreateId = j.value("autoCreateId", true);
  149. opts.defaultTtlSeconds = j.value("defaultTtlSeconds", 0U);
  150. opts.encrypted = j.value("encrypted", false);
  151. opts.sensitiveFields = j.value("sensitiveFields", std::vector<std::string>{});
  152. opts.maxVersions = j.value("maxVersions", 0U);
  153. return opts;
  154. }
  155. };
  156. /**
  157. * Collection metadata.
  158. */
  159. struct CollectionInfo {
  160. std::string name;
  161. uint64_t documentCount = 0;
  162. uint64_t sizeBytes = 0;
  163. CollectionOptions options;
  164. uint64_t createdAt = 0;
  165. uint64_t updatedAt = 0;
  166. [[nodiscard]] nlohmann::json toJson() const {
  167. nlohmann::json j;
  168. j["name"] = name;
  169. j["documentCount"] = documentCount;
  170. j["sizeBytes"] = sizeBytes;
  171. j["options"] = options.toJson();
  172. j["createdAt"] = createdAt;
  173. j["updatedAt"] = updatedAt;
  174. return j;
  175. }
  176. };
  177. /**
  178. * Filter operator for queries.
  179. */
  180. enum class FilterOp {
  181. EQ, // Equal
  182. NE, // Not equal
  183. GT, // Greater than
  184. GTE, // Greater than or equal
  185. LT, // Less than
  186. LTE, // Less than or equal
  187. IN, // Value in array
  188. CONTAINS, // Array contains value
  189. EXISTS, // Field exists
  190. REGEX, // Regex match
  191. SEARCH // Full-text search across ID and string fields
  192. };
  193. /**
  194. * Query filter.
  195. */
  196. struct Filter {
  197. std::string field; // JSON path (e.g., "status" or "user.email")
  198. FilterOp op = FilterOp::EQ;
  199. nlohmann::json value;
  200. [[nodiscard]] nlohmann::json toJson() const {
  201. nlohmann::json j;
  202. j["field"] = field;
  203. j["op"] = static_cast<int>(op);
  204. j["value"] = value;
  205. return j;
  206. }
  207. static Filter fromJson(const nlohmann::json& j) {
  208. Filter f;
  209. f.field = j.value("field", "");
  210. f.op = static_cast<FilterOp>(j.value("op", 0));
  211. f.value = j.value("value", nlohmann::json());
  212. return f;
  213. }
  214. };
  215. /**
  216. * Sort specification.
  217. */
  218. struct Sort {
  219. std::string field;
  220. bool descending = false;
  221. [[nodiscard]] nlohmann::json toJson() const {
  222. nlohmann::json j;
  223. j["field"] = field;
  224. j["descending"] = descending;
  225. return j;
  226. }
  227. static Sort fromJson(const nlohmann::json& j) {
  228. Sort s;
  229. s.field = j.value("field", "");
  230. s.descending = j.value("descending", false);
  231. return s;
  232. }
  233. };
  234. /**
  235. * Query specification.
  236. */
  237. struct Query {
  238. std::vector<Filter> filters;
  239. std::optional<Sort> sort;
  240. uint32_t limit = 100;
  241. uint32_t offset = 0;
  242. std::vector<std::string> projection; // Fields to include (empty = all)
  243. [[nodiscard]] nlohmann::json toJson() const {
  244. nlohmann::json j;
  245. j["filters"] = nlohmann::json::array();
  246. for (const auto& f : filters) {
  247. j["filters"].push_back(f.toJson());
  248. }
  249. if (sort) {
  250. j["sort"] = sort->toJson();
  251. }
  252. j["limit"] = limit;
  253. j["offset"] = offset;
  254. j["projection"] = projection;
  255. return j;
  256. }
  257. static Query fromJson(const nlohmann::json& j) {
  258. Query q;
  259. if (j.contains("filters") && j["filters"].is_array()) {
  260. for (const auto& f : j["filters"]) {
  261. q.filters.push_back(Filter::fromJson(f));
  262. }
  263. }
  264. if (j.contains("sort") && !j["sort"].is_null()) {
  265. q.sort = Sort::fromJson(j["sort"]);
  266. }
  267. q.limit = j.value("limit", 100U);
  268. q.offset = j.value("offset", 0U);
  269. q.projection = j.value("projection", std::vector<std::string>{});
  270. return q;
  271. }
  272. };
  273. /**
  274. * Result of a find query.
  275. */
  276. struct QueryResult {
  277. std::vector<Document> documents;
  278. uint64_t totalCount = 0;
  279. bool hasMore = false;
  280. };
  281. /**
  282. * Storage event types.
  283. */
  284. enum class EventType {
  285. INSERT,
  286. UPDATE,
  287. DELETE,
  288. EXPIRE,
  289. INVALIDATE
  290. };
  291. /**
  292. * Storage event for pub/sub.
  293. */
  294. struct DatabaseEvent {
  295. EventType type;
  296. std::string collection;
  297. std::string documentId;
  298. uint64_t timestamp = 0;
  299. std::string nodeId;
  300. std::optional<nlohmann::json> data; // Document data (if included)
  301. [[nodiscard]] nlohmann::json toJson() const {
  302. nlohmann::json j;
  303. j["type"] = static_cast<int>(type);
  304. j["collection"] = collection;
  305. j["documentId"] = documentId;
  306. j["timestamp"] = timestamp;
  307. j["nodeId"] = nodeId;
  308. if (data) {
  309. j["data"] = *data;
  310. }
  311. return j;
  312. }
  313. };
  314. } // namespace smartbotic::database