Mercurial > hg > orthanc
changeset 1750:55d52567bebb db-changes
LookupResource implemented
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 27 Oct 2015 12:45:50 +0100 |
parents | 99f4a05f39fa |
children | fb569ee09a69 |
files | Core/FileStorage/StorageAccessor.cpp Core/FileStorage/StorageAccessor.h OrthancServer/DatabaseWrapper.h OrthancServer/DatabaseWrapperBase.cpp OrthancServer/DatabaseWrapperBase.h OrthancServer/IDatabaseWrapper.h OrthancServer/Search/IFindConstraint.h OrthancServer/Search/LookupIdentifierQuery.cpp OrthancServer/Search/LookupIdentifierQuery.h OrthancServer/Search/LookupResource.cpp OrthancServer/Search/LookupResource.h OrthancServer/Search/SetOfResources.cpp OrthancServer/Search/SetOfResources.h OrthancServer/ServerIndex.cpp OrthancServer/ServerIndex.h Plugins/Engine/OrthancPluginDatabase.cpp Plugins/Engine/OrthancPluginDatabase.h |
diffstat | 17 files changed, 365 insertions(+), 23 deletions(-) [+] |
line wrap: on
line diff
--- 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) {
--- 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 <string> #include <boost/noncopyable.hpp> #include <stdint.h> +#include <json/value.h> 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());
--- 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<int64_t>& target, + ResourceType resourceType) + { + base_.GetAllInternalIds(target, resourceType); + } + virtual void GetAllPublicIds(std::list<std::string>& target, ResourceType resourceType) {
--- 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<uint64_t>(s.ColumnInt64(0)); } + void DatabaseWrapperBase::GetAllInternalIds(std::list<int64_t>& 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<std::string>& target, ResourceType resourceType) {
--- 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<int64_t>& target, + ResourceType resourceType); + void GetAllPublicIds(std::list<std::string>& target, ResourceType resourceType);
--- 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<MetadataType, std::string>& target, int64_t id) = 0; + virtual void GetAllInternalIds(std::list<int64_t>& target, + ResourceType resourceType) = 0; + virtual void GetAllPublicIds(std::list<std::string>& target, ResourceType resourceType) = 0;
--- 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;
--- 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<int64_t> a; @@ -217,9 +225,8 @@ a.splice(a.end(), b); } - resources.Intersect(a); + result.Intersect(a); } + } - resources.Flatten(result); - } }
--- 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 <vector> #include <boost/noncopyable.hpp> @@ -161,6 +163,9 @@ void Apply(std::list<std::string>& result, IDatabaseWrapper& database); + void Apply(SetOfResources& result, + IDatabaseWrapper& database); + static void LoadIdentifiers(const DicomTag*& tags, size_t& size, ResourceType level);
--- 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<int64_t> children; - database.GetChildrenInternalId(children, parent); + // Secondly, filter using the main DICOM tags + if (!identifiersConstraints_.empty() || + !mainTagsConstraints_.empty()) + { + std::list<int64_t> source; + candidates.Flatten(source); + candidates.Clear(); - if (children.empty()) + std::list<int64_t> filtered; + for (std::list<int64_t>::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<int64_t> source; + candidates.Flatten(source); + candidates.Clear(); + + StorageAccessor accessor(storageArea); + + std::list<int64_t> filtered; + for (std::list<int64_t>::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<int64_t>& 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<std::string>& result, + IDatabaseWrapper& database, + IStorageArea& storageArea) const + { + std::list<int64_t> tmp; + Apply(tmp, database, storageArea); + + result.clear(); + + for (std::list<int64_t>::const_iterator + it = tmp.begin(); it != tmp.end(); ++it) + { + result.push_back(database.GetPublicId(*it)); + } + } }
--- 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<DicomTag> identifiers_; std::set<DicomTag> mainTags_; Constraints identifiersConstraints_; @@ -58,6 +59,9 @@ ~Level(); bool Add(std::auto_ptr<IFindConstraint>& constraint); + + void Apply(SetOfResources& candidates, + IDatabaseWrapper& database) const; }; typedef std::map<ResourceType, Level*> Levels; @@ -70,7 +74,13 @@ bool AddInternal(ResourceType level, std::auto_ptr<IFindConstraint>& 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<int64_t>& result, + IDatabaseWrapper& database, + IStorageArea& storageArea) const; + + void Apply(std::list<std::string>& result, + IDatabaseWrapper& database, + IStorageArea& storageArea) const; }; }
--- 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<int64_t>& 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); + } + } + } }
--- 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<int64_t>& result); + void Flatten(std::list<std::string>& result); + + void Clear() + { + resources_.reset(NULL); + } }; }
--- 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<std::string>& result, + ::Orthanc::LookupResource& lookup, + IStorageArea& area) + { + boost::mutex::scoped_lock lock(mutex_); + lookup.Apply(result, db_, area); + } }
--- 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<std::string>& result, + ::Orthanc::LookupResource& lookup, + IStorageArea& area); }; }
--- 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<int64_t>& target, + ResourceType resourceType) + { + // TODO + throw OrthancException(ErrorCode_NotImplemented); + } + + void OrthancPluginDatabase::GetAllPublicIds(std::list<std::string>& target, ResourceType resourceType) {
--- 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<MetadataType, std::string>& target, int64_t id); + virtual void GetAllInternalIds(std::list<int64_t>& target, + ResourceType resourceType); + virtual void GetAllPublicIds(std::list<std::string>& target, ResourceType resourceType);