# HG changeset patch # User Sebastien Jodogne # Date 1545062728 -3600 # Node ID 039a9d262d64ad64219fdaca197a0d937bdc7906 # Parent ef17a587e10d443f09a121f034a4a9a6fd02de3d preparing to speed up find in databases diff -r ef17a587e10d -r 039a9d262d64 OrthancServer/IDatabaseWrapper.h --- a/OrthancServer/IDatabaseWrapper.h Mon Dec 17 10:26:01 2018 +0100 +++ b/OrthancServer/IDatabaseWrapper.h Mon Dec 17 17:05:28 2018 +0100 @@ -34,11 +34,13 @@ #pragma once #include "../Core/DicomFormat/DicomMap.h" -#include "../Core/SQLite/ITransaction.h" +#include "../Core/FileStorage/FileInfo.h" #include "../Core/FileStorage/IStorageArea.h" -#include "../Core/FileStorage/FileInfo.h" +#include "../Core/SQLite/ITransaction.h" + +#include "ExportedResource.h" #include "IDatabaseListener.h" -#include "ExportedResource.h" +#include "Search/DatabaseLookup.h" #include #include @@ -223,5 +225,18 @@ IStorageArea& storageArea) = 0; virtual bool IsDiskSizeAbove(uint64_t threshold) = 0; + + virtual void FindOneChildInstance(std::vector& instancesId, + const std::vector& resourcesId, + ResourceType level) = 0; + + virtual void ApplyLookupPatients(std::vector& patientsId, + const DatabaseLookup& lookup, + size_t limit) = 0; + + virtual void ApplyLookupResources(std::vector& resourcesId, + const DatabaseLookup& lookup, + ResourceType queryLevel, + size_t limit) = 0; }; } diff -r ef17a587e10d -r 039a9d262d64 OrthancServer/OrthancFindRequestHandler.cpp --- a/OrthancServer/OrthancFindRequestHandler.cpp Mon Dec 17 10:26:01 2018 +0100 +++ b/OrthancServer/OrthancFindRequestHandler.cpp Mon Dec 17 17:05:28 2018 +0100 @@ -615,6 +615,7 @@ **/ LookupResource lookup(level); + DatabaseLookup lookup2; bool caseSensitivePN; @@ -655,6 +656,7 @@ } lookup.AddDicomConstraint(tag, value, sensitive); + lookup2.AddDicomConstraint(tag, value, sensitive, true /* mandatory */); } else { @@ -672,7 +674,7 @@ LookupVisitor visitor(answers, context_, level, *filteredInput, sequencesToReturn); - context_.Apply(visitor, lookup, 0 /* "since" is not relevant to C-FIND */, limit); + context_.Apply(visitor, lookup, lookup2, 0 /* "since" is not relevant to C-FIND */, limit); } diff -r ef17a587e10d -r 039a9d262d64 OrthancServer/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Mon Dec 17 10:26:01 2018 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Mon Dec 17 17:05:28 2018 +0100 @@ -40,6 +40,7 @@ #include "../../Core/HttpServer/HttpContentNegociation.h" #include "../../Core/Logging.h" #include "../OrthancConfiguration.h" +#include "../Search/DatabaseLookup.h" #include "../Search/LookupResource.h" #include "../ServerContext.h" #include "../ServerToolbox.h" @@ -1403,6 +1404,7 @@ std::string level = request[KEY_LEVEL].asString(); LookupResource query(StringToResourceType(level.c_str())); + DatabaseLookup query2; Json::Value::Members members = request[KEY_QUERY].getMemberNames(); for (size_t i = 0; i < members.size(); i++) @@ -1416,10 +1418,13 @@ query.AddDicomConstraint(FromDcmtkBridge::ParseTag(members[i]), request[KEY_QUERY][members[i]].asString(), caseSensitive); + query2.AddDicomConstraint(FromDcmtkBridge::ParseTag(members[i]), + request[KEY_QUERY][members[i]].asString(), + caseSensitive, true); } FindVisitor visitor; - context.Apply(visitor, query, since, limit); + context.Apply(visitor, query, query2, since, limit); visitor.Answer(call.GetOutput(), context.GetIndex(), query.GetLevel(), expand); } } diff -r ef17a587e10d -r 039a9d262d64 OrthancServer/SQLiteDatabaseWrapper.cpp --- a/OrthancServer/SQLiteDatabaseWrapper.cpp Mon Dec 17 10:26:01 2018 +0100 +++ b/OrthancServer/SQLiteDatabaseWrapper.cpp Mon Dec 17 17:05:28 2018 +0100 @@ -54,7 +54,7 @@ public: SignalFileDeleted(IDatabaseListener& listener) : - listener_(listener) + listener_(listener) { } @@ -101,7 +101,7 @@ public: SignalResourceDeleted(IDatabaseListener& listener) : - listener_(listener) + listener_(listener) { } @@ -1201,4 +1201,247 @@ { return GetTotalCompressedSize() > threshold; } + + + namespace + { + class MainDicomTagsRegistry : public boost::noncopyable + { + private: + class TagInfo + { + private: + ResourceType level_; + DicomTagType type_; + + public: + TagInfo() + { + } + + TagInfo(ResourceType level, + DicomTagType type) : + level_(level), + type_(type) + { + } + + ResourceType GetLevel() const + { + return level_; + } + + DicomTagType GetType() const + { + return type_; + } + }; + + typedef std::map Registry; + + + Registry registry_; + + void LoadTags(ResourceType level) + { + const DicomTag* tags = NULL; + size_t size; + + ServerToolbox::LoadIdentifiers(tags, size, level); + + for (size_t i = 0; i < size; i++) + { + if (registry_.find(tags[i]) == registry_.end()) + { + registry_[tags[i]] = TagInfo(level, DicomTagType_Identifier); + } + else + { + // These patient-level tags are copied in the study level + assert(level == ResourceType_Study && + (tags[i] == DICOM_TAG_PATIENT_ID || + tags[i] == DICOM_TAG_PATIENT_NAME || + tags[i] == DICOM_TAG_PATIENT_BIRTH_DATE)); + } + } + + DicomMap::LoadMainDicomTags(tags, size, level); + + for (size_t i = 0; i < size; i++) + { + if (registry_.find(tags[i]) == registry_.end()) + { + registry_[tags[i]] = TagInfo(level, DicomTagType_Main); + } + } + } + + public: + MainDicomTagsRegistry() + { + LoadTags(ResourceType_Patient); + LoadTags(ResourceType_Study); + LoadTags(ResourceType_Series); + LoadTags(ResourceType_Instance); + } + + void LookupTag(ResourceType& level, + DicomTagType& type, + const DicomTag& tag) const + { + Registry::const_iterator it = registry_.find(tag); + + if (it == registry_.end()) + { + // Default values + level = ResourceType_Instance; + type = DicomTagType_Generic; + } + else + { + level = it->second.GetLevel(); + type = it->second.GetType(); + } + } + }; + } + + + void SQLiteDatabaseWrapper::FindOneChildInstance(std::vector& instancesId, + const std::vector& resourcesId, + ResourceType level) + { + printf("ICI 3\n"); + + throw OrthancException(ErrorCode_NotImplemented); + } + + + void SQLiteDatabaseWrapper::ApplyLookupPatients(std::vector& patientsId, + const DatabaseLookup& lookup, + size_t limit) + { + static const MainDicomTagsRegistry registry; + + printf("ICI 1\n"); + + std::string heading = "SELECT patient.publicId FROM Resources AS patient "; + std::string trailer; + std::vector parameters; + + for (size_t i = 0; i < lookup.GetConstraintsCount(); i++) + { + const DicomTagConstraint& constraint = lookup.GetConstraint(i); + + ResourceType level; + DicomTagType type; + registry.LookupTag(level, type, constraint.GetTag()); + + if (level != ResourceType_Patient) + { + throw OrthancException(ErrorCode_ParameterOutOfRange, + "Not a patient-level tag: (" + + lookup.GetConstraint(i).GetTag().Format() + ")"); + } + + if (type == DicomTagType_Identifier || + type == DicomTagType_Main) + { + std::string table = (type == DicomTagType_Identifier ? "DicomIdentifiers" : "MainDicomTags"); + std::string tag = "t" + boost::lexical_cast(i); + + char on[128]; + sprintf(on, " %s ON %s.id = patient.internalId AND %s.tagGroup = 0x%04x AND %s.tagElement = 0x%04x ", + tag.c_str(), tag.c_str(), tag.c_str(), constraint.GetTag().GetGroup(), + tag.c_str(), constraint.GetTag().GetElement()); + + if (constraint.IsMandatory()) + { + heading += "INNER JOIN " + table + std::string(on); + } + else + { + heading += "LEFT JOIN " + table + std::string(on); + } + + trailer += "AND ("; + + if (!constraint.IsMandatory()) + { + trailer += tag + ".value IS NULL OR "; + } + + if (constraint.IsCaseSensitive()) + { + trailer += tag + ".value "; + } + else + { + trailer += "lower(" + tag + ".value) "; + } + + switch (constraint.GetType()) + { + case ConstraintType_Equal: + parameters.push_back(constraint.GetValue()); + + if (constraint.IsCaseSensitive()) + { + trailer += "= ?"; + } + else + { + trailer += "= lower(?)"; + } + + break; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + + trailer += ") "; + } + } + + if (limit != 0) + { + trailer += " LIMIT " + boost::lexical_cast(limit); + } + + std::string sql = (heading + "WHERE patient.resourceType = " + + boost::lexical_cast(ResourceType_Patient) + " " + trailer); + + SQLite::Statement s(db_, sql); + + printf("[%s]\n", sql.c_str()); + + for (size_t i = 0; i < parameters.size(); i++) + { + printf(" %d = '%s'\n", i, parameters[i].c_str()); + s.BindString(i, parameters[i]); + } + + patientsId.clear(); + + while (s.Step()) + { + std::string publicId = s.ColumnString(0); + patientsId.push_back(publicId); + printf("** [%s]\n", publicId.c_str()); + } + + throw OrthancException(ErrorCode_NotImplemented); + } + + + void SQLiteDatabaseWrapper::ApplyLookupResources(std::vector& resourcesId, + const DatabaseLookup& lookup, + ResourceType queryLevel, + size_t limit) + { + printf("ICI 2\n"); + + throw OrthancException(ErrorCode_NotImplemented); + } } diff -r ef17a587e10d -r 039a9d262d64 OrthancServer/SQLiteDatabaseWrapper.h --- a/OrthancServer/SQLiteDatabaseWrapper.h Mon Dec 17 10:26:01 2018 +0100 +++ b/OrthancServer/SQLiteDatabaseWrapper.h Mon Dec 17 17:05:28 2018 +0100 @@ -276,5 +276,18 @@ const std::string& end); virtual bool IsDiskSizeAbove(uint64_t threshold); + + virtual void FindOneChildInstance(std::vector& instancesId, + const std::vector& resourcesId, + ResourceType level); + + virtual void ApplyLookupPatients(std::vector& patientsId, + const DatabaseLookup& lookup, + size_t limit); + + virtual void ApplyLookupResources(std::vector& resourcesId, + const DatabaseLookup& lookup, + ResourceType queryLevel, + size_t limit); }; } diff -r ef17a587e10d -r 039a9d262d64 OrthancServer/Search/DatabaseLookup.cpp --- a/OrthancServer/Search/DatabaseLookup.cpp Mon Dec 17 10:26:01 2018 +0100 +++ b/OrthancServer/Search/DatabaseLookup.cpp Mon Dec 17 17:05:28 2018 +0100 @@ -39,50 +39,6 @@ namespace Orthanc { - void DatabaseLookup::LoadTags(ResourceType level) - { - const DicomTag* tags = NULL; - size_t size; - - ServerToolbox::LoadIdentifiers(tags, size, level); - - for (size_t i = 0; i < size; i++) - { - if (tags_.find(tags[i]) == tags_.end()) - { - tags_[tags[i]] = TagInfo(DicomTagType_Identifier, level); - } - else - { - // These patient-level tags are copied in the study level - assert(level == ResourceType_Study && - (tags[i] == DICOM_TAG_PATIENT_ID || - tags[i] == DICOM_TAG_PATIENT_NAME || - tags[i] == DICOM_TAG_PATIENT_BIRTH_DATE)); - } - } - - DicomMap::LoadMainDicomTags(tags, size, level); - - for (size_t i = 0; i < size; i++) - { - if (tags_.find(tags[i]) == tags_.end()) - { - tags_[tags[i]] = TagInfo(DicomTagType_Main, level); - } - } - } - - - DatabaseLookup::DatabaseLookup() - { - LoadTags(ResourceType_Patient); - LoadTags(ResourceType_Study); - LoadTags(ResourceType_Series); - LoadTags(ResourceType_Instance); - } - - DatabaseLookup::~DatabaseLookup() { for (size_t i = 0; i < constraints_.size(); i++) @@ -116,17 +72,6 @@ else { constraints_.push_back(constraint); - - std::map::const_iterator tag = tags_.find(constraint->GetTag()); - - if (tag == tags_.end()) - { - constraint->SetTagInfo(DicomTagType_Generic, ResourceType_Instance); - } - else - { - constraint->SetTagInfo(tag->second.GetType(), tag->second.GetLevel()); - } } } @@ -148,7 +93,8 @@ void DatabaseLookup::AddDicomConstraint(const DicomTag& tag, const std::string& dicomQuery, - bool caseSensitivePN) + bool caseSensitivePN, + bool mandatoryTag) { ValueRepresentation vr = FromDcmtkBridge::LookupValueRepresentation(tag); @@ -214,13 +160,13 @@ if (!lower.empty()) { AddConstraint(new DicomTagConstraint - (tag, ConstraintType_GreaterOrEqual, lower, caseSensitive)); + (tag, ConstraintType_GreaterOrEqual, lower, caseSensitive, mandatoryTag)); } if (!upper.empty()) { AddConstraint(new DicomTagConstraint - (tag, ConstraintType_SmallerOrEqual, upper, caseSensitive)); + (tag, ConstraintType_SmallerOrEqual, upper, caseSensitive, mandatoryTag)); } } else if (dicomQuery.find('\\') != std::string::npos) @@ -235,7 +181,7 @@ } std::auto_ptr constraint - (new DicomTagConstraint(fixedTag, ConstraintType_List, caseSensitive)); + (new DicomTagConstraint(fixedTag, ConstraintType_List, caseSensitive, mandatoryTag)); std::vector items; Toolbox::TokenizeString(items, dicomQuery, '\\'); @@ -251,12 +197,12 @@ dicomQuery.find('?') != std::string::npos) { AddConstraint(new DicomTagConstraint - (tag, ConstraintType_Wildcard, dicomQuery, caseSensitive)); + (tag, ConstraintType_Wildcard, dicomQuery, caseSensitive, mandatoryTag)); } else { AddConstraint(new DicomTagConstraint - (tag, ConstraintType_Equal, dicomQuery, caseSensitive)); + (tag, ConstraintType_Equal, dicomQuery, caseSensitive, mandatoryTag)); } } } diff -r ef17a587e10d -r 039a9d262d64 OrthancServer/Search/DatabaseLookup.h --- a/OrthancServer/Search/DatabaseLookup.h Mon Dec 17 10:26:01 2018 +0100 +++ b/OrthancServer/Search/DatabaseLookup.h Mon Dec 17 17:05:28 2018 +0100 @@ -40,44 +40,12 @@ class DatabaseLookup : public boost::noncopyable { private: - class TagInfo - { - private: - DicomTagType type_; - ResourceType level_; - - public: - TagInfo() : - type_(DicomTagType_Generic), - level_(ResourceType_Instance) - { - } - - TagInfo(DicomTagType type, - ResourceType level) : - type_(type), - level_(level) - { - } - - DicomTagType GetType() const - { - return type_; - } - - ResourceType GetLevel() const - { - return level_; - } - }; - std::vector constraints_; - std::map tags_; - - void LoadTags(ResourceType level); public: - DatabaseLookup(); + DatabaseLookup() + { + } ~DatabaseLookup(); @@ -99,6 +67,7 @@ void AddDicomConstraint(const DicomTag& tag, const std::string& dicomQuery, - bool caseSensitivePN); + bool caseSensitivePN, + bool mandatoryTag); }; } diff -r ef17a587e10d -r 039a9d262d64 OrthancServer/Search/DicomTagConstraint.cpp --- a/OrthancServer/Search/DicomTagConstraint.cpp Mon Dec 17 10:26:01 2018 +0100 +++ b/OrthancServer/Search/DicomTagConstraint.cpp Mon Dec 17 17:05:28 2018 +0100 @@ -97,13 +97,12 @@ DicomTagConstraint::DicomTagConstraint(const DicomTag& tag, ConstraintType type, const std::string& value, - bool caseSensitive) : - hasTagInfo_(false), - tagType_(DicomTagType_Generic), // Dummy initialization - level_(ResourceType_Patient), // Dummy initialization + bool caseSensitive, + bool mandatory) : tag_(tag), constraintType_(type), - caseSensitive_(caseSensitive) + caseSensitive_(caseSensitive), + mandatory_(mandatory) { if (type == ConstraintType_Equal || type == ConstraintType_SmallerOrEqual || @@ -128,13 +127,12 @@ DicomTagConstraint::DicomTagConstraint(const DicomTag& tag, ConstraintType type, - bool caseSensitive) : - hasTagInfo_(false), - tagType_(DicomTagType_Generic), // Dummy initialization - level_(ResourceType_Patient), // Dummy initialization + bool caseSensitive, + bool mandatory) : tag_(tag), constraintType_(type), - caseSensitive_(caseSensitive) + caseSensitive_(caseSensitive), + mandatory_(mandatory) { if (type != ConstraintType_List) { @@ -143,41 +141,6 @@ } - void DicomTagConstraint::SetTagInfo(DicomTagType tagType, - ResourceType level) - { - hasTagInfo_ = true; - tagType_ = tagType; - level_ = level; - } - - - DicomTagType DicomTagConstraint::GetTagType() const - { - if (!hasTagInfo_) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - else - { - return tagType_; - } - } - - - const ResourceType DicomTagConstraint::GetLevel() const - { - if (!hasTagInfo_) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - else - { - return level_; - } - } - - void DicomTagConstraint::AddValue(const std::string& value) { if (constraintType_ != ConstraintType_List) @@ -268,8 +231,18 @@ const DicomValue* tmp = value.TestAndGetValue(tag_); if (tmp == NULL || - tmp->IsNull() || - tmp->IsBinary()) + tmp->IsNull()) + { + if (mandatory_) + { + return false; + } + else + { + return true; + } + } + else if (tmp->IsBinary()) { return false; } diff -r ef17a587e10d -r 039a9d262d64 OrthancServer/Search/DicomTagConstraint.h --- a/OrthancServer/Search/DicomTagConstraint.h Mon Dec 17 10:26:01 2018 +0100 +++ b/OrthancServer/Search/DicomTagConstraint.h Mon Dec 17 17:05:28 2018 +0100 @@ -46,13 +46,11 @@ class NormalizedString; class RegularExpression; - bool hasTagInfo_; - DicomTagType tagType_; - ResourceType level_; DicomTag tag_; ConstraintType constraintType_; std::set values_; bool caseSensitive_; + bool mandatory_; boost::shared_ptr regex_; @@ -60,30 +58,21 @@ DicomTagConstraint(const DicomTag& tag, ConstraintType type, const std::string& value, - bool caseSensitive); + bool caseSensitive, + bool mandatory); + // For list search DicomTagConstraint(const DicomTag& tag, ConstraintType type, - bool caseSensitive); - - bool HasTagInfo() const - { - return hasTagInfo_; - } - - void SetTagInfo(DicomTagType tagType, - ResourceType level); - - DicomTagType GetTagType() const; - - const ResourceType GetLevel() const; + bool caseSensitive, + bool mandatory); const DicomTag& GetTag() const { return tag_; } - ConstraintType GetConstraintType() const + ConstraintType GetType() const { return constraintType_; } @@ -93,6 +82,11 @@ return caseSensitive_; } + bool IsMandatory() const + { + return mandatory_; + } + void AddValue(const std::string& value); const std::string& GetValue() const; diff -r ef17a587e10d -r 039a9d262d64 OrthancServer/ServerContext.cpp --- a/OrthancServer/ServerContext.cpp Mon Dec 17 10:26:01 2018 +0100 +++ b/OrthancServer/ServerContext.cpp Mon Dec 17 17:05:28 2018 +0100 @@ -775,6 +775,7 @@ void ServerContext::Apply(ILookupVisitor& visitor, const ::Orthanc::LookupResource& lookup, + const DatabaseLookup& lookup2, size_t since, size_t limit) { @@ -810,6 +811,34 @@ std::vector resources, instances; GetIndex().FindCandidates(resources, instances, lookup); +#if 1 + { + std::vector resources2, instances2; + + if (lookup.GetLevel() == ResourceType_Patient) + { + GetIndex().ApplyLookupPatients(resources2, instances2, lookup2, limit /* TODO */); + } + else + { + GetIndex().ApplyLookupResources(resources2, instances2, lookup2, lookup.GetLevel(), limit /* TODO */); + } + + std::set r; + for (size_t i = 0; i < resources2.size(); i++) + { + r.insert(resources[i]); + } + + assert(r.size() == resources.size()); + + for (size_t i = 0; i < resources.size(); i++) + { + assert(r.find(resources[i]) != r.end()); + } + } +#endif + LOG(INFO) << "Number of candidate resources after fast DB filtering on main DICOM tags: " << resources.size(); assert(resources.size() == instances.size()); diff -r ef17a587e10d -r 039a9d262d64 OrthancServer/ServerContext.h --- a/OrthancServer/ServerContext.h Mon Dec 17 10:26:01 2018 +0100 +++ b/OrthancServer/ServerContext.h Mon Dec 17 17:05:28 2018 +0100 @@ -364,6 +364,7 @@ void Apply(ILookupVisitor& visitor, const ::Orthanc::LookupResource& lookup, + const DatabaseLookup& lookup2, size_t since, size_t limit); diff -r ef17a587e10d -r 039a9d262d64 OrthancServer/ServerIndex.cpp --- a/OrthancServer/ServerIndex.cpp Mon Dec 17 10:26:01 2018 +0100 +++ b/OrthancServer/ServerIndex.cpp Mon Dec 17 17:05:28 2018 +0100 @@ -2452,4 +2452,29 @@ LOG(ERROR) << "EXCEPTION [" << e.What() << "]"; } } + + + void ServerIndex::ApplyLookupPatients(std::vector& patientsId, + std::vector& instancesId, + const DatabaseLookup& lookup, + size_t limit) + { + boost::mutex::scoped_lock lock(mutex_); + + db_.ApplyLookupPatients(patientsId, lookup, limit); + db_.FindOneChildInstance(instancesId, patientsId, ResourceType_Patient); + } + + + void ServerIndex::ApplyLookupResources(std::vector& resourcesId, + std::vector& instancesId, + const DatabaseLookup& lookup, + ResourceType queryLevel, + size_t limit) + { + boost::mutex::scoped_lock lock(mutex_); + + db_.ApplyLookupResources(resourcesId, lookup, queryLevel, limit); + db_.FindOneChildInstance(instancesId, resourcesId, queryLevel); + } } diff -r ef17a587e10d -r 039a9d262d64 OrthancServer/ServerIndex.h --- a/OrthancServer/ServerIndex.h Mon Dec 17 10:26:01 2018 +0100 +++ b/OrthancServer/ServerIndex.h Mon Dec 17 17:05:28 2018 +0100 @@ -292,5 +292,16 @@ ResourceType parentType); void ReconstructInstance(ParsedDicomFile& dicom); + + void ApplyLookupPatients(std::vector& patientsId, + std::vector& instancesId, + const DatabaseLookup& lookup, + size_t limit); + + void ApplyLookupResources(std::vector& resourcesId, + std::vector& instancesId, + const DatabaseLookup& lookup, + ResourceType queryLevel, + size_t limit); }; } diff -r ef17a587e10d -r 039a9d262d64 Plugins/Engine/OrthancPluginDatabase.cpp --- a/Plugins/Engine/OrthancPluginDatabase.cpp Mon Dec 17 10:26:01 2018 +0100 +++ b/Plugins/Engine/OrthancPluginDatabase.cpp Mon Dec 17 17:05:28 2018 +0100 @@ -1160,4 +1160,29 @@ return currentDiskSize_ > threshold; } } + + + void OrthancPluginDatabase::FindOneChildInstance(std::vector& instancesId, + const std::vector& resourcesId, + ResourceType level) + { + throw OrthancException(ErrorCode_NotImplemented); + } + + + void OrthancPluginDatabase::ApplyLookupPatients(std::vector& patientsId, + const DatabaseLookup& lookup, + size_t limit) + { + throw OrthancException(ErrorCode_NotImplemented); + } + + + void OrthancPluginDatabase::ApplyLookupResources(std::vector& patientsId, + const DatabaseLookup& lookup, + ResourceType queryLevel, + size_t limit) + { + throw OrthancException(ErrorCode_NotImplemented); + } } diff -r ef17a587e10d -r 039a9d262d64 Plugins/Engine/OrthancPluginDatabase.h --- a/Plugins/Engine/OrthancPluginDatabase.h Mon Dec 17 10:26:01 2018 +0100 +++ b/Plugins/Engine/OrthancPluginDatabase.h Mon Dec 17 17:05:28 2018 +0100 @@ -270,6 +270,19 @@ void AnswerReceived(const _OrthancPluginDatabaseAnswer& answer); virtual bool IsDiskSizeAbove(uint64_t threshold); + + virtual void FindOneChildInstance(std::vector& instancesId, + const std::vector& resourcesId, + ResourceType level); + + virtual void ApplyLookupPatients(std::vector& patientsId, + const DatabaseLookup& lookup, + size_t limit); + + virtual void ApplyLookupResources(std::vector& patientsId, + const DatabaseLookup& lookup, + ResourceType queryLevel, + size_t limit); }; } diff -r ef17a587e10d -r 039a9d262d64 UnitTestsSources/DatabaseLookupTests.cpp --- a/UnitTestsSources/DatabaseLookupTests.cpp Mon Dec 17 10:26:01 2018 +0100 +++ b/UnitTestsSources/DatabaseLookupTests.cpp Mon Dec 17 17:05:28 2018 +0100 @@ -44,27 +44,18 @@ { { ASSERT_THROW(DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_Equal, - "HEL*LO", true), OrthancException); + "HEL*LO", true, true), OrthancException); ASSERT_THROW(DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_Equal, - "HEL?LO", true), OrthancException); + "HEL?LO", true, true), OrthancException); ASSERT_THROW(DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_Equal, - true), OrthancException); + true, true), OrthancException); - DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_Equal, "HELLO", true); + DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_Equal, "HELLO", true, true); ASSERT_TRUE(tag.IsMatch("HELLO")); ASSERT_FALSE(tag.IsMatch("hello")); ASSERT_TRUE(tag.IsCaseSensitive()); - ASSERT_EQ(ConstraintType_Equal, tag.GetConstraintType()); - - ASSERT_FALSE(tag.HasTagInfo()); - ASSERT_THROW(tag.GetTagType(), OrthancException); - ASSERT_THROW(tag.GetLevel(), OrthancException); - - tag.SetTagInfo(DicomTagType_Identifier, ResourceType_Series); - ASSERT_TRUE(tag.HasTagInfo()); - ASSERT_EQ(DicomTagType_Identifier, tag.GetTagType()); - ASSERT_EQ(ResourceType_Series, tag.GetLevel()); + ASSERT_EQ(ConstraintType_Equal, tag.GetType()); DicomMap m; ASSERT_FALSE(tag.IsMatch(m)); @@ -77,7 +68,7 @@ } { - DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_Equal, "HELlo", false); + DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_Equal, "HELlo", false, true); ASSERT_TRUE(tag.IsMatch("HELLO")); ASSERT_TRUE(tag.IsMatch("hello")); @@ -85,7 +76,7 @@ } { - DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_Wildcard, "HE*L?O", true); + DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_Wildcard, "HE*L?O", true, true); ASSERT_TRUE(tag.IsMatch("HELLO")); ASSERT_TRUE(tag.IsMatch("HELLLLLO")); ASSERT_TRUE(tag.IsMatch("HELxO")); @@ -93,32 +84,34 @@ } { - DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_Wildcard, "HE*l?o", false); + DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_Wildcard, "HE*l?o", false, true); ASSERT_TRUE(tag.IsMatch("HELLO")); ASSERT_TRUE(tag.IsMatch("HELLLLLO")); ASSERT_TRUE(tag.IsMatch("HELxO")); ASSERT_TRUE(tag.IsMatch("hello")); ASSERT_FALSE(tag.IsCaseSensitive()); - ASSERT_EQ(ConstraintType_Wildcard, tag.GetConstraintType()); + ASSERT_EQ(ConstraintType_Wildcard, tag.GetType()); } { - DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_SmallerOrEqual, "123", true); + DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_SmallerOrEqual, "123", true, true); ASSERT_TRUE(tag.IsMatch("120")); ASSERT_TRUE(tag.IsMatch("123")); ASSERT_FALSE(tag.IsMatch("124")); + ASSERT_TRUE(tag.IsMandatory()); } { - DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_GreaterOrEqual, "123", true); + DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_GreaterOrEqual, "123", true, false); ASSERT_FALSE(tag.IsMatch("122")); ASSERT_TRUE(tag.IsMatch("123")); ASSERT_TRUE(tag.IsMatch("124")); + ASSERT_FALSE(tag.IsMandatory()); } { - DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_List, true); + DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_List, true, true); ASSERT_FALSE(tag.IsMatch("CT")); ASSERT_FALSE(tag.IsMatch("MR")); @@ -137,7 +130,7 @@ } { - DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_List, false); + DicomTagConstraint tag(DICOM_TAG_PATIENT_NAME, ConstraintType_List, false, true); tag.AddValue("ct"); tag.AddValue("mr"); @@ -155,20 +148,16 @@ { { DatabaseLookup lookup; - lookup.AddDicomConstraint(DICOM_TAG_PATIENT_ID, "HELLO", true); + lookup.AddDicomConstraint(DICOM_TAG_PATIENT_ID, "HELLO", true, true); ASSERT_EQ(1u, lookup.GetConstraintsCount()); - ASSERT_EQ(ConstraintType_Equal, lookup.GetConstraint(0).GetConstraintType()); + ASSERT_EQ(ConstraintType_Equal, lookup.GetConstraint(0).GetType()); ASSERT_EQ("HELLO", lookup.GetConstraint(0).GetValue()); ASSERT_TRUE(lookup.GetConstraint(0).IsCaseSensitive()); - - ASSERT_TRUE(lookup.GetConstraint(0).HasTagInfo()); - ASSERT_EQ(DicomTagType_Identifier, lookup.GetConstraint(0).GetTagType()); - ASSERT_EQ(ResourceType_Patient, lookup.GetConstraint(0).GetLevel()); } { DatabaseLookup lookup; - lookup.AddDicomConstraint(DICOM_TAG_PATIENT_ID, "HELLO", false); + lookup.AddDicomConstraint(DICOM_TAG_PATIENT_ID, "HELLO", false, true); ASSERT_EQ(1u, lookup.GetConstraintsCount()); // This is *not* a PN VR => "false" above is *not* used @@ -177,14 +166,14 @@ { DatabaseLookup lookup; - lookup.AddDicomConstraint(DICOM_TAG_PATIENT_NAME, "HELLO", true); + lookup.AddDicomConstraint(DICOM_TAG_PATIENT_NAME, "HELLO", true, true); ASSERT_EQ(1u, lookup.GetConstraintsCount()); ASSERT_TRUE(lookup.GetConstraint(0).IsCaseSensitive()); } { DatabaseLookup lookup; - lookup.AddDicomConstraint(DICOM_TAG_PATIENT_NAME, "HELLO", false); + lookup.AddDicomConstraint(DICOM_TAG_PATIENT_NAME, "HELLO", false, true); ASSERT_EQ(1u, lookup.GetConstraintsCount()); // This is a PN VR => "false" above is used @@ -193,59 +182,54 @@ { DatabaseLookup lookup; - lookup.AddDicomConstraint(DICOM_TAG_SERIES_DESCRIPTION, "2012-2016", false); - - ASSERT_TRUE(lookup.GetConstraint(0).HasTagInfo()); - ASSERT_EQ(DicomTagType_Main, lookup.GetConstraint(0).GetTagType()); - ASSERT_EQ(ResourceType_Series, lookup.GetConstraint(0).GetLevel()); + lookup.AddDicomConstraint(DICOM_TAG_SERIES_DESCRIPTION, "2012-2016", false, true); // This is not a data VR - ASSERT_EQ(ConstraintType_Equal, lookup.GetConstraint(0).GetConstraintType()); + ASSERT_EQ(ConstraintType_Equal, lookup.GetConstraint(0).GetType()); } { DatabaseLookup lookup; - lookup.AddDicomConstraint(DICOM_TAG_PATIENT_BIRTH_DATE, "2012-2016", false); + lookup.AddDicomConstraint(DICOM_TAG_PATIENT_BIRTH_DATE, "2012-2016", false, true); // This is a data VR => range is effective ASSERT_EQ(2u, lookup.GetConstraintsCount()); - ASSERT_TRUE(lookup.GetConstraint(0).GetConstraintType() != lookup.GetConstraint(1).GetConstraintType()); + ASSERT_TRUE(lookup.GetConstraint(0).GetType() != lookup.GetConstraint(1).GetType()); for (size_t i = 0; i < 2; i++) { - ASSERT_TRUE(lookup.GetConstraint(i).GetConstraintType() == ConstraintType_SmallerOrEqual || - lookup.GetConstraint(i).GetConstraintType() == ConstraintType_GreaterOrEqual); + ASSERT_TRUE(lookup.GetConstraint(i).GetType() == ConstraintType_SmallerOrEqual || + lookup.GetConstraint(i).GetType() == ConstraintType_GreaterOrEqual); } } { DatabaseLookup lookup; - lookup.AddDicomConstraint(DICOM_TAG_PATIENT_BIRTH_DATE, "2012-", false); + lookup.AddDicomConstraint(DICOM_TAG_PATIENT_BIRTH_DATE, "2012-", false, true); ASSERT_EQ(1u, lookup.GetConstraintsCount()); - ASSERT_EQ(ConstraintType_GreaterOrEqual, lookup.GetConstraint(0).GetConstraintType()); + ASSERT_EQ(ConstraintType_GreaterOrEqual, lookup.GetConstraint(0).GetType()); ASSERT_EQ("2012", lookup.GetConstraint(0).GetValue()); } { DatabaseLookup lookup; - lookup.AddDicomConstraint(DICOM_TAG_PATIENT_BIRTH_DATE, "-2016", false); + lookup.AddDicomConstraint(DICOM_TAG_PATIENT_BIRTH_DATE, "-2016", false, true); ASSERT_EQ(1u, lookup.GetConstraintsCount()); ASSERT_EQ(DICOM_TAG_PATIENT_BIRTH_DATE, lookup.GetConstraint(0).GetTag()); - ASSERT_EQ(ConstraintType_SmallerOrEqual, lookup.GetConstraint(0).GetConstraintType()); + ASSERT_EQ(ConstraintType_SmallerOrEqual, lookup.GetConstraint(0).GetType()); ASSERT_EQ("2016", lookup.GetConstraint(0).GetValue()); } { DatabaseLookup lookup; - lookup.AddDicomConstraint(DICOM_TAG_MODALITIES_IN_STUDY, "CT\\MR", false); + lookup.AddDicomConstraint(DICOM_TAG_MODALITIES_IN_STUDY, "CT\\MR", false, true); ASSERT_EQ(1u, lookup.GetConstraintsCount()); ASSERT_EQ(DICOM_TAG_MODALITY, lookup.GetConstraint(0).GetTag()); - ASSERT_EQ(ResourceType_Series, lookup.GetConstraint(0).GetLevel()); - ASSERT_EQ(ConstraintType_List, lookup.GetConstraint(0).GetConstraintType()); + ASSERT_EQ(ConstraintType_List, lookup.GetConstraint(0).GetType()); const std::set& values = lookup.GetConstraint(0).GetValues(); ASSERT_EQ(2u, values.size()); @@ -256,12 +240,11 @@ { DatabaseLookup lookup; - lookup.AddDicomConstraint(DICOM_TAG_STUDY_DESCRIPTION, "CT\\MR", false); + lookup.AddDicomConstraint(DICOM_TAG_STUDY_DESCRIPTION, "CT\\MR", false, true); ASSERT_EQ(1u, lookup.GetConstraintsCount()); ASSERT_EQ(DICOM_TAG_STUDY_DESCRIPTION, lookup.GetConstraint(0).GetTag()); - ASSERT_EQ(ResourceType_Study, lookup.GetConstraint(0).GetLevel()); - ASSERT_EQ(ConstraintType_List, lookup.GetConstraint(0).GetConstraintType()); + ASSERT_EQ(ConstraintType_List, lookup.GetConstraint(0).GetType()); const std::set& values = lookup.GetConstraint(0).GetValues(); ASSERT_EQ(2u, values.size()); @@ -272,26 +255,25 @@ { DatabaseLookup lookup; - lookup.AddDicomConstraint(DICOM_TAG_STUDY_DESCRIPTION, "HE*O", false); + lookup.AddDicomConstraint(DICOM_TAG_STUDY_DESCRIPTION, "HE*O", false, true); ASSERT_EQ(1u, lookup.GetConstraintsCount()); - ASSERT_EQ(ConstraintType_Wildcard, lookup.GetConstraint(0).GetConstraintType()); + ASSERT_EQ(ConstraintType_Wildcard, lookup.GetConstraint(0).GetType()); } { DatabaseLookup lookup; - lookup.AddDicomConstraint(DICOM_TAG_STUDY_DESCRIPTION, "HE?O", false); + lookup.AddDicomConstraint(DICOM_TAG_STUDY_DESCRIPTION, "HE?O", false, true); ASSERT_EQ(1u, lookup.GetConstraintsCount()); - ASSERT_EQ(ConstraintType_Wildcard, lookup.GetConstraint(0).GetConstraintType()); + ASSERT_EQ(ConstraintType_Wildcard, lookup.GetConstraint(0).GetType()); } { DatabaseLookup lookup; - lookup.AddDicomConstraint(DICOM_TAG_RELATED_FRAME_OF_REFERENCE_UID, "TEST", false); - - ASSERT_TRUE(lookup.GetConstraint(0).HasTagInfo()); - ASSERT_EQ(DicomTagType_Generic, lookup.GetConstraint(0).GetTagType()); - ASSERT_EQ(ResourceType_Instance, lookup.GetConstraint(0).GetLevel()); + lookup.AddDicomConstraint(DICOM_TAG_RELATED_FRAME_OF_REFERENCE_UID, "TEST", false, true); + lookup.AddDicomConstraint(DICOM_TAG_PATIENT_NAME, "TEST2", false, false); + ASSERT_TRUE(lookup.GetConstraint(0).IsMandatory()); + ASSERT_FALSE(lookup.GetConstraint(1).IsMandatory()); } }