comparison OrthancServer/ServerIndex.cpp @ 180:626777d01dc4

use of hashes to index dicom objects
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 09 Nov 2012 12:12:59 +0100
parents 78e28d0098d9
children f68c039b0571
comparison
equal deleted inserted replaced
179:78e28d0098d9 180:626777d01dc4
54 namespace Internals 54 namespace Internals
55 { 55 {
56 class DeleteFromFileStorageFunction : public SQLite::IScalarFunction 56 class DeleteFromFileStorageFunction : public SQLite::IScalarFunction
57 { 57 {
58 private: 58 private:
59 FileStorage fileStorage_; 59 std::auto_ptr<FileStorage> fileStorage_;
60 60
61 public: 61 public:
62 DeleteFromFileStorageFunction(const std::string& path) : 62 DeleteFromFileStorageFunction(const std::string& path)
63 fileStorage_(path) 63 {
64 { 64 if (path != ":memory:")
65 {
66 fileStorage_.reset(new FileStorage(path));
67 }
65 } 68 }
66 69
67 virtual const char* GetName() const 70 virtual const char* GetName() const
68 { 71 {
69 return "DeleteFromFileStorage"; 72 return "DeleteFromFileStorage";
74 return 1; 77 return 1;
75 } 78 }
76 79
77 virtual void Compute(SQLite::FunctionContext& context) 80 virtual void Compute(SQLite::FunctionContext& context)
78 { 81 {
82 if (fileStorage_.get() == NULL)
83 {
84 // In-memory index, for unit tests
85 return;
86 }
87
79 std::string fileUuid = context.GetStringValue(0); 88 std::string fileUuid = context.GetStringValue(0);
80 LOG(INFO) << "Removing file [" << fileUuid << "]"; 89 LOG(INFO) << "Removing file [" << fileUuid << "]";
90
81 if (Toolbox::IsUuid(fileUuid)) 91 if (Toolbox::IsUuid(fileUuid))
82 { 92 {
83 fileStorage_.Remove(fileUuid); 93 fileStorage_->Remove(fileUuid);
84 } 94 }
85 } 95 }
86 }; 96 };
87 97
88 98
203 { 213 {
204 return false; 214 return false;
205 } 215 }
206 } 216 }
207 217
208 bool ServerIndex::HasInstance(std::string& instanceUuid, 218
209 const DicomInstanceHasher& hasher) 219 bool ServerIndex::HasInstance(DicomInstanceHasher& hasher)
210 { 220 {
211 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Instances WHERE dicomInstance=?"); 221 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Instances WHERE dicomInstance=?");
212 s.BindString(0, hasher.GetInstanceUid()); 222 s.BindString(0, hasher.GetInstanceUid());
213 if (s.Step()) 223 return s.Step();
214 {
215 instanceUuid = s.ColumnString(0);
216 return true;
217 }
218 else
219 {
220 return false;
221 }
222 } 224 }
223 225
224 226
225 void ServerIndex::RecordChange(const std::string& resourceType, 227 void ServerIndex::RecordChange(const std::string& resourceType,
226 const std::string& uuid) 228 const std::string& uuid)
230 s.BindString(1, uuid); 232 s.BindString(1, uuid);
231 s.Run(); 233 s.Run();
232 } 234 }
233 235
234 236
235 std::string ServerIndex::CreateInstance(const std::string& parentSeriesUuid, 237 void ServerIndex::CreateInstance(DicomInstanceHasher& hasher,
236 const DicomInstanceHasher& hasher, 238 const DicomMap& dicomSummary,
237 const DicomMap& dicomSummary, 239 const std::string& fileUuid,
238 const std::string& fileUuid, 240 uint64_t fileSize,
239 uint64_t fileSize, 241 const std::string& jsonUuid,
240 const std::string& jsonUuid, 242 const std::string& distantAet)
241 const std::string& distantAet) 243 {
242 {
243 std::string instanceUuid = Toolbox::GenerateUuid();
244
245 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)"); 244 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)");
246 s2.BindString(0, instanceUuid); 245 s2.BindString(0, hasher.HashInstance());
247 s2.BindInt(1, ResourceType_Instance); 246 s2.BindInt(1, ResourceType_Instance);
248 s2.Run(); 247 s2.Run();
249 248
250 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Instances VALUES(?, ?, ?, ?, ?, ?, ?, ?)"); 249 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Instances VALUES(?, ?, ?, ?, ?, ?, ?, ?)");
251 s.BindString(0, instanceUuid); 250 s.BindString(0, hasher.HashInstance());
252 s.BindString(1, parentSeriesUuid); 251 s.BindString(1, hasher.HashSeries());
253 s.BindString(2, hasher.GetInstanceUid()); 252 s.BindString(2, hasher.GetInstanceUid());
254 s.BindString(3, fileUuid); 253 s.BindString(3, fileUuid);
255 s.BindInt64(4, fileSize); 254 s.BindInt64(4, fileSize);
256 s.BindString(5, jsonUuid); 255 s.BindString(5, jsonUuid);
257 s.BindString(6, distantAet); 256 s.BindString(6, distantAet);
267 s.BindNull(7); 266 s.BindNull(7);
268 } 267 }
269 268
270 s.Run(); 269 s.Run();
271 270
272 RecordChange("instances", instanceUuid); 271 RecordChange("instances", hasher.HashInstance());
273 272
274 DicomMap dicom; 273 DicomMap dicom;
275 dicomSummary.ExtractInstanceInformation(dicom); 274 dicomSummary.ExtractInstanceInformation(dicom);
276 StoreMainDicomTags(instanceUuid, dicom); 275 StoreMainDicomTags(hasher.HashInstance(), dicom);
277
278 return instanceUuid;
279 } 276 }
280 277
281 void ServerIndex::RemoveInstance(const std::string& uuid) 278 void ServerIndex::RemoveInstance(const std::string& uuid)
282 { 279 {
283 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Instances WHERE uuid=?"); 280 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Instances WHERE uuid=?");
284 s.BindString(0, uuid); 281 s.BindString(0, uuid);
285 s.Run(); 282 s.Run();
286 } 283 }
287 284
288 bool ServerIndex::HasSeries(std::string& seriesUuid, 285 bool ServerIndex::HasSeries(DicomInstanceHasher& hasher)
289 const DicomInstanceHasher& hasher)
290 { 286 {
291 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Series WHERE dicomSeries=?"); 287 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Series WHERE dicomSeries=?");
292 s.BindString(0, hasher.GetSeriesUid()); 288 s.BindString(0, hasher.GetSeriesUid());
293 if (s.Step()) 289 return s.Step();
294 { 290 }
295 seriesUuid = s.ColumnString(0); 291
296 return true; 292 void ServerIndex::CreateSeries(DicomInstanceHasher& hasher,
297 } 293 const DicomMap& dicomSummary)
298 else 294 {
299 {
300 return false;
301 }
302 }
303
304 std::string ServerIndex::CreateSeries(const std::string& parentStudyUuid,
305 const DicomInstanceHasher& hasher,
306 const DicomMap& dicomSummary)
307 {
308 std::string seriesUuid = Toolbox::GenerateUuid();
309
310 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)"); 295 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)");
311 s2.BindString(0, seriesUuid); 296 s2.BindString(0, hasher.HashSeries());
312 s2.BindInt(1, ResourceType_Series); 297 s2.BindInt(1, ResourceType_Series);
313 s2.Run(); 298 s2.Run();
314 299
315 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Series VALUES(?, ?, ?, ?)"); 300 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Series VALUES(?, ?, ?, ?)");
316 s.BindString(0, seriesUuid); 301 s.BindString(0, hasher.HashSeries());
317 s.BindString(1, parentStudyUuid); 302 s.BindString(1, hasher.HashStudy());
318 s.BindString(2, hasher.GetSeriesUid()); 303 s.BindString(2, hasher.GetSeriesUid());
319 304
320 const DicomValue* expectedNumberOfInstances; 305 const DicomValue* expectedNumberOfInstances;
321 if (//(expectedNumberOfInstances = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_FRAMES)) != NULL || 306 if (//(expectedNumberOfInstances = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_FRAMES)) != NULL ||
322 (expectedNumberOfInstances = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_SLICES)) != NULL || 307 (expectedNumberOfInstances = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_SLICES)) != NULL ||
330 s.BindNull(3); 315 s.BindNull(3);
331 } 316 }
332 317
333 s.Run(); 318 s.Run();
334 319
335 RecordChange("series", seriesUuid); 320 RecordChange("series", hasher.HashSeries());
336 321
337 DicomMap dicom; 322 DicomMap dicom;
338 dicomSummary.ExtractSeriesInformation(dicom); 323 dicomSummary.ExtractSeriesInformation(dicom);
339 StoreMainDicomTags(seriesUuid, dicom); 324 StoreMainDicomTags(hasher.HashSeries(), dicom);
340 325 }
341 return seriesUuid; 326
342 } 327 bool ServerIndex::HasStudy(DicomInstanceHasher& hasher)
343
344 bool ServerIndex::HasStudy(std::string& studyUuid,
345 const DicomInstanceHasher& hasher)
346 { 328 {
347 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Studies WHERE dicomStudy=?"); 329 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Studies WHERE dicomStudy=?");
348 s.BindString(0, hasher.GetStudyUid()); 330 s.BindString(0, hasher.GetStudyUid());
349 if (s.Step()) 331 return s.Step();
350 { 332 }
351 studyUuid = s.ColumnString(0); 333
352 return true; 334 void ServerIndex::CreateStudy(DicomInstanceHasher& hasher,
353 } 335 const DicomMap& dicomSummary)
354 else 336 {
355 {
356 return false;
357 }
358 }
359
360 std::string ServerIndex::CreateStudy(const std::string& parentPatientUuid,
361 const DicomInstanceHasher& hasher,
362 const DicomMap& dicomSummary)
363 {
364 std::string studyUuid = Toolbox::GenerateUuid();
365
366 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)"); 337 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)");
367 s2.BindString(0, studyUuid); 338 s2.BindString(0, hasher.HashStudy());
368 s2.BindInt(1, ResourceType_Study); 339 s2.BindInt(1, ResourceType_Study);
369 s2.Run(); 340 s2.Run();
370 341
371 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Studies VALUES(?, ?, ?)"); 342 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Studies VALUES(?, ?, ?)");
372 s.BindString(0, studyUuid); 343 s.BindString(0, hasher.HashStudy());
373 s.BindString(1, parentPatientUuid); 344 s.BindString(1, hasher.HashPatient());
374 s.BindString(2, hasher.GetStudyUid()); 345 s.BindString(2, hasher.GetStudyUid());
375 s.Run(); 346 s.Run();
376 347
377 RecordChange("studies", studyUuid); 348 RecordChange("studies", hasher.HashStudy());
378 349
379 DicomMap dicom; 350 DicomMap dicom;
380 dicomSummary.ExtractStudyInformation(dicom); 351 dicomSummary.ExtractStudyInformation(dicom);
381 StoreMainDicomTags(studyUuid, dicom); 352 StoreMainDicomTags(hasher.HashStudy(), dicom);
382 353 }
383 return studyUuid; 354
384 } 355
385 356
386 357 bool ServerIndex::HasPatient(DicomInstanceHasher& hasher)
387
388 bool ServerIndex::HasPatient(std::string& patientUuid,
389 const DicomInstanceHasher& hasher)
390 { 358 {
391 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Patients WHERE dicomPatientId=?"); 359 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Patients WHERE dicomPatientId=?");
392 s.BindString(0,hasher.GetPatientId()); 360 s.BindString(0,hasher.GetPatientId());
393 if (s.Step()) 361 return s.Step();
394 { 362 }
395 patientUuid = s.ColumnString(0); 363
396 return true; 364 void ServerIndex::CreatePatient(DicomInstanceHasher& hasher,
397 } 365 const DicomMap& dicomSummary)
398 else 366 {
399 {
400 return false;
401 }
402 }
403
404 std::string ServerIndex::CreatePatient(const DicomInstanceHasher& hasher,
405 const DicomMap& dicomSummary)
406 {
407 std::string patientUuid = Toolbox::GenerateUuid();
408 std::string dicomPatientId = dicomSummary.GetValue(DICOM_TAG_PATIENT_ID).AsString(); 367 std::string dicomPatientId = dicomSummary.GetValue(DICOM_TAG_PATIENT_ID).AsString();
409 368
410 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)"); 369 SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)");
411 s2.BindString(0, patientUuid); 370 s2.BindString(0, hasher.HashPatient());
412 s2.BindInt(1, ResourceType_Patient); 371 s2.BindInt(1, ResourceType_Patient);
413 s2.Run(); 372 s2.Run();
414 373
415 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Patients VALUES(?, ?)"); 374 SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Patients VALUES(?, ?)");
416 s.BindString(0, patientUuid); 375 s.BindString(0, hasher.HashPatient());
417 s.BindString(1, dicomPatientId); 376 s.BindString(1, dicomPatientId);
418 s.Run(); 377 s.Run();
419 378
420 RecordChange("patients", patientUuid); 379 RecordChange("patients", hasher.HashPatient());
421 380
422 DicomMap dicom; 381 DicomMap dicom;
423 dicomSummary.ExtractPatientInformation(dicom); 382 dicomSummary.ExtractPatientInformation(dicom);
424 StoreMainDicomTags(patientUuid, dicom); 383 StoreMainDicomTags(hasher.HashPatient(), dicom);
425
426 return patientUuid;
427 } 384 }
428 385
429 386
430 void ServerIndex::GetMainDicomTags(DicomMap& map, 387 void ServerIndex::GetMainDicomTags(DicomMap& map,
431 const std::string& uuid) 388 const std::string& uuid)
493 } 450 }
494 451
495 452
496 ServerIndex::ServerIndex(const std::string& storagePath) 453 ServerIndex::ServerIndex(const std::string& storagePath)
497 { 454 {
498 boost::filesystem::path p = storagePath; 455 if (storagePath == ":memory:")
499 456 {
500 try 457 db_.OpenInMemory();
501 { 458 }
502 boost::filesystem::create_directories(storagePath); 459 else
503 } 460 {
504 catch (boost::filesystem::filesystem_error) 461 boost::filesystem::path p = storagePath;
505 { 462
506 } 463 try
507 464 {
508 p /= "index"; 465 boost::filesystem::create_directories(storagePath);
509 db_.Open(p.string()); 466 }
467 catch (boost::filesystem::filesystem_error)
468 {
469 }
470
471 p /= "index";
472 db_.Open(p.string());
473 }
474
510 db_.Register(new Internals::DeleteFromFileStorageFunction(storagePath)); 475 db_.Register(new Internals::DeleteFromFileStorageFunction(storagePath));
511 deletedLevels_ = (Internals::SignalDeletedLevelFunction*) 476 deletedLevels_ = (Internals::SignalDeletedLevelFunction*)
512 db_.Register(new Internals::SignalDeletedLevelFunction); 477 db_.Register(new Internals::SignalDeletedLevelFunction);
513 478
514 if (!db_.DoesTableExist("GlobalProperties")) 479 if (!db_.DoesTableExist("GlobalProperties"))
535 try 500 try
536 { 501 {
537 SQLite::Transaction t(db_); 502 SQLite::Transaction t(db_);
538 t.Begin(); 503 t.Begin();
539 504
540 if (HasInstance(instanceUuid, hasher)) 505 if (HasInstance(hasher))
541 { 506 {
542 return StoreStatus_AlreadyStored; 507 return StoreStatus_AlreadyStored;
543 // TODO: Check consistency? 508 // TODO: Check consistency?
544 } 509 }
545 510
546 std::string patientUuid; 511 if (HasPatient(hasher))
547 if (HasPatient(patientUuid, hasher))
548 { 512 {
549 // TODO: Check consistency? 513 // TODO: Check consistency?
550 } 514 }
551 else 515 else
552 { 516 {
553 patientUuid = CreatePatient(hasher, dicomSummary); 517 CreatePatient(hasher, dicomSummary);
554 } 518 }
555 519
556 std::string studyUuid; 520 if (HasStudy(hasher))
557 if (HasStudy(studyUuid, hasher))
558 { 521 {
559 // TODO: Check consistency? 522 // TODO: Check consistency?
560 } 523 }
561 else 524 else
562 { 525 {
563 studyUuid = CreateStudy(patientUuid, hasher, dicomSummary); 526 CreateStudy(hasher, dicomSummary);
564 } 527 }
565 528
566 std::string seriesUuid; 529 if (HasSeries(hasher))
567 if (HasSeries(seriesUuid, hasher))
568 { 530 {
569 // TODO: Check consistency? 531 // TODO: Check consistency?
570 } 532 }
571 else 533 else
572 { 534 {
573 seriesUuid = CreateSeries(studyUuid, hasher, dicomSummary); 535 CreateSeries(hasher, dicomSummary);
574 } 536 }
575 537
576 instanceUuid = CreateInstance(seriesUuid, hasher, dicomSummary, fileUuid, 538 CreateInstance(hasher, dicomSummary, fileUuid,
577 uncompressedFileSize, jsonUuid, distantAet); 539 uncompressedFileSize, jsonUuid, distantAet);
578 540
579 t.Commit(); 541 t.Commit();
580 return StoreStatus_Success; 542 return StoreStatus_Success;
581 //t.Rollback(); 543 //t.Rollback();
582 } 544 }