# HG changeset patch # User Sebastien Jodogne # Date 1446033558 -3600 # Node ID e268412adcf18afa67b3954c8df31094d8ed7510 # Parent 318c2e83c2bd82a69f3dbf1062996f3ca866c529 removal of old implementation of search diff -r 318c2e83c2bd -r e268412adcf1 CMakeLists.txt --- a/CMakeLists.txt Wed Oct 28 12:53:45 2015 +0100 +++ b/CMakeLists.txt Wed Oct 28 12:59:18 2015 +0100 @@ -150,7 +150,6 @@ OrthancServer/DatabaseWrapper.cpp OrthancServer/DatabaseWrapperBase.cpp OrthancServer/DicomDirWriter.cpp - OrthancServer/DicomFindQuery.cpp OrthancServer/DicomModification.cpp OrthancServer/DicomProtocol/DicomFindAnswers.cpp OrthancServer/DicomProtocol/DicomServer.cpp @@ -179,7 +178,6 @@ OrthancServer/OrthancRestApi/OrthancRestSystem.cpp OrthancServer/ParsedDicomFile.cpp OrthancServer/QueryRetrieveHandler.cpp - OrthancServer/ResourceFinder.cpp OrthancServer/Search/LookupIdentifierQuery.cpp OrthancServer/Search/LookupResource.cpp OrthancServer/Search/SetOfResources.cpp diff -r 318c2e83c2bd -r e268412adcf1 OrthancServer/DicomFindQuery.cpp --- a/OrthancServer/DicomFindQuery.cpp Wed Oct 28 12:53:45 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,395 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - - -#include "PrecompiledHeadersServer.h" -#include "DicomFindQuery.h" - -#include "FromDcmtkBridge.h" - -#include - - -namespace Orthanc -{ - class DicomFindQuery::ValueConstraint : public DicomFindQuery::IConstraint - { - private: - bool isCaseSensitive_; - std::string expected_; - - public: - ValueConstraint(const std::string& value, - bool caseSensitive) : - isCaseSensitive_(caseSensitive), - expected_(value) - { - } - - const std::string& GetValue() const - { - return expected_; - } - - virtual bool IsExactConstraint() const - { - return isCaseSensitive_; - } - - virtual bool Apply(const std::string& value) const - { - if (isCaseSensitive_) - { - return expected_ == value; - } - else - { - std::string v, c; - Toolbox::ToLowerCase(v, value); - Toolbox::ToLowerCase(c, expected_); - return v == c; - } - } - }; - - - class DicomFindQuery::ListConstraint : public DicomFindQuery::IConstraint - { - private: - std::set values_; - - public: - ListConstraint(const std::string& values) - { - std::vector items; - Toolbox::TokenizeString(items, values, '\\'); - - for (size_t i = 0; i < items.size(); i++) - { - std::string lower; - Toolbox::ToLowerCase(lower, items[i]); - values_.insert(lower); - } - } - - virtual bool Apply(const std::string& value) const - { - std::string tmp; - Toolbox::ToLowerCase(tmp, value); - return values_.find(tmp) != values_.end(); - } - }; - - - class DicomFindQuery::RangeConstraint : public DicomFindQuery::IConstraint - { - private: - std::string lower_; - std::string upper_; - - public: - RangeConstraint(const std::string& range) - { - size_t separator = range.find('-'); - Toolbox::ToLowerCase(lower_, range.substr(0, separator)); - Toolbox::ToLowerCase(upper_, range.substr(separator + 1)); - } - - virtual bool Apply(const std::string& value) const - { - std::string v; - Toolbox::ToLowerCase(v, value); - - if (lower_.size() == 0 && - upper_.size() == 0) - { - return false; - } - - if (lower_.size() == 0) - { - return v <= upper_; - } - - if (upper_.size() == 0) - { - return v >= lower_; - } - - return (v >= lower_ && v <= upper_); - } - }; - - - class DicomFindQuery::WildcardConstraint : public DicomFindQuery::IConstraint - { - private: - boost::regex pattern_; - - public: - WildcardConstraint(const std::string& wildcard, - bool caseSensitive) - { - std::string re = Toolbox::WildcardToRegularExpression(wildcard); - - if (caseSensitive) - { - pattern_ = boost::regex(re); - } - else - { - pattern_ = boost::regex(re, boost::regex::icase /* case insensitive search */); - } - } - - virtual bool Apply(const std::string& value) const - { - return boost::regex_match(value, pattern_); - } - }; - - - void DicomFindQuery::PrepareMainDicomTags(ResourceType level) - { - std::set tags; - DicomMap::GetMainDicomTags(tags, level); - - for (std::set::const_iterator - it = tags.begin(); it != tags.end(); ++it) - { - mainDicomTags_[*it] = level; - } - } - - - DicomFindQuery::DicomFindQuery() : - level_(ResourceType_Patient), - filterJson_(false) - { - PrepareMainDicomTags(ResourceType_Patient); - PrepareMainDicomTags(ResourceType_Study); - PrepareMainDicomTags(ResourceType_Series); - PrepareMainDicomTags(ResourceType_Instance); - } - - - DicomFindQuery::~DicomFindQuery() - { - for (Constraints::iterator it = constraints_.begin(); - it != constraints_.end(); ++it) - { - delete it->second; - } - } - - - - - void DicomFindQuery::AssignConstraint(const DicomTag& tag, - IConstraint* constraint) - { - Constraints::iterator it = constraints_.find(tag); - - if (it != constraints_.end()) - { - constraints_.erase(it); - } - - constraints_[tag] = constraint; - - MainDicomTags::const_iterator tmp = mainDicomTags_.find(tag); - if (tmp == mainDicomTags_.end()) - { - // The query depends upon a DICOM tag that is not a main tag - // from the point of view of Orthanc, we need to decode the - // JSON file on the disk. - filterJson_ = true; - } - else - { - filteredLevels_.insert(tmp->second); - } - } - - - void DicomFindQuery::SetConstraint(const DicomTag& tag, - const std::string& constraint, - bool caseSensitivePN) - { - ValueRepresentation vr = FromDcmtkBridge::GetValueRepresentation(tag); - - bool sensitive = true; - if (vr == ValueRepresentation_PatientName) - { - sensitive = caseSensitivePN; - } - - // http://www.itk.org/Wiki/DICOM_QueryRetrieve_Explained - // http://dicomiseasy.blogspot.be/2012/01/dicom-queryretrieve-part-i.html - - if ((vr == ValueRepresentation_Date || - vr == ValueRepresentation_DateTime || - vr == ValueRepresentation_Time) && - constraint.find('-') != std::string::npos) - { - /** - * Range matching is only defined for TM, DA and DT value - * representations. This code fixes issues 35 and 37. - * - * Reference: "Range matching is not defined for types of - * Attributes other than dates and times", DICOM PS 3.4, - * C.2.2.2.5 ("Range Matching"). - **/ - AssignConstraint(tag, new RangeConstraint(constraint)); - } - else if (constraint.find('\\') != std::string::npos) - { - AssignConstraint(tag, new ListConstraint(constraint)); - } - else if (constraint.find('*') != std::string::npos || - constraint.find('?') != std::string::npos) - { - AssignConstraint(tag, new WildcardConstraint(constraint, sensitive)); - } - else - { - /** - * Case-insensitive match for PN value representation (Patient - * Name). Case-senstive match for all the other value - * representations. - * - * Reference: DICOM PS 3.4 - * - C.2.2.2.1 ("Single Value Matching") - * - C.2.2.2.4 ("Wild Card Matching") - * http://medical.nema.org/Dicom/2011/11_04pu.pdf - * - * "Except for Attributes with a PN Value Representation, only - * entities with values which match exactly the value specified in the - * request shall match. This matching is case-sensitive, i.e., - * sensitive to the exact encoding of the key attribute value in - * character sets where a letter may have multiple encodings (e.g., - * based on its case, its position in a word, or whether it is - * accented) - * - * For Attributes with a PN Value Representation (e.g., Patient Name - * (0010,0010)), an application may perform literal matching that is - * either case-sensitive, or that is insensitive to some or all - * aspects of case, position, accent, or other character encoding - * variants." - * - * (0008,0018) UI SOPInstanceUID => Case-sensitive - * (0008,0050) SH AccessionNumber => Case-sensitive - * (0010,0020) LO PatientID => Case-sensitive - * (0020,000D) UI StudyInstanceUID => Case-sensitive - * (0020,000E) UI SeriesInstanceUID => Case-sensitive - **/ - - AssignConstraint(tag, new ValueConstraint(constraint, sensitive)); - } - } - - - bool DicomFindQuery::RestrictIdentifier(std::string& value, - DicomTag identifier) const - { - Constraints::const_iterator it = constraints_.find(identifier); - if (it == constraints_.end() || - !it->second->IsExactConstraint()) - { - return false; - } - else - { - value = dynamic_cast(it->second)->GetValue(); - return true; - } - } - - bool DicomFindQuery::HasMainDicomTagsFilter(ResourceType level) const - { - return filteredLevels_.find(level) != filteredLevels_.end(); - } - - bool DicomFindQuery::FilterMainDicomTags(const std::string& resourceId, - ResourceType level, - const DicomMap& mainTags) const - { - std::set tags; - mainTags.GetTags(tags); - - for (std::set::const_iterator - it = tags.begin(); it != tags.end(); ++it) - { - const DicomValue& value = mainTags.GetValue(*it); - if (value.IsBinary() || value.IsNull()) - { - return false; - } - - Constraints::const_iterator constraint = constraints_.find(*it); - if (constraint != constraints_.end() && - !constraint->second->Apply(value.GetContent())) - { - return false; - } - } - - return true; - } - - bool DicomFindQuery::HasInstanceFilter() const - { - return filterJson_; - } - - bool DicomFindQuery::FilterInstance(const std::string& instanceId, - const Json::Value& content) const - { - for (Constraints::const_iterator it = constraints_.begin(); - it != constraints_.end(); ++it) - { - std::string tag = it->first.Format(); - std::string value; - if (content.isMember(tag)) - { - value = content.get(tag, Json::arrayValue).get("Value", "").asString(); - } - - if (!it->second->Apply(value)) - { - return false; - } - } - - return true; - } -} diff -r 318c2e83c2bd -r e268412adcf1 OrthancServer/DicomFindQuery.h --- a/OrthancServer/DicomFindQuery.h Wed Oct 28 12:53:45 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "ResourceFinder.h" - -namespace Orthanc -{ - class DicomFindQuery : public ResourceFinder::IQuery - { - private: - class IConstraint : public boost::noncopyable - { - public: - virtual ~IConstraint() - { - } - - virtual bool IsExactConstraint() const - { - return false; - } - - virtual bool Apply(const std::string& value) const = 0; - }; - - - class ValueConstraint; - class RangeConstraint; - class ListConstraint; - class WildcardConstraint; - - typedef std::map Constraints; - typedef std::map MainDicomTags; - - MainDicomTags mainDicomTags_; - ResourceType level_; - bool filterJson_; - Constraints constraints_; - std::set filteredLevels_; - - void AssignConstraint(const DicomTag& tag, - IConstraint* constraint); - - void PrepareMainDicomTags(ResourceType level); - - - public: - DicomFindQuery(); - - virtual ~DicomFindQuery(); - - void SetLevel(ResourceType level) - { - level_ = level; - } - - virtual ResourceType GetLevel() const - { - return level_; - } - - void SetConstraint(const DicomTag& tag, - const std::string& constraint, - bool caseSensitivePN); - - virtual bool RestrictIdentifier(std::string& value, - DicomTag identifier) const; - - virtual bool HasMainDicomTagsFilter(ResourceType level) const; - - virtual bool FilterMainDicomTags(const std::string& resourceId, - ResourceType level, - const DicomMap& mainTags) const; - - virtual bool HasInstanceFilter() const; - - virtual bool FilterInstance(const std::string& instanceId, - const Json::Value& content) const; - }; -} diff -r 318c2e83c2bd -r e268412adcf1 OrthancServer/OrthancFindRequestHandler.cpp --- a/OrthancServer/OrthancFindRequestHandler.cpp Wed Oct 28 12:53:45 2015 +0100 +++ b/OrthancServer/OrthancFindRequestHandler.cpp Wed Oct 28 12:59:18 2015 +0100 @@ -30,22 +30,15 @@ **/ -#define USE_LOOKUP_RESOURCE 1 - - - #include "PrecompiledHeadersServer.h" #include "OrthancFindRequestHandler.h" +#include "../Core/DicomFormat/DicomArray.h" #include "../Core/Logging.h" -#include "../Core/DicomFormat/DicomArray.h" -#include "ServerToolbox.h" -#include "OrthancInitialization.h" #include "FromDcmtkBridge.h" - -#include "ResourceFinder.h" -#include "DicomFindQuery.h" +#include "OrthancInitialization.h" #include "Search/LookupResource.h" +#include "ServerToolbox.h" #include @@ -95,130 +88,6 @@ } - namespace - { - class CFindQuery : public DicomFindQuery - { - private: - DicomFindAnswers& answers_; - ServerIndex& index_; - const DicomArray& query_; - bool hasModalitiesInStudy_; - std::set modalitiesInStudy_; - - public: - CFindQuery(DicomFindAnswers& answers, - ServerIndex& index, - const DicomArray& query) : - answers_(answers), - index_(index), - query_(query), - hasModalitiesInStudy_(false) - { - } - - void SetModalitiesInStudy(const std::string& value) - { - hasModalitiesInStudy_ = true; - - std::vector tmp; - Toolbox::TokenizeString(tmp, value, '\\'); - - for (size_t i = 0; i < tmp.size(); i++) - { - modalitiesInStudy_.insert(tmp[i]); - } - } - - virtual bool HasMainDicomTagsFilter(ResourceType level) const - { - if (DicomFindQuery::HasMainDicomTagsFilter(level)) - { - return true; - } - - return (level == ResourceType_Study && - hasModalitiesInStudy_); - } - - virtual bool FilterMainDicomTags(const std::string& resourceId, - ResourceType level, - const DicomMap& mainTags) const - { - if (!DicomFindQuery::FilterMainDicomTags(resourceId, level, mainTags)) - { - return false; - } - - if (level != ResourceType_Study || - !hasModalitiesInStudy_) - { - return true; - } - - try - { - // We are considering a single study, and the - // "MODALITIES_IN_STUDY" tag is set in the C-Find. Check - // whether one of its child series matches one of the - // modalities. - - Json::Value study; - if (index_.LookupResource(study, resourceId, ResourceType_Study)) - { - // Loop over the series of the considered study. - for (Json::Value::ArrayIndex j = 0; j < study["Series"].size(); j++) - { - Json::Value series; - if (index_.LookupResource(series, study["Series"][j].asString(), ResourceType_Series)) - { - // Get the modality of this series - if (series["MainDicomTags"].isMember("Modality")) - { - std::string modality = series["MainDicomTags"]["Modality"].asString(); - if (modalitiesInStudy_.find(modality) != modalitiesInStudy_.end()) - { - // This series of the considered study matches one - // of the required modalities. Take the study into - // consideration for future filtering. - return true; - } - } - } - } - } - } - catch (OrthancException&) - { - // This resource has probably been deleted during the find request - } - - return false; - } - - virtual bool HasInstanceFilter() const - { - return true; - } - - virtual bool FilterInstance(const std::string& instanceId, - const Json::Value& content) const - { - bool ok = DicomFindQuery::FilterInstance(instanceId, content); - - if (ok) - { - // Add this resource to the answers - AddAnswer(answers_, content, query_); - } - - return ok; - } - }; - } - - - bool OrthancFindRequestHandler::Handle(DicomFindAnswers& answers, const DicomMap& input, const std::string& remoteIp, @@ -281,13 +150,8 @@ * Build up the query object. **/ -#if USE_LOOKUP_RESOURCE == 1 LookupResource finder(level); -#else - CFindQuery findQuery(answers, context_.GetIndex(), query); - findQuery.SetLevel(level); -#endif - + for (size_t i = 0; i < query.GetSize(); i++) { const DicomTag tag = query.GetElement(i).GetTag(); @@ -306,28 +170,17 @@ continue; } -#if USE_LOOKUP_RESOURCE == 1 - ValueRepresentation vr = FromDcmtkBridge::GetValueRepresentation(tag); + // DICOM specifies that searches must be case sensitive, except + // for tags with a PN value representation bool sensitive = true; if (vr == ValueRepresentation_PatientName) { - sensitive = caseSensitivePN; - } + sensitive = caseSensitivePN; + } finder.AddDicomConstraint(tag, value, sensitive); -#else - - if (tag == DICOM_TAG_MODALITIES_IN_STUDY) - { - findQuery.SetModalitiesInStudy(value); - } - else - { - findQuery.SetConstraint(tag, value, caseSensitivePN); - } -#endif } @@ -335,30 +188,8 @@ * Run the query. **/ -#if USE_LOOKUP_RESOURCE != 1 - ResourceFinder finder(context_); - - switch (level) - { - case ResourceType_Patient: - case ResourceType_Study: - case ResourceType_Series: - finder.SetMaxResults(maxResults_); - break; - - case ResourceType_Instance: - finder.SetMaxResults(maxInstances_); - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } -#else size_t maxResults = (level == ResourceType_Instance) ? maxInstances_ : maxResults_; -#endif - -#if USE_LOOKUP_RESOURCE == 1 std::vector resources, instances; context_.GetIndex().FindCandidates(resources, instances, finder); @@ -385,11 +216,6 @@ } } -#else - std::list tmp; - bool finished = finder.Apply(tmp, findQuery); -#endif - LOG(INFO) << "Number of matching resources: " << answers.GetSize(); return finished; diff -r 318c2e83c2bd -r e268412adcf1 OrthancServer/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Wed Oct 28 12:53:45 2015 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Wed Oct 28 12:59:18 2015 +0100 @@ -36,8 +36,6 @@ #include "../../Core/Logging.h" #include "../ServerToolbox.h" #include "../FromDcmtkBridge.h" -#include "../ResourceFinder.h" -#include "../DicomFindQuery.h" #include "../ServerContext.h" #include "../SliceOrdering.h" diff -r 318c2e83c2bd -r e268412adcf1 OrthancServer/ResourceFinder.cpp --- a/OrthancServer/ResourceFinder.cpp Wed Oct 28 12:53:45 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,409 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "PrecompiledHeadersServer.h" -#include "ResourceFinder.h" - -#include "../Core/Logging.h" -#include "FromDcmtkBridge.h" -#include "ServerContext.h" - -namespace Orthanc -{ - class ResourceFinder::CandidateResources - { - private: - typedef std::map Query; - - ResourceFinder& finder_; - ServerIndex& index_; - ResourceType level_; - bool isFilterApplied_; - std::set filtered_; - - - static void ListToSet(std::set& target, - const std::list& source) - { - for (std::list::const_iterator - it = source.begin(); it != source.end(); ++it) - { - target.insert(*it); - } - } - - - public: - CandidateResources(ResourceFinder& finder) : - finder_(finder), - index_(finder.context_.GetIndex()), - level_(ResourceType_Patient), - isFilterApplied_(false) - { - } - - ResourceType GetLevel() const - { - return level_; - } - - void GoDown() - { - assert(level_ != ResourceType_Instance); - - if (isFilterApplied_) - { - std::set tmp = filtered_; - - filtered_.clear(); - - for (std::set::const_iterator - it = tmp.begin(); it != tmp.end(); ++it) - { - std::list children; - try - { - index_.GetChildren(children, *it); - ListToSet(filtered_, children); - } - catch (OrthancException&) - { - // The resource was removed in the meantime - } - } - } - - switch (level_) - { - 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); - } - } - - - void Flatten(std::list& resources) const - { - resources.clear(); - - if (isFilterApplied_) - { - for (std::set::const_iterator - it = filtered_.begin(); it != filtered_.end(); ++it) - { - resources.push_back(*it); - } - } - else - { - index_.GetAllUuids(resources, level_); - } - } - - - void RestrictIdentifier(const IQuery& query, - const DicomTag& tag) - { - 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)); - - std::string value; - if (!query.RestrictIdentifier(value, tag)) - { - return; - } - - LOG(INFO) << "Lookup for identifier tag " - << FromDcmtkBridge::GetName(tag) << " (value: " << value << ")"; - - std::list resources; - index_.LookupIdentifierExact(resources, level_, tag, value); - - if (isFilterApplied_) - { - std::set s; - ListToSet(s, resources); - - std::set tmp = filtered_; - filtered_.clear(); - - for (std::set::const_iterator - it = tmp.begin(); it != tmp.end(); ++it) - { - if (s.find(*it) != s.end()) - { - filtered_.insert(*it); - } - } - } - else - { - assert(filtered_.empty()); - isFilterApplied_ = true; - ListToSet(filtered_, resources); - } - } - - - void RestrictMainDicomTags(const IQuery& query, - bool filterPatientTagsAtStudyLevel) - { - if (filterPatientTagsAtStudyLevel && - level_ == ResourceType_Patient) - { - return; - } - - bool hasTagsAtThisLevel = query.HasMainDicomTagsFilter(level_); - bool hasTagsAtPatientLevel = (filterPatientTagsAtStudyLevel && - level_ == ResourceType_Study && - query.HasMainDicomTagsFilter(ResourceType_Patient)); - - if (!hasTagsAtThisLevel && !hasTagsAtPatientLevel) - { - return; - } - - std::list resources; - Flatten(resources); - - isFilterApplied_ = true; - filtered_.clear(); - - for (std::list::const_iterator - it = resources.begin(); it != resources.end(); ++it) - { - DicomMap mainTags; - - if (hasTagsAtThisLevel && - (!index_.GetMainDicomTags(mainTags, *it, level_, level_) || - !query.FilterMainDicomTags(*it, level_, mainTags))) - { - continue; - } - - if (hasTagsAtPatientLevel && - (!index_.GetMainDicomTags(mainTags, *it, ResourceType_Study, ResourceType_Patient) || - !query.FilterMainDicomTags(*it, ResourceType_Patient, mainTags))) - { - continue; - } - - filtered_.insert(*it); - } - } - }; - - - ResourceFinder::ResourceFinder(ServerContext& context) : - context_(context), - maxResults_(0) - { - } - - - void ResourceFinder::ApplyAtLevel(CandidateResources& candidates, - const IQuery& query, - ResourceType level) - { - if (level != ResourceType_Patient) - { - candidates.GoDown(); - } - - switch (level) - { - case ResourceType_Patient: - { - candidates.RestrictIdentifier(query, DICOM_TAG_PATIENT_ID); - break; - } - - case ResourceType_Study: - { - candidates.RestrictIdentifier(query, DICOM_TAG_STUDY_INSTANCE_UID); - candidates.RestrictIdentifier(query, DICOM_TAG_ACCESSION_NUMBER); - break; - } - - case ResourceType_Series: - { - candidates.RestrictIdentifier(query, DICOM_TAG_SERIES_INSTANCE_UID); - break; - } - - case ResourceType_Instance: - { - candidates.RestrictIdentifier(query, DICOM_TAG_SOP_INSTANCE_UID); - break; - } - - default: - throw OrthancException(ErrorCode_InternalError); - } - - if (query.GetLevel() == ResourceType_Patient) - { - candidates.RestrictMainDicomTags(query, false); - } - else - { - candidates.RestrictMainDicomTags(query, true); - } - } - - - - static bool LookupOneInstance(std::string& result, - ServerIndex& index, - const std::string& id, - ResourceType type) - { - if (type == ResourceType_Instance) - { - result = id; - return true; - } - - std::string childId; - - { - std::list children; - index.GetChildInstances(children, id); - - if (children.empty()) - { - return false; - } - - childId = children.front(); - } - - return LookupOneInstance(result, index, childId, GetChildResourceType(type)); - } - - - bool ResourceFinder::Apply(std::list& result, - const IQuery& query) - { - CandidateResources candidates(*this); - - ApplyAtLevel(candidates, query, ResourceType_Patient); - - const ResourceType level = query.GetLevel(); - - if (level == ResourceType_Study || - level == ResourceType_Series || - level == ResourceType_Instance) - { - ApplyAtLevel(candidates, query, ResourceType_Study); - } - - if (level == ResourceType_Series || - level == ResourceType_Instance) - { - ApplyAtLevel(candidates, query, ResourceType_Series); - } - - if (level == ResourceType_Instance) - { - ApplyAtLevel(candidates, query, ResourceType_Instance); - } - - if (!query.HasInstanceFilter()) - { - candidates.Flatten(result); - - if (maxResults_ != 0 && - result.size() >= maxResults_) - { - result.resize(maxResults_); - return false; - } - else - { - return true; - } - } - else - { - std::list tmp; - candidates.Flatten(tmp); - - result.clear(); - for (std::list::const_iterator - resource = tmp.begin(); resource != tmp.end(); ++resource) - { - try - { - std::string instance; - if (LookupOneInstance(instance, context_.GetIndex(), *resource, level)) - { - Json::Value content; - context_.ReadJson(content, instance); - if (query.FilterInstance(*resource, content)) - { - result.push_back(*resource); - - if (maxResults_ != 0 && - result.size() >= maxResults_) - { - // Too many results, stop before recording this new match - return false; - } - } - } - } - catch (OrthancException&) - { - // This resource has been deleted since the search was started - } - } - } - - return true; // All the matching resources have been returned - } -} diff -r 318c2e83c2bd -r e268412adcf1 OrthancServer/ResourceFinder.h --- a/OrthancServer/ResourceFinder.h Wed Oct 28 12:53:45 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,101 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "ServerIndex.h" - -#include - -namespace Orthanc -{ - class ResourceFinder : public boost::noncopyable - { - public: - class IQuery : public boost::noncopyable - { - public: - virtual ~IQuery() - { - } - - virtual ResourceType GetLevel() const = 0; - - virtual bool RestrictIdentifier(std::string& value, - DicomTag identifier) const = 0; - - virtual bool HasMainDicomTagsFilter(ResourceType level) const = 0; - - virtual bool FilterMainDicomTags(const std::string& resourceId, - ResourceType level, - const DicomMap& mainTags) const = 0; - - virtual bool HasInstanceFilter() const = 0; - - virtual bool FilterInstance(const std::string& instanceId, - const Json::Value& content) const = 0; - }; - - - private: - typedef std::map Identifiers; - - class CandidateResources; - - ServerContext& context_; - size_t maxResults_; - - void ApplyAtLevel(CandidateResources& candidates, - const IQuery& query, - ResourceType level); - - public: - ResourceFinder(ServerContext& context); - - void SetMaxResults(size_t value) - { - maxResults_ = value; - } - - size_t GetMaxResults() const - { - return maxResults_; - } - - // Returns "true" iff. all the matching resources have been - // returned. Will be "false" if the results were truncated by - // "SetMaxResults()". - bool Apply(std::list& result, - const IQuery& query); - }; - -}