Mercurial > hg > orthanc
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 } |