-
Notifications
You must be signed in to change notification settings - Fork 0
This is a simplified documentation of the c++ API. Start a new project by using the template CMakeFile.txt (TODO).
Two sections here: first the core functions common to all types (schema) of database, and the standard functions specific to the standard database. Those classes and functions (should) works for any type of databases derivated from the StandardDatabase
.
In the following code, we omit the syd
namespace, we use:
using namespace syd;
Get the unique instance of the database manager:
DatabaseManager * m = DatabaseManager::GetInstance();
Create a database:
Database * db = m->Create("StandardDatabase", dbname, folder);
Read a database:
// Read a database, get a generic pointer syd::Database
Database * db = m->Read(dbname);
// or if you know the type (do the cast for you)
StandardDatabase * db = m->Read<StandardDatabase>(dbname);
Other function: auto & list = m->GetDatabaseSchemas();
to get a string list of all known database schema. Those schemas are dynamically loaded at runtime via plugin read in the SYD_PLUGIN
environment variable.
Basics operations are 'CRUD': Create, Read, Update and Delete.
// Get the name of the sql schema, i.e. "StandardDatabase"
std::string s = db->GetDatabaseSchema();
// Get the filename i.e. test.db
std::string s = db->GetFilename();
// Get the image folder of the db, relative to the test.db folder or absolute
std::string s = db->GetDatabaseRelativeFolder();
std::string s = db->GetDatabaseAbsoluteFolder();
Insert an element (with table Patient as example but could be other tables):
// For a generic database (unknown type)
syd::Record::pointer r = db->New("Patient");
// r is shared_ptr<syd::Record>
// Also possible :
auto r = db->New("Patient");
db->Set(r, args);
db->Insert(r);
// For a standard database (or derived from)
syd::Patient::pointer r;
db->New(r);
// r is a syd::Patient::pointer (or inherit from that)
p->name = "hello";
db->Insert(p);
Using the New operator from the database (db->New(r)
)is mandatory. Because of polymorphism, a database that inherit from StandardDatabase may manage Patient with class extended from syd::Patient.
// Get all patients
syd::Patient::vector v;
db->Query(v);
// Get all patients matching a query
typedef odb::query<Patient> QueryType;
QueryType q = (QueryType::name == "toto" and QueryType::age < 20)
db->Query(q, v);
// Get only one patient matching the query
syd::Patient::pointer patient;
db->QueryOne(patient, q);
// Get only one patient with its id
db->QueryOne(patient, id);
// Others functions
int n = db->GetNumberOfElements<syd::Patient>(); // slow
Update records in the database.
db->Update(patient);
db->Update(vector_of_patients);
syd::Patient::pointer patient = ...;
syd::Patient::vector patients = ...;
syd::Record::vector records = ...;
db->Delete(patient);
db->Delete(patients);
db->Delete(records);
Deleting a record can be complicated due to foreign keys (FK) in some tables. Two mecanisms are provided:
- On cascade delete. When a table has a FK, it is possible to choose to delete the record if the record corresponding to the FK is deleted. This is done with the following pragma :
#pragma db on_delete(cascade)
. See table DicomFile in standard database for example: we decide to automatically delete a DicomFile if the associated File is deleted. - In other circumstance, additional tasks must be performed when a record is deleted (for example removing the file on disk when an image is deleted). This is done with the callback mecanism:
void Callback(odb::callback_event, odb::database&) const;
void Callback(odb::callback_event, odb::database&);
Every access to a database can fail for one reason, you can test the result with a try/catch. See here:
try {
// Here, access to the database (insert, query etc)
}
catch (const Exception & e) {
std::cout << e->what(); // Go here if something wrong
}
For debug, you can access to the last SQL query with:
std::string s = db->GetLastSQLQuery();
- First step
- inherit from syd::Record (or a class that inherit from)
- Use this pragma (with abc the namespace and 'MyTable' the name of the table:
#pragma db object polymorphic pointer(std::shared_ptr) table("abc::MyTable") callback(Callback)
-
Second step
-
constructor ? not needed ?
-
add
friend class odb::access;
if the constructor is protected -
define typedef pointer and vector
-
implement
GetTableName
andGetStaticTableName
-
implement
New
-
All previous can be automatically done with
TABLE_DEFINE(MyTable)
-
Third step
-
implement
ToString
FIXME --> provide default ? -
implement
IsEqual
FIXME --> provide default ? -
implement
CopyFrom
FIXME --> provide default ? -
implement
Callback
or use macroTABLE_DEFAULT_CALLBACK
-
Fourth Step (optional, if not overloaded, default exist)
-
implement
Set
-
implement
InitPrintTable
andDumpInTable
-
implement
Sort
List of tables: TODO
Convenient functions for common queries (to retrieve a patient from his name for example):
// Let db be a syd::Database
Patient patient;
FindPatientByNameOrStudyId(patient, db, "toto");
std::vector<Tag> tags;
std::string tag_names; // separated by space
FindTags(tags, db, tag_names);
When records are delete, a mecanism takes care about dependencies.
Policy:
- Image: also delete Files.
- Files: also delete the files on disk, the Image and the DicomFile
- DicomFile: also delete the File, the DicomSerie
- DicomSerie: also delete the DicomFiles
- Injection: also delete DicomSeries and Timepoint
- Radionuclide: also delete the Injection
- Patient: also delete the Injection
- Tag : TODO
- Timepoint: TODO
Create an object DicomSerieBuilder and set the basics informations:
DicomSerieBuilder b(db);
b.SetInjection(injection);
b.SetForcePatientFlag(true);
b.SearchForFilesInFolder(folder, files);
Main function to create a DicomSerie from a file. Checks if the file is not already in the db, guess if the file should be added to an existing DicomSerie or if a new DicomSerie. The DicomSerie is not yet inserted into the db. See next function.
for(auto f:files) b.CreateDicomSerieFromFile(f.c_str());
Once the files have been created (previous function), insert all created series in the db.
b.InsertDicomSeries();
The class ImageBuilder allow to to create record in the Image table and the associated mhd/raw files.
Convert a dicom image to a mhd (and insert an Image)
ImageBuilder b(db);
syd::Image image = builder.InsertImage(d);
Create and insert a ROI as a binary mask: RoiMaskImage. Need a filename (mhd) and a DicomSerie to be linked with.
ImageBuilder b(db);
syd::Image image = b.InsertRoiMaskImage(dicom, roitype, filename);
It is also possible to create an image by stitching two dicom together:
ImageBuilder b(db);
syd::Image image = builder.InsertStitchedImage(tag, dicom1, dicom2);
It is recommended to add tag to images:
Tag t; // get a tag somehow
image.AddTag(t);
db->Update(image);