# HG changeset patch # User Sebastien Jodogne # Date 1431512255 -7200 # Node ID 3dd494f201a146f500eff6f81c3d6488da18cf39 # Parent d7da97e21161c95931a8e929f27a91797189982e ResourceFinder diff -r d7da97e21161 -r 3dd494f201a1 OrthancServer/OrthancFindRequestHandler.cpp --- a/OrthancServer/OrthancFindRequestHandler.cpp Tue May 12 18:27:14 2015 +0200 +++ b/OrthancServer/OrthancFindRequestHandler.cpp Wed May 13 12:17:35 2015 +0200 @@ -432,12 +432,7 @@ } else { - Json::Value tmp; - index_.GetAllUuids(tmp, level_); - for (Json::Value::ArrayIndex i = 0; i < tmp.size(); i++) - { - resources.push_back(tmp[i].asString()); - } + index_.GetAllUuids(resources, level_); } } diff -r d7da97e21161 -r 3dd494f201a1 OrthancServer/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Tue May 12 18:27:14 2015 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Wed May 13 12:17:35 2015 +0200 @@ -35,6 +35,7 @@ #include "../ServerToolbox.h" #include "../FromDcmtkBridge.h" +#include "../ResourceFinder.h" #include @@ -42,32 +43,44 @@ { // List all the patients, studies, series or instances ---------------------- + static void AnswerListOfResources(RestApiOutput& output, + ServerIndex& index, + const std::list& resources, + ResourceType level, + bool expand) + { + Json::Value answer = Json::arrayValue; + + for (std::list::const_iterator + resource = resources.begin(); resource != resources.end(); resource++) + { + if (expand) + { + Json::Value item; + if (index.LookupResource(item, *resource, level)) + { + answer.append(item); + } + } + else + { + answer.append(*resource); + } + } + + output.AnswerJson(answer); + } + + template static void ListResources(RestApiGetCall& call) { ServerIndex& index = OrthancRestApi::GetIndex(call); - Json::Value result; + std::list result; index.GetAllUuids(result, resourceType); - if (call.HasArgument("expand")) - { - Json::Value expanded = Json::arrayValue; - for (Json::Value::ArrayIndex i = 0; i < result.size(); i++) - { - Json::Value item; - if (index.LookupResource(item, result[i].asString(), resourceType)) - { - expanded.append(item); - } - } - - call.GetOutput().AnswerJson(expanded); - } - else - { - call.GetOutput().AnswerJson(result); - } + AnswerListOfResources(call.GetOutput(), index, result, resourceType, call.HasArgument("expand")); } template @@ -835,6 +848,56 @@ } + static void Find(RestApiPostCall& call) + { + ServerIndex& index = OrthancRestApi::GetIndex(call); + + Json::Value request; + if (call.ParseJsonRequest(request) && + request.type() == Json::objectValue && + request.isMember("Level") && + request.isMember("Query") && + request["Level"].type() == Json::stringValue && + request["Query"].type() == Json::objectValue) + { + std::string level = request["Level"].asString(); + + ResourceFinder finder(index); + finder.SetLevel(StringToResourceType(level.c_str())); + + if (request.isMember("CaseSensitive")) + { + finder.SetCaseSensitive(request["CaseSensitive"].asBool()); + } + + bool expand = false; + if (request.isMember("Expand")) + { + expand = request["Expand"].asBool(); + } + + Json::Value::Members members = request["Query"].getMemberNames(); + for (size_t i = 0; i < members.size(); i++) + { + if (request["Query"][members[i]].type() != Json::stringValue) + { + throw OrthancException(ErrorCode_BadRequest); + } + + finder.AddTag(members[i], request["Query"][members[i]].asString()); + } + + std::list resources; + finder.Apply(resources); + AnswerListOfResources(call.GetOutput(), index, resources, finder.GetLevel(), expand); + } + else + { + throw OrthancException(ErrorCode_BadRequest); + } + } + + template static void GetChildResources(RestApiGetCall& call) @@ -1042,6 +1105,7 @@ Register("/{resourceType}/{id}/attachments/{name}", UploadAttachment); Register("/tools/lookup", Lookup); + Register("/tools/find", Find); Register("/patients/{id}/studies", GetChildResources); Register("/patients/{id}/series", GetChildResources); diff -r d7da97e21161 -r 3dd494f201a1 OrthancServer/ResourceFinder.cpp --- a/OrthancServer/ResourceFinder.cpp Tue May 12 18:27:14 2015 +0200 +++ b/OrthancServer/ResourceFinder.cpp Wed May 13 12:17:35 2015 +0200 @@ -55,243 +55,236 @@ } - namespace + class ResourceFinder::CandidateResources { - class CandidateResources - { - private: - typedef std::map Query; + private: + typedef std::map Query; - ServerIndex& index_; - ResourceType level_; - bool isFilterApplied_; - std::set filtered_; + ServerIndex& index_; + ResourceType level_; + bool isFilterApplied_; + std::set filtered_; - static void ListToSet(std::set& target, - const std::list& source) + static void ListToSet(std::set& target, + const std::list& source) + { + for (std::list::const_iterator + it = source.begin(); it != source.end(); ++it) { - for (std::list::const_iterator - it = source.begin(); it != source.end(); ++it) - { - target.insert(*it); - } + target.insert(*it); } + } - void RestrictIdentifier(const DicomTag& tag, - const std::string& value) - { - assert((level_ == ResourceType_Patient && tag == DICOM_TAG_PATIENT_ID) || - (level_ == ResourceType_Study && tag == DICOM_TAG_STUDY_INSTANCE_UID) || - (level_ == ResourceType_Study && tag == DICOM_TAG_ACCESSION_NUMBER) || - (level_ == ResourceType_Series && tag == DICOM_TAG_SERIES_INSTANCE_UID) || - (level_ == ResourceType_Instance && tag == DICOM_TAG_SOP_INSTANCE_UID)); + void RestrictIdentifier(const DicomTag& tag, + const std::string& value) + { + assert((level_ == ResourceType_Patient && tag == DICOM_TAG_PATIENT_ID) || + (level_ == ResourceType_Study && tag == DICOM_TAG_STUDY_INSTANCE_UID) || + (level_ == ResourceType_Study && tag == DICOM_TAG_ACCESSION_NUMBER) || + (level_ == ResourceType_Series && tag == DICOM_TAG_SERIES_INSTANCE_UID) || + (level_ == ResourceType_Instance && tag == DICOM_TAG_SOP_INSTANCE_UID)); - LOG(INFO) << "Lookup for identifier tag " - << FromDcmtkBridge::GetName(tag) << " (value: " << value << ")"; + LOG(INFO) << "Lookup for identifier tag " + << FromDcmtkBridge::GetName(tag) << " (value: " << value << ")"; - std::list resources; - index_.LookupIdentifier(resources, tag, value, level_); + std::list resources; + index_.LookupIdentifier(resources, tag, value, level_); - if (isFilterApplied_) - { - std::set s; - ListToSet(s, resources); - - std::set tmp = filtered_; - filtered_.clear(); + if (isFilterApplied_) + { + std::set s; + ListToSet(s, resources); - for (std::set::const_iterator - it = tmp.begin(); it != tmp.end(); ++it) + std::set tmp = filtered_; + filtered_.clear(); + + for (std::set::const_iterator + it = tmp.begin(); it != tmp.end(); ++it) + { + if (s.find(*it) != s.end()) { - if (s.find(*it) != s.end()) - { - filtered_.insert(*it); - } + filtered_.insert(*it); } } - else - { - assert(filtered_.empty()); - isFilterApplied_ = true; - ListToSet(filtered_, resources); - } + } + else + { + assert(filtered_.empty()); + isFilterApplied_ = true; + ListToSet(filtered_, resources); } + } + + + void RestrictIdentifier(Query& query, + const DicomTag& tag) + { + Query::iterator it = query.find(tag); + if (it != query.end()) + { + RestrictIdentifier(it->first, it->second); + query.erase(it); + } + } - void RestrictIdentifier(const Query& query, - const DicomTag& tag) + public: + CandidateResources(ServerIndex& index) : + index_(index), + level_(ResourceType_Patient), + isFilterApplied_(false) + { + } + + ResourceType GetLevel() const + { + return level_; + } + + void GoDown() + { + assert(level_ != ResourceType_Instance); + + if (isFilterApplied_) { - Query::const_iterator it = query.find(tag); - if (it != query.end()) + std::set tmp = filtered_; + + filtered_.clear(); + + for (std::set::const_iterator + it = tmp.begin(); it != tmp.end(); ++it) { - RestrictIdentifier(it->first, it->second); + std::list children; + index_.GetChildren(children, *it); + ListToSet(filtered_, children); } } - - public: - CandidateResources(ServerIndex& index) : - index_(index), - level_(ResourceType_Patient), - isFilterApplied_(false) - { - } - - ResourceType GetLevel() const + switch (level_) { - return level_; - } + case ResourceType_Patient: + level_ = ResourceType_Study; + break; - void GoDown() - { - assert(level_ != ResourceType_Instance); + case ResourceType_Study: + level_ = ResourceType_Series; + break; - if (isFilterApplied_) - { - std::set tmp = filtered_; - - filtered_.clear(); + case ResourceType_Series: + level_ = ResourceType_Instance; + break; - for (std::set::const_iterator - it = tmp.begin(); it != tmp.end(); ++it) - { - std::list children; - index_.GetChildren(children, *it); - ListToSet(filtered_, children); - } - } + default: + throw OrthancException(ErrorCode_InternalError); + } + } + - switch (level_) + void Flatten(std::list& resources) const + { + resources.clear(); + + if (isFilterApplied_) + { + for (std::set::const_iterator + it = filtered_.begin(); it != filtered_.end(); ++it) { - case ResourceType_Patient: - level_ = ResourceType_Study; - break; - - case ResourceType_Study: - level_ = ResourceType_Series; - break; - - case ResourceType_Series: - level_ = ResourceType_Instance; - break; - - default: - throw OrthancException(ErrorCode_InternalError); + resources.push_back(*it); } } - - - void Flatten(std::list& resources) const + else { - resources.clear(); - - if (isFilterApplied_) - { - for (std::set::const_iterator - it = filtered_.begin(); it != filtered_.end(); ++it) - { - resources.push_back(*it); - } - } - else - { - Json::Value tmp; - index_.GetAllUuids(tmp, level_); - for (Json::Value::ArrayIndex i = 0; i < tmp.size(); i++) - { - resources.push_back(tmp[i].asString()); - } - } + index_.GetAllUuids(resources, level_); } + } - void RestrictIdentifier(const Query& query) + void RestrictIdentifier(Query& query) + { + switch (level_) { - switch (level_) + case ResourceType_Patient: { - case ResourceType_Patient: - { - RestrictIdentifier(query, DICOM_TAG_PATIENT_ID); - break; - } - - case ResourceType_Study: - { - RestrictIdentifier(query, DICOM_TAG_STUDY_INSTANCE_UID); - RestrictIdentifier(query, DICOM_TAG_ACCESSION_NUMBER); - break; - } + RestrictIdentifier(query, DICOM_TAG_PATIENT_ID); + break; + } - case ResourceType_Series: - { - RestrictIdentifier(query, DICOM_TAG_SERIES_INSTANCE_UID); - break; - } - - case ResourceType_Instance: - { - RestrictIdentifier(query, DICOM_TAG_SOP_INSTANCE_UID); - break; - } + case ResourceType_Study: + { + RestrictIdentifier(query, DICOM_TAG_STUDY_INSTANCE_UID); + RestrictIdentifier(query, DICOM_TAG_ACCESSION_NUMBER); + break; + } - default: - throw OrthancException(ErrorCode_InternalError); + case ResourceType_Series: + { + RestrictIdentifier(query, DICOM_TAG_SERIES_INSTANCE_UID); + break; } - } - - void RestrictMainDicomTags(const Query& query, - bool caseSensitive) - { - if (query.size() == 0) + case ResourceType_Instance: { - return; + RestrictIdentifier(query, DICOM_TAG_SOP_INSTANCE_UID); + break; } - std::list resources; - Flatten(resources); + default: + throw OrthancException(ErrorCode_InternalError); + } + } + - isFilterApplied_ = true; - filtered_.clear(); + void RestrictMainDicomTags(const Query& query, + bool caseSensitive) + { + if (query.size() == 0) + { + return; + } + + std::list resources; + Flatten(resources); - for (std::list::const_iterator - it = resources.begin(); it != resources.end(); it++) + isFilterApplied_ = true; + filtered_.clear(); + + for (std::list::const_iterator + it = resources.begin(); it != resources.end(); it++) + { + DicomMap mainTags; + if (index_.GetMainDicomTags(mainTags, *it, level_)) { - DicomMap mainTags; - if (index_.GetMainDicomTags(mainTags, *it, level_)) + for (Query::const_iterator tag = query.begin(); + tag != query.end(); ++tag) { - for (Query::const_iterator tag = query.begin(); - tag != query.end(); ++tag) + assert(DicomMap::IsMainDicomTag(tag->first, level_)); + if (tag->first != DICOM_TAG_PATIENT_ID && + tag->first != DICOM_TAG_STUDY_INSTANCE_UID && + tag->first != DICOM_TAG_ACCESSION_NUMBER && + tag->first != DICOM_TAG_SERIES_INSTANCE_UID && + tag->first != DICOM_TAG_SOP_INSTANCE_UID) { - assert(DicomMap::IsMainDicomTag(tag->first, level_)); - if (tag->first != DICOM_TAG_PATIENT_ID && - tag->first != DICOM_TAG_STUDY_INSTANCE_UID && - tag->first != DICOM_TAG_ACCESSION_NUMBER && - tag->first != DICOM_TAG_SERIES_INSTANCE_UID && - tag->first != DICOM_TAG_SOP_INSTANCE_UID) - { - LOG(INFO) << "Lookup for main DICOM tag " - << FromDcmtkBridge::GetName(tag->first) << " (value: " << tag->second << ")"; + LOG(INFO) << "Lookup for main DICOM tag " + << FromDcmtkBridge::GetName(tag->first) << " (value: " << tag->second << ")"; - const DicomValue* value = mainTags.TestAndGetValue(tag->first); - if (value != NULL && - Compare(value->AsString(), tag->second, caseSensitive)) - { - filtered_.insert(*it); - } + const DicomValue* value = mainTags.TestAndGetValue(tag->first); + if (value != NULL && + Compare(value->AsString(), tag->second, caseSensitive)) + { + filtered_.insert(*it); } - } - } + } + } } } - }; - } + } + }; - ResourceFinder::ResourceFinder(ServerContext& context) : - context_(context), + ResourceFinder::ResourceFinder(ServerIndex& index) : + index_(index), level_(ResourceType_Patient), caseSensitive_(true) { @@ -305,29 +298,75 @@ } - void ResourceFinder::GetTagsForLevel(Query& result, - const Query& source, - ResourceType level) + void ResourceFinder::ExtractTagsForLevel(Query& target, + Query& source, + ResourceType level) { typedef std::set Tags; Tags tags; DicomMap::GetMainDicomTags(tags, level); + target.clear(); + for (Tags::const_iterator tag = tags.begin(); tag != tags.end(); tag++) { - Query::const_iterator value = source.find(*tag); + Query::iterator value = source.find(*tag); if (value != source.end()) { - result.insert(*value); + target.insert(*value); + source.erase(value); } } } + void ResourceFinder::ApplyAtLevel(CandidateResources& candidates, + ResourceType level) + { + if (level != ResourceType_Patient) + { + candidates.GoDown(); + } + + candidates.RestrictIdentifier(query_); + + Query tmp; + ExtractTagsForLevel(tmp, query_, level); + candidates.RestrictMainDicomTags(tmp, caseSensitive_); + } + + void ResourceFinder::Apply(std::list& result) { - result.clear(); + CandidateResources candidates(index_); + + ApplyAtLevel(candidates, ResourceType_Patient); + if (level_ == ResourceType_Study || + level_ == ResourceType_Series || + level_ == ResourceType_Instance) + { + ApplyAtLevel(candidates, ResourceType_Study); + } + + if (level_ == ResourceType_Series || + level_ == ResourceType_Instance) + { + ApplyAtLevel(candidates, ResourceType_Series); + } + + if (level_ == ResourceType_Instance) + { + ApplyAtLevel(candidates, ResourceType_Instance); + } + + if (!query_.empty()) + { + LOG(ERROR) << "Invalid query: Searching against a tag that is not valid for the requested level"; + throw OrthancException(ErrorCode_BadRequest); + } + + candidates.Flatten(result); } } diff -r d7da97e21161 -r 3dd494f201a1 OrthancServer/ResourceFinder.h --- a/OrthancServer/ResourceFinder.h Tue May 12 18:27:14 2015 +0200 +++ b/OrthancServer/ResourceFinder.h Wed May 13 12:17:35 2015 +0200 @@ -32,7 +32,7 @@ #pragma once -#include "ServerContext.h" +#include "ServerIndex.h" #include @@ -43,17 +43,22 @@ private: typedef std::map Query; - ServerContext& context_; - ResourceType level_; - bool caseSensitive_; - Query query_; + class CandidateResources; + + ServerIndex& index_; + ResourceType level_; + bool caseSensitive_; + Query query_; - static void GetTagsForLevel(Query& result, - const Query& source, - ResourceType level); + static void ExtractTagsForLevel(Query& result, + Query& source, + ResourceType level); + + void ApplyAtLevel(CandidateResources& candidates, + ResourceType level); public: - ResourceFinder(ServerContext& context); + ResourceFinder(ServerIndex& index); bool IsCaseSensitive() const { @@ -62,7 +67,7 @@ void SetCaseSensitive(bool sensitive) { - caseSensitive_ = true; + caseSensitive_ = sensitive; } ResourceType GetLevel() const diff -r d7da97e21161 -r 3dd494f201a1 OrthancServer/ServerIndex.cpp --- a/OrthancServer/ServerIndex.cpp Tue May 12 18:27:14 2015 +0200 +++ b/OrthancServer/ServerIndex.cpp Wed May 13 12:17:35 2015 +0200 @@ -1075,7 +1075,7 @@ - void ServerIndex::GetAllUuids(Json::Value& target, + void ServerIndex::GetAllUuids(std::list& target, ResourceType resourceType) { std::list lst; @@ -1085,11 +1085,11 @@ db_.GetAllPublicIds(lst, resourceType); } - target = Json::arrayValue; + target.clear(); for (std::list::const_iterator it = lst.begin(); it != lst.end(); ++it) { - target.append(*it); + target.push_back(*it); } } diff -r d7da97e21161 -r 3dd494f201a1 OrthancServer/ServerIndex.h --- a/OrthancServer/ServerIndex.h Tue May 12 18:27:14 2015 +0200 +++ b/OrthancServer/ServerIndex.h Wed May 13 12:17:35 2015 +0200 @@ -158,7 +158,7 @@ const std::string& instanceUuid, FileContentType contentType); - void GetAllUuids(Json::Value& target, + void GetAllUuids(std::list& target, ResourceType resourceType); bool DeleteResource(Json::Value& target /* out */,