Stroika Library 3.0d23x
 
Loading...
Searching...
No Matches
LocalDocumentDB.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#include <system_error>
7
10#include "Stroika/Foundation/Common/GUID.h"
18
19#include "LocalDocumentDB.h"
20
21using namespace Stroika::Foundation;
22
23using namespace Characters;
24using namespace Containers;
25using namespace Debug;
26using namespace DataExchange;
27using namespace Database;
28using namespace Database::Document::LocalDocumentDB;
29using namespace Execution;
30using namespace Time;
31
36
37// Comment this in to turn on aggressive noisy DbgTrace in this module
38// #define USE_NOISY_TRACE_IN_THIS_MODULE_ 1
39
40using Common::GUID;
42
43namespace {
44
45 /*
46 * Store collections entirely in RAM.
47 * \note \em Thread-Safety <a href='#Internally-Synchronized-Thread-Safety'>Internally-Synchronized-Thread-Safety</a>
48 */
49 struct MemoryDatabaseRep_ final : Database::Document::Connection::IRep {
50
51 using CollectionRep_ = Mapping<GUID, Document::Document>;
52
55
56 struct MyCollectionRep_ final : Document::Collection::IRep {
57 const shared_ptr<MemoryDatabaseRep_> fConnectionRep_; // save to bump reference count (so lifetime of collection always >= lifetime of documentDB)
58 const String fTableName_;
59
60 MyCollectionRep_ (const shared_ptr<MemoryDatabaseRep_>& connectionRep, const String& collectionName)
61 : fConnectionRep_{connectionRep}
62 , fTableName_{collectionName}
63 {
64 }
65 virtual IDType Add (const Document::Document& v) override
66 {
67#if USE_NOISY_TRACE_IN_THIS_MODULE_
68 TraceContextBumper ctx{"LocalDocumentDB::MemoryDatabaseRep_::MyCollectionRep_::Add"};
69#endif
70 optional<VariantValue> vID = v.Lookup (Document::kID);
71 Require (not vID.has_value () or fConnectionRep_->fOptions_.fAddAllowsExternallySpecifiedIDs);
72 auto rwLock = fConnectionRep_->fCollections_.rwget ();
73 CollectionRep_ collection = rwLock.cref ().LookupValue (fTableName_);
74 GUID id = vID.has_value () ? GUID{vID->As<String> ()} : GUID::GenerateNew ();
75 Document::Document doc2Add = v;
76 if (vID) {
77 doc2Add.Remove (Document::kID); // already in parent KEY so don't store redundantly
78 }
79 collection.Add (id, doc2Add);
80 rwLock.rwref ().Add (fTableName_, collection);
81 return id.ToString ();
82 }
83 virtual optional<Document::Document> Get (const IDType& id, const optional<Projection>& projection) override
84 {
85#if USE_NOISY_TRACE_IN_THIS_MODULE_
86 TraceContextBumper ctx{"LocalDocumentDB::MemoryDatabaseRep_::MyCollectionRep_::Get"};
87#endif
88 auto r = fConnectionRep_->fCollections_->LookupValue (fTableName_).Lookup (GUID{id});
89 if (r) {
90 r->Add (Document::kID, id);
91 }
92 if (projection and r) {
93 r = projection->Apply (*r);
94 }
95 return r;
96 }
97 virtual Sequence<Document::Document> GetAll (const optional<Filter>& filter, const optional<Projection>& projection) override
98 {
99#if USE_NOISY_TRACE_IN_THIS_MODULE_
100 TraceContextBumper ctx{"LocalDocumentDB::MemoryDatabaseRep_::MyCollectionRep_::GetAll", "filter={}, projection={}"_f, filter, projection};
101#endif
102 return fConnectionRep_->fCollections_->LookupValue (fTableName_)
103 .Map<Sequence<Document::Document>> ([&] (const KeyValuePair<GUID, Document::Document>& kvp) -> optional<Document::Document> {
104 Document::Document d = kvp.fValue;
105 d.Add (Document::kID, kvp.fKey.ToString ());
106 if (filter and not filter->Matches (d)) {
107 return nullopt; // skip cuz didn't match filter
108 }
109 else {
110 if (projection) {
111 d = projection->Apply (d);
112 }
113 return d;
114 }
115 });
116 }
117 virtual void Update (const IDType& id, const Document::Document& newV, const optional<Set<String>>& onlyTheseFields) override
118 {
119#if USE_NOISY_TRACE_IN_THIS_MODULE_
120 TraceContextBumper ctx{"LocalDocumentDB::MemoryDatabaseRep_::MyCollectionRep_::Update"};
121#endif
122 Document::Document uploadDoc = newV;
123 if (onlyTheseFields) {
124 uploadDoc.RetainAll (*onlyTheseFields);
125 }
126 static const auto kExcept1_ = RuntimeErrorException{"no such table"sv};
127 static const auto kNoSuchIDException_ = RuntimeErrorException{"no such id"sv};
128 auto rwLock = fConnectionRep_->fCollections_.rwget ();
129 CollectionRep_ collection = rwLock.cref ().LookupChecked (fTableName_, kExcept1_);
130 Document::Document d2Update = onlyTheseFields ? collection.LookupChecked (id, kNoSuchIDException_) : uploadDoc;
131 // any fields listed in onlyTheseFields, but not present in newV need to be removed
132 if (onlyTheseFields) {
133 d2Update.AddAll (uploadDoc);
134 Set<String> removeMe = *onlyTheseFields - newV.Keys ();
135 d2Update.RemoveAll (removeMe);
136 }
137 d2Update.RemoveIf (Document::kID);
138 collection.Add (id, d2Update);
139 rwLock.rwref ().Add (fTableName_, collection); // replace the actual collection in our master database of collections
140 }
141 virtual void Remove (const IDType& id) override
142 {
143#if USE_NOISY_TRACE_IN_THIS_MODULE_
144 TraceContextBumper ctx{"LocalDocumentDB::MemoryDatabaseRep_::MyCollectionRep_::Remove"};
145#endif
146 auto rwLock = fConnectionRep_->fCollections_.rwget ();
147 if (optional<CollectionRep_> oc = rwLock.cref ().Lookup (fTableName_)) {
148 CollectionRep_ c = *oc;
149 if (c.RemoveIf (id)) {
150 rwLock.rwref ().Add (fTableName_, c); // replace the actual collection in our master database of collections
151 }
152 }
153 }
154 };
155
156 struct MyTransactionRep_ final : Database::Document::Transaction::IRep {
157 virtual void Commit () override
158 {
159 // nothing todo
160 }
161 virtual void Rollback () override
162 {
164 }
165 virtual Disposition GetDisposition () const override
166 {
167 return Disposition::eCompleted;
168 }
169 };
170
171 MemoryDatabaseRep_ () = delete;
172 MemoryDatabaseRep_ (const MemoryDatabaseRep_&) = delete;
173 MemoryDatabaseRep_ ([[maybe_unused]] const Document::LocalDocumentDB::Options& options)
174 : fOptions_{options}
175 {
176 TraceContextBumper ctx{"LocalDocumentDB::MemoryDatabaseRep_::MemoryDatabaseRep_"};
177 //Assert (shared_from_this ().get () == this); // only support allocating with make_shared - cannot check here cuz object not yet fully constructed
178 }
179 virtual shared_ptr<const EngineProperties> GetEngineProperties () const override
180 {
181 struct MyEngineProperties_ final : EngineProperties {
182 virtual String GetEngineName () const override
183 {
184 return "LocalDocumentDB.MemoryDB"sv;
185 }
186 };
187 static const shared_ptr<const EngineProperties> kProps_ = Memory::MakeSharedPtr<const MyEngineProperties_> ();
188 return kProps_;
189 }
190 virtual Database::Document::Connection::Options GetOptions () const override
191 {
192 return fOptions_;
193 }
194 virtual Set<String> GetCollections () override
195 {
196 return Set<String>{fCollections_.load ().Keys ()};
197 }
198 virtual Document::Collection::Ptr CreateCollection (const String& name) override
199 {
200 auto rwLock = fCollections_.rwget ();
201 if (not rwLock.cref ().Lookup (name)) {
202 rwLock.rwref ().Add (name, {});
203 }
204 return GetCollection (name);
205 }
206 virtual void DropCollection (const String& name) override
207 {
208 auto rwLock = fCollections_.rwget ();
209 rwLock.rwref ().RemoveIf (name);
210 }
211 virtual Document::Collection::Ptr GetCollection (const String& name) override
212 {
213 Require (fCollections_.load ().ContainsKey (name));
215 Memory::MakeSharedPtr<MyCollectionRep_> (Debug::UncheckedDynamicPointerCast<MemoryDatabaseRep_> (shared_from_this ()), name)};
216 }
217 virtual Document::Transaction mkTransaction () override
218 {
219 return Document::Transaction{make_unique<MyTransactionRep_> ()};
220 }
221 };
222
223 /*
224 * Store collections in json file (leveraging MemoryDatabaseRep_ internally).
225 * \note \em Thread-Safety <a href='#Internally-Synchronized-Thread-Safety'>Internally-Synchronized-Thread-Safety</a>
226 */
227 struct SingleFileDatabaseRep_ final : Database::Document::Connection::IRep {
228
229 const filesystem::path fExternalFile_;
230 shared_ptr<MemoryDatabaseRep_> fMemoryDB_; // already internally syncrhonized, must be shared_ptr cuz it uses shared_from_this
231 const DataExchange::Variant::Reader fReader_;
232 const DataExchange::Variant::Writer fWriter_;
233
234 struct MyCollectionRep_ final : Document::Collection::IRep {
235 const shared_ptr<SingleFileDatabaseRep_> fDBRep_; // save to bump reference count (lifetime safety), and to force write
236 shared_ptr<Database::Document::Collection::IRep> fDelegateToInMemoryDB_; // inside memorydb
237
238 MyCollectionRep_ (const shared_ptr<SingleFileDatabaseRep_>& dbRep, const shared_ptr<Database::Document::Collection::IRep>& delgateImplTo)
239 : fDBRep_{dbRep}
240 , fDelegateToInMemoryDB_{delgateImplTo}
241 {
242 }
243 virtual IDType Add (const Document::Document& v) override
244 {
245#if USE_NOISY_TRACE_IN_THIS_MODULE_
246 TraceContextBumper ctx{"LocalDocumentDB::SingleFileDatabaseRep_::MyCollectionRep_::Add"};
247#endif
248 [[maybe_unused]] auto rwLock = fDBRep_->fMemoryDB_->fCollections_.rwget ();
249 auto id = fDelegateToInMemoryDB_->Add (v);
250 fDBRep_->DoWriteToFS ();
251 return id;
252 }
253 virtual optional<Document::Document> Get (const IDType& id, const optional<Projection>& projection) override
254 {
255#if USE_NOISY_TRACE_IN_THIS_MODULE_
256 TraceContextBumper ctx{"LocalDocumentDB::SingleFileDatabaseRep_::MyCollectionRep_::Get"};
257#endif
258 return fDelegateToInMemoryDB_->Get (id, projection);
259 }
260 virtual Sequence<Document::Document> GetAll (const optional<Filter>& filter, const optional<Projection>& projection) override
261 {
262#if USE_NOISY_TRACE_IN_THIS_MODULE_
263 TraceContextBumper ctx{"LocalDocumentDB::SingleFileDatabaseRep_::MyCollectionRep_::GetAll", "filter={}, projection={}"_f,
264 filter, projection};
265#endif
266 return fDelegateToInMemoryDB_->GetAll (filter, projection);
267 }
268 virtual void Update (const IDType& id, const Document::Document& newV, const optional<Set<String>>& onlyTheseFields) override
269 {
270#if USE_NOISY_TRACE_IN_THIS_MODULE_
271 TraceContextBumper ctx{"LocalDocumentDB::SingleFileDatabaseRep_::MyCollectionRep_::Update"};
272#endif
273 [[maybe_unused]] auto rwLock = fDBRep_->fMemoryDB_->fCollections_.rwget ();
274 fDelegateToInMemoryDB_->Update (id, newV, onlyTheseFields);
275 fDBRep_->DoWriteToFS ();
276 }
277 virtual void Remove (const IDType& id) override
278 {
279#if USE_NOISY_TRACE_IN_THIS_MODULE_
280 TraceContextBumper ctx{"LocalDocumentDB::SingleFileDatabaseRep_::MyCollectionRep_::Remove"};
281#endif
282 [[maybe_unused]] auto rwLock = fDBRep_->fMemoryDB_->fCollections_.rwget ();
283 fDelegateToInMemoryDB_->Remove (id);
284 fDBRep_->DoWriteToFS ();
285 }
286 };
287
288 struct MyTransactionRep_ final : Database::Document::Transaction::IRep {
289 virtual void Commit () override
290 {
291 // nothing todo
292 }
293 virtual void Rollback () override
294 {
296 }
297 virtual Disposition GetDisposition () const override
298 {
299 return Disposition::eCompleted;
300 }
301 };
302
303 SingleFileDatabaseRep_ () = delete;
304 SingleFileDatabaseRep_ (const SingleFileDatabaseRep_&) = delete;
305 SingleFileDatabaseRep_ ([[maybe_unused]] const Document::LocalDocumentDB::Options& options,
306 const Document::LocalDocumentDB::Options::SingleFileStorage& sfOptions)
307 : fExternalFile_{sfOptions.fFile}
308 , fMemoryDB_{make_shared<MemoryDatabaseRep_> (options)}
309 , fReader_{get<DataExchange::Variant::Reader> (sfOptions.fSerialization)}
310 , fWriter_{get<DataExchange::Variant::Writer> (sfOptions.fSerialization)}
311 {
312 DoReadFromFS ();
313 }
314 virtual shared_ptr<const EngineProperties> GetEngineProperties () const override
315 {
316 struct MyEngineProperties_ final : EngineProperties {
317 virtual String GetEngineName () const override
318 {
319 return "LocalDocumentDB.SingleFile"sv;
320 }
321 };
322 static const shared_ptr<const EngineProperties> kProps_ = Memory::MakeSharedPtr<const MyEngineProperties_> ();
323 return kProps_;
324 }
325 virtual Database::Document::Connection::Options GetOptions () const override
326 {
327 return fMemoryDB_->GetOptions ();
328 }
329 virtual Set<String> GetCollections () override
330 {
331 return fMemoryDB_->GetCollections ();
332 }
333 virtual Document::Collection::Ptr CreateCollection (const String& name) override
334 {
335 [[maybe_unused]] auto rwLock = fMemoryDB_->fCollections_.rwget ();
336 fMemoryDB_->CreateCollection (name);
337 DoWriteToFS ();
338 return GetCollection (name);
339 }
340 virtual void DropCollection (const String& name) override
341 {
342 [[maybe_unused]] auto rwLock = fMemoryDB_->fCollections_.rwget ();
343 fMemoryDB_->DropCollection (name);
344 DoWriteToFS ();
345 }
346 virtual Document::Collection::Ptr GetCollection (const String& name) override
347 {
348 Document::Collection::Ptr memDBCollection = fMemoryDB_->GetCollection (name);
349 return Document::Collection::Ptr{Memory::MakeSharedPtr<MyCollectionRep_> (
350 Debug::UncheckedDynamicPointerCast<SingleFileDatabaseRep_> (shared_from_this ()), memDBCollection)};
351 }
352 virtual Document::Transaction mkTransaction () override
353 {
354 return Document::Transaction{make_unique<MyTransactionRep_> ()};
355 }
356 void DoReadFromFS ()
357 {
358 using namespace IO::FileSystem;
359#if USE_NOISY_TRACE_IN_THIS_MODULE_
360 TraceContextBumper ctx{"LocalDocumentDB::SingleFileDatabaseRep_::DoReadFromFS", "path={}"_f, fExternalFile_};
361#endif
362 if (filesystem::exists (fExternalFile_)) {
363 auto rwLock = fMemoryDB_->fCollections_.rwget ();
364 rwLock.rwref ().clear ();
365 for (KeyValuePair<String, VariantValue> collectionAndDocument :
366 fReader_.Read (FileInputStream::New (fExternalFile_)).As<Mapping<String, VariantValue>> ()) {
367 rwLock.rwref ().Add (collectionAndDocument.fKey,
368 collectionAndDocument.fValue.As<Mapping<String, VariantValue>> ().Map<Mapping<GUID, Document::Document>> (
370 return {GUID{kvp.fKey}, kvp.fValue.As<Document::Document> ()};
371 }));
372 }
373 }
374 }
375 void DoWriteToFS ()
376 {
377#if USE_NOISY_TRACE_IN_THIS_MODULE_
378 TraceContextBumper ctx{"LocalDocumentDB::SingleFileDatabaseRep_::DoWriteToFS", "path={}"_f, fExternalFile_};
379#endif
380 using namespace IO::FileSystem;
381 ThroughTmpFileWriter tmpFile{fExternalFile_};
382 IO::FileSystem::FileOutputStream::Ptr outStream = IO::FileSystem::FileOutputStream::New (tmpFile.GetFilePath ());
383 Mapping<String, VariantValue> collectionsAsVV;
384 for (const KeyValuePair<String, Mapping<GUID, Document::Document>>& collection : fMemoryDB_->fCollections_.load ()) {
385 Mapping<GUID, Document::Document> collectionValue = collection.fValue;
386 Mapping<String, VariantValue> collWithStringKey;
387 for (const KeyValuePair<GUID, Document::Document>& kvp : collectionValue) {
388 collWithStringKey.Add (kvp.fKey.ToString (), VariantValue{kvp.fValue});
389 }
390 collectionsAsVV.Add (collection.fKey, VariantValue{collWithStringKey});
391 }
392 this->fWriter_.Write (VariantValue{collectionsAsVV}, outStream);
393 outStream.Close (); // close like this so we can throw exception - cannot throw if we count on DTOR
394 tmpFile.Commit (); // any exceptions cause the tmp file to be automatically cleaned up
395 }
396 };
397
398 // Store each collection in a folder under the root folder
399 struct DirectoryFilesystemDatabaseRep_ final : Database::Document::Connection::IRep {
401 const filesystem::path fRoot_;
402 const DataExchange::Variant::Reader fReader_;
403 const DataExchange::Variant::Writer fWriter_;
404
405 struct MyCollectionRep_ final : Document::Collection::IRep {
406 const shared_ptr<DirectoryFilesystemDatabaseRep_> fDBRep_; // save to bump reference count (lifetime safety)
407 const String fName_;
408 const filesystem::path fCollectionRoot_;
409
410 MyCollectionRep_ (const shared_ptr<DirectoryFilesystemDatabaseRep_>& dbRep, const String& name)
411 : fDBRep_{dbRep}
412 , fName_{name}
413 , fCollectionRoot_{dbRep->GetCollectionFilePath_ (name)}
414 {
415 }
416 virtual IDType Add (const Document::Document& v) override
417 {
418#if USE_NOISY_TRACE_IN_THIS_MODULE_
419 TraceContextBumper ctx{"LocalDocumentDB::DirectoryFilesystemDatabaseRep_::MyCollectionRep_::Add"};
420#endif
421 optional<VariantValue> vID = v.Lookup (Document::kID);
422 Require (not vID.has_value () or fDBRep_->fOptions_.fAddAllowsExternallySpecifiedIDs);
423 GUID id = vID.has_value () ? GUID{vID->As<String> ()} : GUID::GenerateNew ();
424 Document::Document doc2Add = v;
425 if (vID) {
426 doc2Add.Remove (Document::kID); // already in parent KEY so don't store redundantly
427 }
428 DoWriteToFS_ (id, VariantValue{doc2Add});
429 return id.As<IDType> ();
430 }
431 virtual optional<Document::Document> Get (const IDType& id, const optional<Projection>& projection) override
432 {
433#if USE_NOISY_TRACE_IN_THIS_MODULE_
434 TraceContextBumper ctx{"LocalDocumentDB::DirectoryFilesystemDatabaseRep_::MyCollectionRep_::Get"};
435#endif
436 if (auto od = DoReadFromFS_ (GUID{id})) {
437 Document::Document d = *od;
438 d.Add (Document::kID, id);
439 if (projection) {
440 d = projection->Apply (d);
441 }
442 return d;
443 }
444 else {
445 return nullopt;
446 }
447 }
448 virtual Sequence<Document::Document> GetAll (const optional<Filter>& filter, const optional<Projection>& projection) override
449 {
450#if USE_NOISY_TRACE_IN_THIS_MODULE_
451 TraceContextBumper ctx{"LocalDocumentDB::DirectoryFilesystemDatabaseRep_::MyCollectionRep_::GetAll",
452 "filter={}, projection={}"_f, filter, projection};
453#endif
455 for (const auto& entry : filesystem::directory_iterator{fCollectionRoot_}) {
456 if (entry.path ().extension () == ".json"sv) { // Check if the entry is a JSON file
458 fDBRep_->fReader_.Read (IO::FileSystem::FileInputStream::New (entry.path ())).As<Document::Document> ();
459 d.Add (Document::kID, entry.path ().stem ().string ());
460 if (not filter or filter->Matches (d)) {
461 if (projection) {
462 d = projection->Apply (d);
463 }
464 result += d;
465 }
466 }
467 }
468 return result;
469 }
470 virtual void Update (const IDType& id, const Document::Document& newV, const optional<Set<String>>& onlyTheseFields) override
471 {
472#if USE_NOISY_TRACE_IN_THIS_MODULE_
473 TraceContextBumper ctx{"LocalDocumentDB::DirectoryFilesystemDatabaseRep_::MyCollectionRep_::Update",
474 "id={},newV={}, onlyTheseFields={}"_f, id, newV, onlyTheseFields};
475#endif
476 Document::Document updatedDoc =
477 onlyTheseFields ? Memory::ValueOfOrThrow (DoReadFromFS_ (id), RuntimeErrorException{"no such id"sv}) : newV;
478 Document::Document updateWithDoc = newV;
479 if (onlyTheseFields) {
480 updateWithDoc.RetainAll (*onlyTheseFields);
481 }
482 updatedDoc.AddAll (updateWithDoc);
483 if (onlyTheseFields) {
484 // any fields listed in onlyTheseFields, but not present in newV need to be removed
485 Set<String> removeMe = *onlyTheseFields - newV.Keys ();
486 updatedDoc.RemoveAll (removeMe);
487 }
488 DoWriteToFS_ (GUID{id}, VariantValue{updatedDoc});
489 }
490 virtual void Remove (const IDType& id) override
491 {
492#if USE_NOISY_TRACE_IN_THIS_MODULE_
493 TraceContextBumper ctx{"LocalDocumentDB::DirectoryFilesystemDatabaseRep_::MyCollectionRep_::Remove", "id={}"_f, id};
494#endif
495 (void)filesystem::remove (GetDocumentFilePath_ (GUID{id}));
496 }
497 filesystem::path GetDocumentFilePath_ (const GUID& id) const
498 {
499 return fCollectionRoot_ / (id.As<String> () + ".json"sv).As<filesystem::path> ();
500 }
501 optional<Document::Document> DoReadFromFS_ (const GUID& id)
502 {
503 using namespace IO::FileSystem;
504 filesystem::path docFilePath = GetDocumentFilePath_ (id);
505#if USE_NOISY_TRACE_IN_THIS_MODULE_
506 TraceContextBumper ctx{"LocalDocumentDB::DirectoryFilesystemDatabaseRep_::DoReadFromFS", "path={}"_f, docFilePath};
507#endif
508 if (filesystem::exists (docFilePath)) {
509 return fDBRep_->fReader_.Read (FileInputStream::New (docFilePath)).As<Document::Document> ();
510 }
511 return nullopt;
512 }
513 void DoWriteToFS_ (const GUID& id, const VariantValue& vv)
514 {
515 filesystem::path docFilePath = GetDocumentFilePath_ (id);
516#if USE_NOISY_TRACE_IN_THIS_MODULE_
517 TraceContextBumper ctx{"LocalDocumentDB::DirectoryFilesystemDatabaseRep_::DoWriteToFS()", "path={}"_f, docFilePath};
518#endif
519 using namespace IO::FileSystem;
520 ThroughTmpFileWriter tmpFile{docFilePath};
521 IO::FileSystem::FileOutputStream::Ptr outStream = IO::FileSystem::FileOutputStream::New (tmpFile.GetFilePath ());
522 fDBRep_->fWriter_.Write (vv, outStream);
523 outStream.Close (); // close like this so we can throw exception - cannot throw if we count on DTOR
524 tmpFile.Commit (); // any exceptions cause the tmp file to be automatically cleaned up
525 }
526 };
527
528 struct MyTransactionRep_ final : Database::Document::Transaction::IRep {
529 virtual void Commit () override
530 {
531 // nothing todo
532 }
533 virtual void Rollback () override
534 {
536 }
537 virtual Disposition GetDisposition () const override
538 {
539 return Disposition::eCompleted;
540 }
541 };
542
543 filesystem::path GetCollectionFilePath_ (const String& collectionName) const
544 {
545 // @todo - in future - consider mapping name to URL-safe name
546 // @todo use PCTEncode2String
547 return fRoot_ / (collectionName.As<filesystem::path> ());
548 }
549
550 DirectoryFilesystemDatabaseRep_ () = delete;
551 DirectoryFilesystemDatabaseRep_ (const DirectoryFilesystemDatabaseRep_&) = delete;
552 DirectoryFilesystemDatabaseRep_ (const Document::LocalDocumentDB::Options& options,
553 const Document::LocalDocumentDB::Options::DirectoryFileStorage& dfOptions)
554 : fOptions_{options}
555 , fRoot_{dfOptions.fRoot}
556 , fReader_{get<DataExchange::Variant::Reader> (dfOptions.fSerialization)}
557 , fWriter_{get<DataExchange::Variant::Writer> (dfOptions.fSerialization)}
558 {
559 filesystem::create_directories (fRoot_);
560 }
561 virtual shared_ptr<const EngineProperties> GetEngineProperties () const override
562 {
563 struct MyEngineProperties_ final : EngineProperties {
564 virtual String GetEngineName () const override
565 {
566 return "LocalDocumentDB.Folder"sv;
567 }
568 };
569 static const shared_ptr<const EngineProperties> kProps_ = Memory::MakeSharedPtr<const MyEngineProperties_> ();
570 return kProps_;
571 }
572 virtual Database::Document::Connection::Options GetOptions () const override
573 {
574 return fOptions_;
575 }
576 virtual Set<String> GetCollections () override
577 {
578 Set<String> result;
579 for (const auto& entry : filesystem::directory_iterator{fRoot_}) {
580 if (filesystem::is_directory (entry.path ())) { // Check if the entry is a directory
581 result += String{entry.path ().filename ()};
582 }
583 }
584 return result;
585 }
586 virtual Document::Collection::Ptr CreateCollection (const String& name) override
587 {
588 filesystem::create_directories (GetCollectionFilePath_ (name));
589 return Document::Collection::Ptr{Memory::MakeSharedPtr<MyCollectionRep_> (
590 Debug::UncheckedDynamicPointerCast<DirectoryFilesystemDatabaseRep_> (shared_from_this ()), name)};
591 }
592 virtual void DropCollection (const String& name) override
593 {
594 filesystem::remove_all (GetCollectionFilePath_ (name));
595 }
596 virtual Document::Collection::Ptr GetCollection (const String& name) override
597 {
598 Require (GetCollections ().Contains (name));
599 return Document::Collection::Ptr{Memory::MakeSharedPtr<MyCollectionRep_> (
600 Debug::UncheckedDynamicPointerCast<DirectoryFilesystemDatabaseRep_> (shared_from_this ()), name)};
601 }
602 virtual Document::Transaction mkTransaction () override
603 {
604 return Document::Transaction{make_unique<MyTransactionRep_> ()};
605 }
606 };
607}
608
609/*
610 ********************************************************************************
611 *********************** SQL::LocalDocumentDB::New ******************************
612 ********************************************************************************
613 */
614auto Document::LocalDocumentDB::New (const Options& options) -> Ptr
615{
616 if (get_if<Options::MemoryStorage> (&options.fStorage)) {
617 return Ptr{Memory::MakeSharedPtr<MemoryDatabaseRep_> (options)};
618 }
619 else if (auto fop = get_if<Options::SingleFileStorage> (&options.fStorage)) {
620 return Ptr{Memory::MakeSharedPtr<SingleFileDatabaseRep_> (options, *fop)};
621 }
622 else if (auto dop = get_if<Options::DirectoryFileStorage> (&options.fStorage)) {
623 return Ptr{Memory::MakeSharedPtr<DirectoryFilesystemDatabaseRep_> (options, *dop)};
624 }
626 return nullptr;
627}
#define AssertNotImplemented()
Definition Assertions.h:401
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual CONTAINER_OF_Key_T As() const
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
Definition Mapping.inl:188
nonvirtual optional< mapped_type > Lookup(ArgByValueType< key_type > key) const
Definition Mapping.inl:142
nonvirtual mapped_type LookupChecked(ArgByValueType< key_type > key, const THROW_IF_MISSING &throwIfMissing) const
nonvirtual unsigned int AddAll(ITERABLE_OF_ADDABLE &&items, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
nonvirtual bool RemoveIf(ArgByValueType< key_type > key)
Remove the given item, if it exists. Return true if found and removed.
Definition Mapping.inl:235
nonvirtual RESULT_CONTAINER Map(ELEMENT_MAPPER &&elementMapper) const
'override' Iterable<>::Map () function so container template defaults to Mapping, and improve that ca...
nonvirtual void RemoveAll()
RemoveAll removes all, or all matching (predicate, iterator range, equals comparer or whatever) items...
Definition Mapping.inl:240
nonvirtual void Remove(ArgByValueType< key_type > key)
Remove the given item (which must exist).
Definition Mapping.inl:223
nonvirtual Iterable< key_type > Keys() const
Definition Mapping.inl:111
nonvirtual void RetainAll(const ITERABLE_OF_KEY_TYPE &items)
A generalization of a vector: a container whose elements are keyed by the natural numbers.
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
abstract class specifying interface for readers that map a source like XML or JSON to a VariantValue ...
abstract class specifying interface for writers VariantValue objects to serialized formats like JSON,...
nonvirtual void Write(const VariantValue &v, const Streams::OutputStream::Ptr< byte > &out) const
Simple variant-value (case variant union) object, with (variant) basic types analogous to a value in ...
virtual Collection::Ptr CreateCollection(const String &name)=0
EngineProperties captures the features associated with a given database engine (being talked to throu...
define a (simple) projection on a document, subsetting the fields of that document.
Definition Projection.h:32
Wrap any object with Synchronized<> and it can be used similarly to the base type,...
nonvirtual WritableReference rwget()
get a read-write smart pointer to the underlying Synchronized<> object, holding the full lock the who...
nonvirtual void Apply(const function< void(ArgByValueType< T > item)> &doToElement, Execution::SequencePolicy seq=Execution::SequencePolicy::eDEFAULT) const
Run the argument function (or lambda) on each element of the container.
static GUID GenerateNew() noexcept
Definition GUID.cpp:76