# HG changeset patch # User Sebastien Jodogne # Date 1445946350 -3600 # Node ID 55d52567bebbe63e6faf7a9d4ca35eb58ba6360f # Parent 99f4a05f39faa0f292fdf1c3b6b32b49f001ec5e LookupResource implemented diff -r 99f4a05f39fa -r 55d52567bebb Core/FileStorage/StorageAccessor.cpp --- a/Core/FileStorage/StorageAccessor.cpp Tue Oct 27 10:54:51 2015 +0100 +++ b/Core/FileStorage/StorageAccessor.cpp Tue Oct 27 12:45:50 2015 +0100 @@ -126,6 +126,20 @@ } + void StorageAccessor::Read(Json::Value& content, + const FileInfo& info) + { + std::string s; + Read(s, info); + + Json::Reader reader; + if (!reader.parse(s, content)) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + } + + void StorageAccessor::SetupSender(BufferHttpSender& sender, const FileInfo& info) { diff -r 99f4a05f39fa -r 55d52567bebb Core/FileStorage/StorageAccessor.h --- a/Core/FileStorage/StorageAccessor.h Tue Oct 27 10:54:51 2015 +0100 +++ b/Core/FileStorage/StorageAccessor.h Tue Oct 27 12:45:50 2015 +0100 @@ -41,6 +41,7 @@ #include #include #include +#include namespace Orthanc { @@ -75,6 +76,9 @@ void Read(std::string& content, const FileInfo& info); + void Read(Json::Value& content, + const FileInfo& info); + void Remove(const FileInfo& info) { area_.Remove(info.GetUuid(), info.GetContentType()); diff -r 99f4a05f39fa -r 55d52567bebb OrthancServer/DatabaseWrapper.h --- a/OrthancServer/DatabaseWrapper.h Tue Oct 27 10:54:51 2015 +0100 +++ b/OrthancServer/DatabaseWrapper.h Tue Oct 27 12:45:50 2015 +0100 @@ -249,6 +249,12 @@ return base_.GetResourceCount(resourceType); } + virtual void GetAllInternalIds(std::list& target, + ResourceType resourceType) + { + base_.GetAllInternalIds(target, resourceType); + } + virtual void GetAllPublicIds(std::list& target, ResourceType resourceType) { diff -r 99f4a05f39fa -r 55d52567bebb OrthancServer/DatabaseWrapperBase.cpp --- a/OrthancServer/DatabaseWrapperBase.cpp Tue Oct 27 10:54:51 2015 +0100 +++ b/OrthancServer/DatabaseWrapperBase.cpp Tue Oct 27 12:45:50 2015 +0100 @@ -533,6 +533,20 @@ return static_cast(s.ColumnInt64(0)); } + void DatabaseWrapperBase::GetAllInternalIds(std::list& target, + ResourceType resourceType) + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT internalId FROM Resources WHERE resourceType=?"); + s.BindInt(0, resourceType); + + target.clear(); + while (s.Step()) + { + target.push_back(s.ColumnInt64(0)); + } + } + + void DatabaseWrapperBase::GetAllPublicIds(std::list& target, ResourceType resourceType) { diff -r 99f4a05f39fa -r 55d52567bebb OrthancServer/DatabaseWrapperBase.h --- a/OrthancServer/DatabaseWrapperBase.h Tue Oct 27 10:54:51 2015 +0100 +++ b/OrthancServer/DatabaseWrapperBase.h Tue Oct 27 12:45:50 2015 +0100 @@ -168,6 +168,9 @@ uint64_t GetTotalUncompressedSize(); + void GetAllInternalIds(std::list& target, + ResourceType resourceType); + void GetAllPublicIds(std::list& target, ResourceType resourceType); diff -r 99f4a05f39fa -r 55d52567bebb OrthancServer/IDatabaseWrapper.h --- a/OrthancServer/IDatabaseWrapper.h Tue Oct 27 10:54:51 2015 +0100 +++ b/OrthancServer/IDatabaseWrapper.h Tue Oct 27 12:45:50 2015 +0100 @@ -83,6 +83,9 @@ virtual void GetAllMetadata(std::map& target, int64_t id) = 0; + virtual void GetAllInternalIds(std::list& target, + ResourceType resourceType) = 0; + virtual void GetAllPublicIds(std::list& target, ResourceType resourceType) = 0; diff -r 99f4a05f39fa -r 55d52567bebb OrthancServer/Search/IFindConstraint.h --- a/OrthancServer/Search/IFindConstraint.h Tue Oct 27 10:54:51 2015 +0100 +++ b/OrthancServer/Search/IFindConstraint.h Tue Oct 27 12:45:50 2015 +0100 @@ -55,6 +55,8 @@ return tag_; } + virtual IFindConstraint* Clone() const = 0; + virtual void Setup(LookupIdentifierQuery& lookup) const = 0; virtual bool Match(const std::string& value) const = 0; diff -r 99f4a05f39fa -r 55d52567bebb OrthancServer/Search/LookupIdentifierQuery.cpp --- a/OrthancServer/Search/LookupIdentifierQuery.cpp Tue Oct 27 10:54:51 2015 +0100 +++ b/OrthancServer/Search/LookupIdentifierQuery.cpp Tue Oct 27 12:45:50 2015 +0100 @@ -203,7 +203,15 @@ IDatabaseWrapper& database) { SetOfResources resources(database, level_); - + Apply(resources, database); + + resources.Flatten(result); + } + + + void LookupIdentifierQuery::Apply(SetOfResources& result, + IDatabaseWrapper& database) + { for (size_t i = 0; i < GetSize(); i++) { std::list a; @@ -217,9 +225,8 @@ a.splice(a.end(), b); } - resources.Intersect(a); + result.Intersect(a); } + } - resources.Flatten(result); - } } diff -r 99f4a05f39fa -r 55d52567bebb OrthancServer/Search/LookupIdentifierQuery.h --- a/OrthancServer/Search/LookupIdentifierQuery.h Tue Oct 27 10:54:51 2015 +0100 +++ b/OrthancServer/Search/LookupIdentifierQuery.h Tue Oct 27 12:45:50 2015 +0100 @@ -35,6 +35,8 @@ #include "../ServerEnumerations.h" #include "../IDatabaseWrapper.h" +#include "SetOfResources.h" + #include #include @@ -161,6 +163,9 @@ void Apply(std::list& result, IDatabaseWrapper& database); + void Apply(SetOfResources& result, + IDatabaseWrapper& database); + static void LoadIdentifiers(const DicomTag*& tags, size_t& size, ResourceType level); diff -r 99f4a05f39fa -r 55d52567bebb OrthancServer/Search/LookupResource.cpp --- a/OrthancServer/Search/LookupResource.cpp Tue Oct 27 10:54:51 2015 +0100 +++ b/OrthancServer/Search/LookupResource.cpp Tue Oct 27 12:45:50 2015 +0100 @@ -34,10 +34,13 @@ #include "LookupResource.h" #include "../../Core/OrthancException.h" +#include "../../Core/FileStorage/StorageAccessor.h" +#include "../ServerToolbox.h" + namespace Orthanc { - LookupResource::Level::Level(ResourceType level) + LookupResource::Level::Level(ResourceType level) : level_(level) { const DicomTag* tags = NULL; size_t size; @@ -79,12 +82,30 @@ { if (identifiers_.find(constraint->GetTag()) != identifiers_.end()) { - identifiersConstraints_.push_back(constraint.release()); + if (level_ == ResourceType_Patient) + { + // The filters on the patient level must be cloned to the study level + identifiersConstraints_.push_back(constraint->Clone()); + } + else + { + identifiersConstraints_.push_back(constraint.release()); + } + return true; } else if (mainTags_.find(constraint->GetTag()) != mainTags_.end()) { - mainTagsConstraints_.push_back(constraint.release()); + if (level_ == ResourceType_Patient) + { + // The filters on the patient level must be cloned to the study level + mainTagsConstraints_.push_back(constraint->Clone()); + } + else + { + mainTagsConstraints_.push_back(constraint.release()); + } + return true; } else @@ -167,28 +188,220 @@ } - static int64_t ChooseOneInstance(IDatabaseWrapper& database, - int64_t parent, - ResourceType type) + static bool Match(const DicomMap& tags, + const IFindConstraint& constraint) { - for (;;) + const DicomValue* value = tags.TestAndGetValue(constraint.GetTag()); + + if (value == NULL || + value->IsNull() || + value->IsBinary()) + { + return false; + } + else { - if (type == ResourceType_Instance) - { - return parent; - } + return constraint.Match(value->GetContent()); + } + } + + + void LookupResource::Level::Apply(SetOfResources& candidates, + IDatabaseWrapper& database) const + { + // First, use the indexed identifiers + LookupIdentifierQuery query(level_); + + for (Constraints::const_iterator it = identifiersConstraints_.begin(); + it != identifiersConstraints_.end(); ++it) + { + (*it)->Setup(query); + } + + query.Apply(candidates, database); - std::list children; - database.GetChildrenInternalId(children, parent); + // Secondly, filter using the main DICOM tags + if (!identifiersConstraints_.empty() || + !mainTagsConstraints_.empty()) + { + std::list source; + candidates.Flatten(source); + candidates.Clear(); - if (children.empty()) + std::list filtered; + for (std::list::const_iterator candidate = source.begin(); + candidate != source.end(); ++candidate) { - throw OrthancException(ErrorCode_InternalError); - } + DicomMap tags; + database.GetMainDicomTags(tags, *candidate); + + bool match = true; + + // Re-apply the identifier constraints, as their "Setup" + // method is less restrictive than their "Match" method + for (Constraints::const_iterator it = identifiersConstraints_.begin(); + match && it != identifiersConstraints_.end(); ++it) + { + if (!Match(tags, **it)) + { + match = false; + } + } - parent = children.front(); - type = GetChildResourceType(type); + for (Constraints::const_iterator it = mainTagsConstraints_.begin(); + match && it != mainTagsConstraints_.end(); ++it) + { + if (!Match(tags, **it)) + { + match = false; + } + } + + if (match) + { + filtered.push_back(*candidate); + } + } + + candidates.Intersect(filtered); } } + + + void LookupResource::ApplyUnoptimizedConstraints(SetOfResources& candidates, + IDatabaseWrapper& database, + IStorageArea& storageArea) const + { + if (unoptimizedConstraints_.empty()) + { + // Nothing to do + return; + } + + std::list source; + candidates.Flatten(source); + candidates.Clear(); + + StorageAccessor accessor(storageArea); + + std::list filtered; + for (std::list::const_iterator candidate = source.begin(); + candidate != source.end(); ++candidate) + { + if (maxResults_ != 0 && + filtered.size() >= maxResults_) + { + // We have enough results + break; + } + + int64_t instance; + FileInfo attachment; + if (!Toolbox::FindOneChildInstance(instance, database, *candidate, level_) || + !database.LookupAttachment(attachment, instance, FileContentType_DicomAsJson)) + { + continue; + } + + Json::Value content; + accessor.Read(content, attachment); + + bool match = true; + + for (Constraints::const_iterator it = unoptimizedConstraints_.begin(); + match && it != unoptimizedConstraints_.end(); ++it) + { + std::string tag = (*it)->GetTag().Format(); + if (content.isMember(tag) && + content[tag]["Type"] == "String") + { + std::string value = content[tag]["Value"].asString(); + if (!(*it)->Match(value)) + { + match = false; + } + } + else + { + match = false; + } + } + + if (match) + { + filtered.push_back(*candidate); + } + } + + candidates.Intersect(filtered); + } + + + void LookupResource::ApplyLevel(SetOfResources& candidates, + ResourceType level, + IDatabaseWrapper& database) const + { + Levels::const_iterator it = levels_.find(level); + if (it != levels_.end()) + { + it->second->Apply(candidates, database); + } + } + + + void LookupResource::Apply(std::list& result, + IDatabaseWrapper& database, + IStorageArea& storageArea) const + { + SetOfResources candidates(database, level_); + + switch (level_) + { + case ResourceType_Patient: + ApplyLevel(candidates, ResourceType_Patient, database); + break; + + case ResourceType_Study: + ApplyLevel(candidates, ResourceType_Study, database); + break; + + case ResourceType_Series: + ApplyLevel(candidates, ResourceType_Study, database); + candidates.GoDown(); + ApplyLevel(candidates, ResourceType_Series, database); + break; + + case ResourceType_Instance: + ApplyLevel(candidates, ResourceType_Study, database); + candidates.GoDown(); + ApplyLevel(candidates, ResourceType_Series, database); + candidates.GoDown(); + ApplyLevel(candidates, ResourceType_Instance, database); + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + + ApplyUnoptimizedConstraints(candidates, database, storageArea); + candidates.Flatten(result); + } + + + void LookupResource::Apply(std::list& result, + IDatabaseWrapper& database, + IStorageArea& storageArea) const + { + std::list tmp; + Apply(tmp, database, storageArea); + + result.clear(); + + for (std::list::const_iterator + it = tmp.begin(); it != tmp.end(); ++it) + { + result.push_back(database.GetPublicId(*it)); + } + } } diff -r 99f4a05f39fa -r 55d52567bebb OrthancServer/Search/LookupResource.h --- a/OrthancServer/Search/LookupResource.h Tue Oct 27 10:54:51 2015 +0100 +++ b/OrthancServer/Search/LookupResource.h Tue Oct 27 12:45:50 2015 +0100 @@ -47,6 +47,7 @@ class Level { private: + ResourceType level_; std::set identifiers_; std::set mainTags_; Constraints identifiersConstraints_; @@ -58,6 +59,9 @@ ~Level(); bool Add(std::auto_ptr& constraint); + + void Apply(SetOfResources& candidates, + IDatabaseWrapper& database) const; }; typedef std::map Levels; @@ -70,7 +74,13 @@ bool AddInternal(ResourceType level, std::auto_ptr& constraint); - void ApplyUnoptimizedConstraints(SetOfResources& result); + void ApplyLevel(SetOfResources& candidates, + ResourceType level, + IDatabaseWrapper& database) const; + + void ApplyUnoptimizedConstraints(SetOfResources& candidates, + IDatabaseWrapper& database, + IStorageArea& storageArea) const; public: LookupResource(ResourceType level); @@ -88,5 +98,13 @@ { return maxResults_; } + + void Apply(std::list& result, + IDatabaseWrapper& database, + IStorageArea& storageArea) const; + + void Apply(std::list& result, + IDatabaseWrapper& database, + IStorageArea& storageArea) const; }; } diff -r 99f4a05f39fa -r 55d52567bebb OrthancServer/Search/SetOfResources.cpp --- a/OrthancServer/Search/SetOfResources.cpp Tue Oct 27 10:54:51 2015 +0100 +++ b/OrthancServer/Search/SetOfResources.cpp Tue Oct 27 12:45:50 2015 +0100 @@ -130,4 +130,24 @@ } } } + + + void SetOfResources::Flatten(std::list& result) + { + result.clear(); + + if (resources_.get() == NULL) + { + // All the resources of this level are part of the filter + database_.GetAllInternalIds(result, level_); + } + else + { + for (Resources::const_iterator it = resources_->begin(); + it != resources_->end(); ++it) + { + result.push_back(*it); + } + } + } } diff -r 99f4a05f39fa -r 55d52567bebb OrthancServer/Search/SetOfResources.h --- a/OrthancServer/Search/SetOfResources.h Tue Oct 27 10:54:51 2015 +0100 +++ b/OrthancServer/Search/SetOfResources.h Tue Oct 27 12:45:50 2015 +0100 @@ -66,6 +66,13 @@ void GoDown(); + void Flatten(std::list& result); + void Flatten(std::list& result); + + void Clear() + { + resources_.reset(NULL); + } }; } diff -r 99f4a05f39fa -r 55d52567bebb OrthancServer/ServerIndex.cpp --- a/OrthancServer/ServerIndex.cpp Tue Oct 27 10:54:51 2015 +0100 +++ b/OrthancServer/ServerIndex.cpp Tue Oct 27 12:45:50 2015 +0100 @@ -46,6 +46,7 @@ #include "../Core/Uuid.h" #include "../Core/DicomFormat/DicomArray.h" #include "Search/LookupIdentifierQuery.h" +#include "Search/LookupResource.h" #include "FromDcmtkBridge.h" #include "ServerContext.h" @@ -2112,4 +2113,13 @@ boost::mutex::scoped_lock lock(mutex_); return db_.GetDatabaseVersion(); } + + + void ServerIndex::Apply(std::list& result, + ::Orthanc::LookupResource& lookup, + IStorageArea& area) + { + boost::mutex::scoped_lock lock(mutex_); + lookup.Apply(result, db_, area); + } } diff -r 99f4a05f39fa -r 55d52567bebb OrthancServer/ServerIndex.h --- a/OrthancServer/ServerIndex.h Tue Oct 27 10:54:51 2015 +0100 +++ b/OrthancServer/ServerIndex.h Tue Oct 27 12:45:50 2015 +0100 @@ -45,6 +45,7 @@ namespace Orthanc { + class LookupResource; class ServerContext; class ServerIndex : public boost::noncopyable @@ -261,5 +262,9 @@ const std::string& publicId); unsigned int GetDatabaseVersion(); + + void Apply(std::list& result, + ::Orthanc::LookupResource& lookup, + IStorageArea& area); }; } diff -r 99f4a05f39fa -r 55d52567bebb Plugins/Engine/OrthancPluginDatabase.cpp --- a/Plugins/Engine/OrthancPluginDatabase.cpp Tue Oct 27 10:54:51 2015 +0100 +++ b/Plugins/Engine/OrthancPluginDatabase.cpp Tue Oct 27 12:45:50 2015 +0100 @@ -274,6 +274,14 @@ } + void OrthancPluginDatabase::GetAllInternalIds(std::list& target, + ResourceType resourceType) + { + // TODO + throw OrthancException(ErrorCode_NotImplemented); + } + + void OrthancPluginDatabase::GetAllPublicIds(std::list& target, ResourceType resourceType) { diff -r 99f4a05f39fa -r 55d52567bebb Plugins/Engine/OrthancPluginDatabase.h --- a/Plugins/Engine/OrthancPluginDatabase.h Tue Oct 27 10:54:51 2015 +0100 +++ b/Plugins/Engine/OrthancPluginDatabase.h Tue Oct 27 12:45:50 2015 +0100 @@ -140,6 +140,9 @@ virtual void GetAllMetadata(std::map& target, int64_t id); + virtual void GetAllInternalIds(std::list& target, + ResourceType resourceType); + virtual void GetAllPublicIds(std::list& target, ResourceType resourceType);