# HG changeset patch # User Sebastien Jodogne # Date 1545233227 -3600 # Node ID 8fd203510d8b57b39bdf2a273be9c94bfd13b0cc # Parent 54e422fe31ceac9818f710f7fca941f2a951faaa moving LookupIdentifierQuery to the graveyard diff -r 54e422fe31ce -r 8fd203510d8b CMakeLists.txt --- a/CMakeLists.txt Wed Dec 19 14:20:11 2018 +0100 +++ b/CMakeLists.txt Wed Dec 19 16:27:07 2018 +0100 @@ -77,7 +77,6 @@ OrthancServer/Search/HierarchicalMatcher.cpp OrthancServer/Search/IFindConstraint.cpp OrthancServer/Search/ListConstraint.cpp - OrthancServer/Search/LookupIdentifierQuery.cpp OrthancServer/Search/RangeConstraint.cpp OrthancServer/Search/SetOfResources.cpp OrthancServer/Search/ValueConstraint.cpp diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/IDatabaseWrapper.h --- a/OrthancServer/IDatabaseWrapper.h Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/IDatabaseWrapper.h Wed Dec 19 16:27:07 2018 +0100 @@ -228,7 +228,7 @@ virtual bool IsDiskSizeAbove(uint64_t threshold) = 0; virtual void ApplyLookupResources(std::vector& resourcesId, - std::vector& instancesId, + std::vector* instancesId, // Can be NULL if not needed const std::vector& lookup, ResourceType queryLevel, size_t limit) = 0; diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/SQLiteDatabaseWrapper.cpp --- a/OrthancServer/SQLiteDatabaseWrapper.cpp Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/SQLiteDatabaseWrapper.cpp Wed Dec 19 16:27:07 2018 +0100 @@ -1498,11 +1498,17 @@ void SQLiteDatabaseWrapper::ApplyLookupResources(std::vector& resourcesId, - std::vector& instancesId, + std::vector* instancesId, const std::vector& lookup, ResourceType queryLevel, size_t limit) { + for (size_t i = 0; i < lookup.size(); i++) + { + std::cout << i << ": " << lookup[i].GetTag() << " - " << EnumerationToString(lookup[i].GetLevel()); + std::cout << std::endl; + } + assert(ResourceType_Patient < ResourceType_Study && ResourceType_Study < ResourceType_Series && ResourceType_Series < ResourceType_Instance); @@ -1584,16 +1590,33 @@ sql += " LIMIT " + boost::lexical_cast(limit); } + printf("[%s]\n", sql.c_str()); + SQLite::Statement s(db_, sql); for (size_t i = 0; i < parameters.size(); i++) { + printf(" %lu = '%s'\n", i, parameters[i].c_str()); s.BindString(i, parameters[i]); } s.Run(); } - AnswerLookup(resourcesId, instancesId, db_, queryLevel); + if (instancesId != NULL) + { + AnswerLookup(resourcesId, *instancesId, db_, queryLevel); + } + else + { + resourcesId.clear(); + + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Lookup"); + + while (s.Step()) + { + resourcesId.push_back(s.ColumnString(0)); + } + } } } diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/SQLiteDatabaseWrapper.h --- a/OrthancServer/SQLiteDatabaseWrapper.h Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/SQLiteDatabaseWrapper.h Wed Dec 19 16:27:07 2018 +0100 @@ -278,7 +278,7 @@ virtual bool IsDiskSizeAbove(uint64_t threshold); virtual void ApplyLookupResources(std::vector& resourcesId, - std::vector& instancesId, + std::vector* instancesId, const std::vector& lookup, ResourceType queryLevel, size_t limit); diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/Search/IFindConstraint.cpp --- a/OrthancServer/Search/IFindConstraint.cpp Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/Search/IFindConstraint.cpp Wed Dec 19 16:27:07 2018 +0100 @@ -41,6 +41,7 @@ #include "../../Core/DicomParsing/FromDcmtkBridge.h" #include "../../Core/OrthancException.h" +#include "../../Core/Toolbox.h" namespace Orthanc { diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/Search/IFindConstraint.h --- a/OrthancServer/Search/IFindConstraint.h Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/Search/IFindConstraint.h Wed Dec 19 16:27:07 2018 +0100 @@ -33,7 +33,9 @@ #pragma once -#include "LookupIdentifierQuery.h" +#include "../../Core/DicomFormat/DicomTag.h" + +#include namespace Orthanc { @@ -46,9 +48,6 @@ virtual IFindConstraint* Clone() const = 0; - virtual void Setup(LookupIdentifierQuery& lookup, - const DicomTag& tag) const = 0; - virtual bool Match(const std::string& value) const = 0; virtual std::string Format() const = 0; diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/Search/ListConstraint.cpp --- a/OrthancServer/Search/ListConstraint.cpp Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/Search/ListConstraint.cpp Wed Dec 19 16:27:07 2018 +0100 @@ -34,6 +34,7 @@ #include "../PrecompiledHeadersServer.h" #include "ListConstraint.h" +#include "../../Core/Toolbox.h" namespace Orthanc { @@ -50,19 +51,6 @@ } - void ListConstraint::Setup(LookupIdentifierQuery& lookup, - const DicomTag& tag) const - { - LookupIdentifierQuery::Disjunction& target = lookup.AddDisjunction(); - - for (std::set::const_iterator - it = allowedValues_.begin(); it != allowedValues_.end(); ++it) - { - target.Add(tag, IdentifierConstraintType_Equal, *it); - } - } - - bool ListConstraint::Match(const std::string& value) const { std::string s; diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/Search/ListConstraint.h --- a/OrthancServer/Search/ListConstraint.h Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/Search/ListConstraint.h Wed Dec 19 16:27:07 2018 +0100 @@ -64,9 +64,6 @@ return new ListConstraint(*this); } - virtual void Setup(LookupIdentifierQuery& lookup, - const DicomTag& tag) const; - virtual bool Match(const std::string& value) const; virtual std::string Format() const; diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/Search/LookupIdentifierQuery.cpp --- a/OrthancServer/Search/LookupIdentifierQuery.cpp Wed Dec 19 14:20:11 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,215 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2018 Osimis S.A., 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 "LookupIdentifierQuery.h" - -#include "../../Core/DicomParsing/FromDcmtkBridge.h" -#include "../../Core/OrthancException.h" -#include "../ServerToolbox.h" -#include "SetOfResources.h" - -#include - - - -namespace Orthanc -{ - LookupIdentifierQuery::SingleConstraint:: - SingleConstraint(const DicomTag& tag, - IdentifierConstraintType type, - const std::string& value) : - tag_(tag), - type_(type), - value_(ServerToolbox::NormalizeIdentifier(value)) - { - } - - - LookupIdentifierQuery::RangeConstraint:: - RangeConstraint(const DicomTag& tag, - const std::string& start, - const std::string& end) : - tag_(tag), - start_(ServerToolbox::NormalizeIdentifier(start)), - end_(ServerToolbox::NormalizeIdentifier(end)) - { - } - - - LookupIdentifierQuery::Disjunction::~Disjunction() - { - for (size_t i = 0; i < singleConstraints_.size(); i++) - { - delete singleConstraints_[i]; - } - - for (size_t i = 0; i < rangeConstraints_.size(); i++) - { - delete rangeConstraints_[i]; - } - } - - - void LookupIdentifierQuery::Disjunction::Add(const DicomTag& tag, - IdentifierConstraintType type, - const std::string& value) - { - singleConstraints_.push_back(new SingleConstraint(tag, type, value)); - } - - - void LookupIdentifierQuery::Disjunction::AddRange(const DicomTag& tag, - const std::string& start, - const std::string& end) - { - rangeConstraints_.push_back(new RangeConstraint(tag, start, end)); - } - - - LookupIdentifierQuery::~LookupIdentifierQuery() - { - for (Disjunctions::iterator it = disjunctions_.begin(); - it != disjunctions_.end(); ++it) - { - delete *it; - } - } - - - bool LookupIdentifierQuery::IsIdentifier(const DicomTag& tag) - { - return ServerToolbox::IsIdentifier(tag, level_); - } - - - void LookupIdentifierQuery::AddConstraint(DicomTag tag, - IdentifierConstraintType type, - const std::string& value) - { - assert(IsIdentifier(tag)); - disjunctions_.push_back(new Disjunction); - disjunctions_.back()->Add(tag, type, value); - } - - - void LookupIdentifierQuery::AddRange(DicomTag tag, - const std::string& start, - const std::string& end) - { - assert(IsIdentifier(tag)); - disjunctions_.push_back(new Disjunction); - disjunctions_.back()->AddRange(tag, start, end); - } - - - LookupIdentifierQuery::Disjunction& LookupIdentifierQuery::AddDisjunction() - { - disjunctions_.push_back(new Disjunction); - return *disjunctions_.back(); - } - - - void LookupIdentifierQuery::Apply(std::list& result, - 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 < disjunctions_.size(); i++) - { - std::list a; - - for (size_t j = 0; j < disjunctions_[i]->GetSingleConstraintsCount(); j++) - { - const SingleConstraint& constraint = disjunctions_[i]->GetSingleConstraint(j); - std::list b; - database.LookupIdentifier(b, level_, constraint.GetTag(), - constraint.GetType(), constraint.GetValue()); - - a.splice(a.end(), b); - } - - for (size_t j = 0; j < disjunctions_[i]->GetRangeConstraintsCount(); j++) - { - const RangeConstraint& constraint = disjunctions_[i]->GetRangeConstraint(j); - std::list b; - database.LookupIdentifierRange(b, level_, constraint.GetTag(), - constraint.GetStart(), constraint.GetEnd()); - - a.splice(a.end(), b); - } - - result.Intersect(a); - } - } - - - void LookupIdentifierQuery::Print(std::ostream& s) const - { - s << "Constraint: " << std::endl; - for (Disjunctions::const_iterator - it = disjunctions_.begin(); it != disjunctions_.end(); ++it) - { - if (it == disjunctions_.begin()) - s << " "; - else - s << "OR "; - - for (size_t j = 0; j < (*it)->GetSingleConstraintsCount(); j++) - { - const SingleConstraint& c = (*it)->GetSingleConstraint(j); - s << FromDcmtkBridge::GetTagName(c.GetTag(), ""); - - switch (c.GetType()) - { - case IdentifierConstraintType_Equal: s << " == "; break; - case IdentifierConstraintType_SmallerOrEqual: s << " <= "; break; - case IdentifierConstraintType_GreaterOrEqual: s << " >= "; break; - case IdentifierConstraintType_Wildcard: s << " ~= "; break; - default: - s << " ? "; - } - - s << c.GetValue() << std::endl; - } - } - } -} diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/Search/LookupIdentifierQuery.h --- a/OrthancServer/Search/LookupIdentifierQuery.h Wed Dec 19 14:20:11 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,207 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2018 Osimis S.A., 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 "../IDatabaseWrapper.h" - -#include "SetOfResources.h" - -#include -#include - -namespace Orthanc -{ - /** - * Primitive for wildcard matching, as defined in DICOM: - * http://dicom.nema.org/dicom/2013/output/chtml/part04/sect_C.2.html#sect_C.2.2.2.4 - * - * "Any occurrence of an "*" or a "?", then "*" shall match any - * sequence of characters (including a zero length value) and "?" - * shall match any single character. This matching is case - * sensitive, except for Attributes with an PN Value - * Representation (e.g., Patient Name (0010,0010))." - * - * Pay attention to the fact that "*" (resp. "?") generally - * corresponds to "%" (resp. "_") in primitive LIKE of SQL. The - * values "%", "_", "\" should in the user request should - * respectively be escaped as "\%", "\_" and "\\". - * - * This matching must be case sensitive: The special case of PN VR - * is taken into consideration by normalizing the query string in - * method "NormalizeIdentifier()". - **/ - - class LookupIdentifierQuery : public boost::noncopyable - { - // This class encodes a conjunction ("AND") of disjunctions. Each - // disjunction represents an "OR" of several constraints. - - public: - class SingleConstraint - { - private: - DicomTag tag_; - IdentifierConstraintType type_; - std::string value_; - - public: - SingleConstraint(const DicomTag& tag, - IdentifierConstraintType type, - const std::string& value); - - const DicomTag& GetTag() const - { - return tag_; - } - - IdentifierConstraintType GetType() const - { - return type_; - } - - const std::string& GetValue() const - { - return value_; - } - }; - - - class RangeConstraint - { - private: - DicomTag tag_; - std::string start_; - std::string end_; - - public: - RangeConstraint(const DicomTag& tag, - const std::string& start, - const std::string& end); - - const DicomTag& GetTag() const - { - return tag_; - } - - const std::string& GetStart() const - { - return start_; - } - - const std::string& GetEnd() const - { - return end_; - } - }; - - - class Disjunction : public boost::noncopyable - { - private: - std::vector singleConstraints_; - std::vector rangeConstraints_; - - public: - ~Disjunction(); - - void Add(const DicomTag& tag, - IdentifierConstraintType type, - const std::string& value); - - void AddRange(const DicomTag& tag, - const std::string& start, - const std::string& end); - - size_t GetSingleConstraintsCount() const - { - return singleConstraints_.size(); - } - - const SingleConstraint& GetSingleConstraint(size_t i) const - { - return *singleConstraints_[i]; - } - - size_t GetRangeConstraintsCount() const - { - return rangeConstraints_.size(); - } - - const RangeConstraint& GetRangeConstraint(size_t i) const - { - return *rangeConstraints_[i]; - } - }; - - - private: - typedef std::vector Disjunctions; - - ResourceType level_; - Disjunctions disjunctions_; - - public: - LookupIdentifierQuery(ResourceType level) : level_(level) - { - } - - ~LookupIdentifierQuery(); - - bool IsIdentifier(const DicomTag& tag); - - void AddConstraint(DicomTag tag, - IdentifierConstraintType type, - const std::string& value); - - void AddRange(DicomTag tag, - const std::string& start, - const std::string& end); - - Disjunction& AddDisjunction(); - - ResourceType GetLevel() const - { - return level_; - } - - // The database must be locked - void Apply(std::list& result, - IDatabaseWrapper& database); - - void Apply(SetOfResources& result, - IDatabaseWrapper& database); - - void Print(std::ostream& s) const; - }; -} diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/Search/RangeConstraint.cpp --- a/OrthancServer/Search/RangeConstraint.cpp Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/Search/RangeConstraint.cpp Wed Dec 19 16:27:07 2018 +0100 @@ -56,29 +56,6 @@ } - void RangeConstraint::Setup(LookupIdentifierQuery& lookup, - const DicomTag& tag) const - { - if (!lower_.empty() && - !upper_.empty()) - { - lookup.AddRange(tag, lower_, upper_); - } - else - { - if (!lower_.empty()) - { - lookup.AddConstraint(tag, IdentifierConstraintType_GreaterOrEqual, lower_); - } - - if (!upper_.empty()) - { - lookup.AddConstraint(tag, IdentifierConstraintType_SmallerOrEqual, upper_); - } - } - } - - bool RangeConstraint::Match(const std::string& value) const { std::string v; diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/Search/RangeConstraint.h --- a/OrthancServer/Search/RangeConstraint.h Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/Search/RangeConstraint.h Wed Dec 19 16:27:07 2018 +0100 @@ -61,9 +61,6 @@ return new RangeConstraint(*this); } - virtual void Setup(LookupIdentifierQuery& lookup, - const DicomTag& tag) const; - virtual bool Match(const std::string& value) const; virtual std::string Format() const diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/Search/ValueConstraint.cpp --- a/OrthancServer/Search/ValueConstraint.cpp Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/Search/ValueConstraint.cpp Wed Dec 19 16:27:07 2018 +0100 @@ -55,12 +55,6 @@ } - void ValueConstraint::Setup(LookupIdentifierQuery& lookup, - const DicomTag& tag) const - { - lookup.AddConstraint(tag, IdentifierConstraintType_Equal, value_); - } - bool ValueConstraint::Match(const std::string& value) const { if (isCaseSensitive_) diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/Search/ValueConstraint.h --- a/OrthancServer/Search/ValueConstraint.h Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/Search/ValueConstraint.h Wed Dec 19 16:27:07 2018 +0100 @@ -58,9 +58,6 @@ return new ValueConstraint(*this); } - virtual void Setup(LookupIdentifierQuery& lookup, - const DicomTag& tag) const; - virtual bool Match(const std::string& value) const; virtual std::string Format() const diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/Search/WildcardConstraint.cpp --- a/OrthancServer/Search/WildcardConstraint.cpp Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/Search/WildcardConstraint.cpp Wed Dec 19 16:27:07 2018 +0100 @@ -34,6 +34,8 @@ #include "../PrecompiledHeadersServer.h" #include "WildcardConstraint.h" +#include "../../Core/Toolbox.h" + #include namespace Orthanc @@ -87,12 +89,6 @@ } } - void WildcardConstraint::Setup(LookupIdentifierQuery& lookup, - const DicomTag& tag) const - { - lookup.AddConstraint(tag, IdentifierConstraintType_Wildcard, pimpl_->wildcard_); - } - std::string WildcardConstraint::Format() const { return pimpl_->wildcard_; diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/Search/WildcardConstraint.h --- a/OrthancServer/Search/WildcardConstraint.h Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/Search/WildcardConstraint.h Wed Dec 19 16:27:07 2018 +0100 @@ -56,9 +56,6 @@ return new WildcardConstraint(*this); } - virtual void Setup(LookupIdentifierQuery& lookup, - const DicomTag& tag) const; - virtual bool Match(const std::string& value) const; virtual std::string Format() const; diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/ServerContext.cpp --- a/OrthancServer/ServerContext.cpp Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/ServerContext.cpp Wed Dec 19 16:27:07 2018 +0100 @@ -819,7 +819,7 @@ std::vector resources, instances; const size_t lookupLimit = (databaseLimit == 0 ? 0 : databaseLimit + 1); - GetIndex().ApplyLookupResources(resources, instances, lookup, queryLevel, lookupLimit); + GetIndex().ApplyLookupResources(resources, &instances, lookup, queryLevel, lookupLimit); bool complete = (databaseLimit == 0 || resources.size() > databaseLimit); diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/ServerIndex.cpp --- a/OrthancServer/ServerIndex.cpp Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/ServerIndex.cpp Wed Dec 19 16:27:07 2018 +0100 @@ -2141,11 +2141,9 @@ std::vector query; query.push_back(DatabaseConstraint(c, level, DicomTagType_Identifier)); - std::vector instancesId; - { boost::mutex::scoped_lock lock(mutex_); - db_.ApplyLookupResources(result, instancesId, query, level, 0); + db_.ApplyLookupResources(result, NULL, query, level, 0); } } @@ -2565,7 +2563,7 @@ void ServerIndex::ApplyLookupResources(std::vector& resourcesId, - std::vector& instancesId, + std::vector* instancesId, const DatabaseLookup& lookup, ResourceType queryLevel, size_t limit) diff -r 54e422fe31ce -r 8fd203510d8b OrthancServer/ServerIndex.h --- a/OrthancServer/ServerIndex.h Wed Dec 19 14:20:11 2018 +0100 +++ b/OrthancServer/ServerIndex.h Wed Dec 19 16:27:07 2018 +0100 @@ -44,7 +44,6 @@ namespace Orthanc { - class LookupResource; class ServerContext; class DicomInstanceToStore; class ParsedDicomFile; @@ -296,7 +295,7 @@ void ReconstructInstance(ParsedDicomFile& dicom); void ApplyLookupResources(std::vector& resourcesId, - std::vector& instancesId, + std::vector* instancesId, // Can be NULL if not needed const DatabaseLookup& lookup, ResourceType queryLevel, size_t limit); diff -r 54e422fe31ce -r 8fd203510d8b Plugins/Engine/OrthancPluginDatabase.cpp --- a/Plugins/Engine/OrthancPluginDatabase.cpp Wed Dec 19 14:20:11 2018 +0100 +++ b/Plugins/Engine/OrthancPluginDatabase.cpp Wed Dec 19 16:27:07 2018 +0100 @@ -1163,7 +1163,7 @@ void OrthancPluginDatabase::ApplyLookupResources(std::vector& patientsId, - std::vector& instancesId, + std::vector* instancesId, const std::vector& lookup, ResourceType queryLevel, size_t limit) diff -r 54e422fe31ce -r 8fd203510d8b Plugins/Engine/OrthancPluginDatabase.h --- a/Plugins/Engine/OrthancPluginDatabase.h Wed Dec 19 14:20:11 2018 +0100 +++ b/Plugins/Engine/OrthancPluginDatabase.h Wed Dec 19 16:27:07 2018 +0100 @@ -272,7 +272,7 @@ virtual bool IsDiskSizeAbove(uint64_t threshold); virtual void ApplyLookupResources(std::vector& patientsId, - std::vector& instancesId, + std::vector* instancesId, const std::vector& lookup, ResourceType queryLevel, size_t limit); diff -r 54e422fe31ce -r 8fd203510d8b Resources/Graveyard/DatabaseOptimizations/LookupIdentifierQuery.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/Graveyard/DatabaseOptimizations/LookupIdentifierQuery.cpp Wed Dec 19 16:27:07 2018 +0100 @@ -0,0 +1,215 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., 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 "LookupIdentifierQuery.h" + +#include "../../Core/DicomParsing/FromDcmtkBridge.h" +#include "../../Core/OrthancException.h" +#include "../ServerToolbox.h" +#include "SetOfResources.h" + +#include + + + +namespace Orthanc +{ + LookupIdentifierQuery::SingleConstraint:: + SingleConstraint(const DicomTag& tag, + IdentifierConstraintType type, + const std::string& value) : + tag_(tag), + type_(type), + value_(ServerToolbox::NormalizeIdentifier(value)) + { + } + + + LookupIdentifierQuery::RangeConstraint:: + RangeConstraint(const DicomTag& tag, + const std::string& start, + const std::string& end) : + tag_(tag), + start_(ServerToolbox::NormalizeIdentifier(start)), + end_(ServerToolbox::NormalizeIdentifier(end)) + { + } + + + LookupIdentifierQuery::Disjunction::~Disjunction() + { + for (size_t i = 0; i < singleConstraints_.size(); i++) + { + delete singleConstraints_[i]; + } + + for (size_t i = 0; i < rangeConstraints_.size(); i++) + { + delete rangeConstraints_[i]; + } + } + + + void LookupIdentifierQuery::Disjunction::Add(const DicomTag& tag, + IdentifierConstraintType type, + const std::string& value) + { + singleConstraints_.push_back(new SingleConstraint(tag, type, value)); + } + + + void LookupIdentifierQuery::Disjunction::AddRange(const DicomTag& tag, + const std::string& start, + const std::string& end) + { + rangeConstraints_.push_back(new RangeConstraint(tag, start, end)); + } + + + LookupIdentifierQuery::~LookupIdentifierQuery() + { + for (Disjunctions::iterator it = disjunctions_.begin(); + it != disjunctions_.end(); ++it) + { + delete *it; + } + } + + + bool LookupIdentifierQuery::IsIdentifier(const DicomTag& tag) + { + return ServerToolbox::IsIdentifier(tag, level_); + } + + + void LookupIdentifierQuery::AddConstraint(DicomTag tag, + IdentifierConstraintType type, + const std::string& value) + { + assert(IsIdentifier(tag)); + disjunctions_.push_back(new Disjunction); + disjunctions_.back()->Add(tag, type, value); + } + + + void LookupIdentifierQuery::AddRange(DicomTag tag, + const std::string& start, + const std::string& end) + { + assert(IsIdentifier(tag)); + disjunctions_.push_back(new Disjunction); + disjunctions_.back()->AddRange(tag, start, end); + } + + + LookupIdentifierQuery::Disjunction& LookupIdentifierQuery::AddDisjunction() + { + disjunctions_.push_back(new Disjunction); + return *disjunctions_.back(); + } + + + void LookupIdentifierQuery::Apply(std::list& result, + 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 < disjunctions_.size(); i++) + { + std::list a; + + for (size_t j = 0; j < disjunctions_[i]->GetSingleConstraintsCount(); j++) + { + const SingleConstraint& constraint = disjunctions_[i]->GetSingleConstraint(j); + std::list b; + database.LookupIdentifier(b, level_, constraint.GetTag(), + constraint.GetType(), constraint.GetValue()); + + a.splice(a.end(), b); + } + + for (size_t j = 0; j < disjunctions_[i]->GetRangeConstraintsCount(); j++) + { + const RangeConstraint& constraint = disjunctions_[i]->GetRangeConstraint(j); + std::list b; + database.LookupIdentifierRange(b, level_, constraint.GetTag(), + constraint.GetStart(), constraint.GetEnd()); + + a.splice(a.end(), b); + } + + result.Intersect(a); + } + } + + + void LookupIdentifierQuery::Print(std::ostream& s) const + { + s << "Constraint: " << std::endl; + for (Disjunctions::const_iterator + it = disjunctions_.begin(); it != disjunctions_.end(); ++it) + { + if (it == disjunctions_.begin()) + s << " "; + else + s << "OR "; + + for (size_t j = 0; j < (*it)->GetSingleConstraintsCount(); j++) + { + const SingleConstraint& c = (*it)->GetSingleConstraint(j); + s << FromDcmtkBridge::GetTagName(c.GetTag(), ""); + + switch (c.GetType()) + { + case IdentifierConstraintType_Equal: s << " == "; break; + case IdentifierConstraintType_SmallerOrEqual: s << " <= "; break; + case IdentifierConstraintType_GreaterOrEqual: s << " >= "; break; + case IdentifierConstraintType_Wildcard: s << " ~= "; break; + default: + s << " ? "; + } + + s << c.GetValue() << std::endl; + } + } + } +} diff -r 54e422fe31ce -r 8fd203510d8b Resources/Graveyard/DatabaseOptimizations/LookupIdentifierQuery.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/Graveyard/DatabaseOptimizations/LookupIdentifierQuery.h Wed Dec 19 16:27:07 2018 +0100 @@ -0,0 +1,207 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., 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 "../IDatabaseWrapper.h" + +#include "SetOfResources.h" + +#include +#include + +namespace Orthanc +{ + /** + * Primitive for wildcard matching, as defined in DICOM: + * http://dicom.nema.org/dicom/2013/output/chtml/part04/sect_C.2.html#sect_C.2.2.2.4 + * + * "Any occurrence of an "*" or a "?", then "*" shall match any + * sequence of characters (including a zero length value) and "?" + * shall match any single character. This matching is case + * sensitive, except for Attributes with an PN Value + * Representation (e.g., Patient Name (0010,0010))." + * + * Pay attention to the fact that "*" (resp. "?") generally + * corresponds to "%" (resp. "_") in primitive LIKE of SQL. The + * values "%", "_", "\" should in the user request should + * respectively be escaped as "\%", "\_" and "\\". + * + * This matching must be case sensitive: The special case of PN VR + * is taken into consideration by normalizing the query string in + * method "NormalizeIdentifier()". + **/ + + class LookupIdentifierQuery : public boost::noncopyable + { + // This class encodes a conjunction ("AND") of disjunctions. Each + // disjunction represents an "OR" of several constraints. + + public: + class SingleConstraint + { + private: + DicomTag tag_; + IdentifierConstraintType type_; + std::string value_; + + public: + SingleConstraint(const DicomTag& tag, + IdentifierConstraintType type, + const std::string& value); + + const DicomTag& GetTag() const + { + return tag_; + } + + IdentifierConstraintType GetType() const + { + return type_; + } + + const std::string& GetValue() const + { + return value_; + } + }; + + + class RangeConstraint + { + private: + DicomTag tag_; + std::string start_; + std::string end_; + + public: + RangeConstraint(const DicomTag& tag, + const std::string& start, + const std::string& end); + + const DicomTag& GetTag() const + { + return tag_; + } + + const std::string& GetStart() const + { + return start_; + } + + const std::string& GetEnd() const + { + return end_; + } + }; + + + class Disjunction : public boost::noncopyable + { + private: + std::vector singleConstraints_; + std::vector rangeConstraints_; + + public: + ~Disjunction(); + + void Add(const DicomTag& tag, + IdentifierConstraintType type, + const std::string& value); + + void AddRange(const DicomTag& tag, + const std::string& start, + const std::string& end); + + size_t GetSingleConstraintsCount() const + { + return singleConstraints_.size(); + } + + const SingleConstraint& GetSingleConstraint(size_t i) const + { + return *singleConstraints_[i]; + } + + size_t GetRangeConstraintsCount() const + { + return rangeConstraints_.size(); + } + + const RangeConstraint& GetRangeConstraint(size_t i) const + { + return *rangeConstraints_[i]; + } + }; + + + private: + typedef std::vector Disjunctions; + + ResourceType level_; + Disjunctions disjunctions_; + + public: + LookupIdentifierQuery(ResourceType level) : level_(level) + { + } + + ~LookupIdentifierQuery(); + + bool IsIdentifier(const DicomTag& tag); + + void AddConstraint(DicomTag tag, + IdentifierConstraintType type, + const std::string& value); + + void AddRange(DicomTag tag, + const std::string& start, + const std::string& end); + + Disjunction& AddDisjunction(); + + ResourceType GetLevel() const + { + return level_; + } + + // The database must be locked + void Apply(std::list& result, + IDatabaseWrapper& database); + + void Apply(SetOfResources& result, + IDatabaseWrapper& database); + + void Print(std::ostream& s) const; + }; +} diff -r 54e422fe31ce -r 8fd203510d8b UnitTestsSources/ServerIndexTests.cpp --- a/UnitTestsSources/ServerIndexTests.cpp Wed Dec 19 14:20:11 2018 +0100 +++ b/UnitTestsSources/ServerIndexTests.cpp Wed Dec 19 16:27:07 2018 +0100 @@ -38,9 +38,7 @@ #include "../Core/FileStorage/MemoryStorageArea.h" #include "../Core/Logging.h" #include "../OrthancServer/SQLiteDatabaseWrapper.h" -#include "../OrthancServer/Search/LookupIdentifierQuery.h" #include "../OrthancServer/ServerContext.h" -#include "../OrthancServer/ServerIndex.h" #include "../OrthancServer/ServerToolbox.h" #include @@ -251,16 +249,42 @@ } - void DoLookup(std::list& result, - ResourceType level, - const DicomTag& tag, - const std::string& value) + void DoLookupIdentifier(std::vector& result, + ResourceType level, + const DicomTag& tag, + ConstraintType type, + const std::string& value) { - LookupIdentifierQuery query(level); - query.AddConstraint(tag, IdentifierConstraintType_Equal, value); - query.Apply(result, *index_); + assert(ServerToolbox::IsIdentifier(tag, level)); + + DicomTagConstraint c(tag, type, value, true, true); + + std::vector lookup; + lookup.push_back(DatabaseConstraint(c, level, DicomTagType_Identifier)); + + index_->ApplyLookupResources(result, NULL, lookup, level, 0 /* no limit */); } + + void DoLookupIdentifier2(std::vector& result, + ResourceType level, + const DicomTag& tag, + ConstraintType type1, + const std::string& value1, + ConstraintType type2, + const std::string& value2) + { + assert(ServerToolbox::IsIdentifier(tag, level)); + + DicomTagConstraint c1(tag, type1, value1, true, true); + DicomTagConstraint c2(tag, type2, value2, true, true); + + std::vector lookup; + lookup.push_back(DatabaseConstraint(c1, level, DicomTagType_Identifier)); + lookup.push_back(DatabaseConstraint(c2, level, DicomTagType_Identifier)); + + index_->ApplyLookupResources(result, NULL, lookup, level, 0 /* no limit */); + } }; } @@ -716,64 +740,48 @@ index_->SetIdentifierTag(a[2], DICOM_TAG_STUDY_INSTANCE_UID, "0"); index_->SetIdentifierTag(a[3], DICOM_TAG_SERIES_INSTANCE_UID, "0"); - std::list s; + std::vector s; - DoLookup(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, "0"); + DoLookupIdentifier(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, ConstraintType_Equal, "0"); ASSERT_EQ(2u, s.size()); ASSERT_TRUE(std::find(s.begin(), s.end(), "a") != s.end()); ASSERT_TRUE(std::find(s.begin(), s.end(), "c") != s.end()); - DoLookup(s, ResourceType_Series, DICOM_TAG_SERIES_INSTANCE_UID, "0"); + DoLookupIdentifier(s, ResourceType_Series, DICOM_TAG_SERIES_INSTANCE_UID, ConstraintType_Equal, "0"); ASSERT_EQ(1u, s.size()); ASSERT_TRUE(std::find(s.begin(), s.end(), "d") != s.end()); - DoLookup(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, "1"); + DoLookupIdentifier(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, ConstraintType_Equal, "1"); ASSERT_EQ(1u, s.size()); ASSERT_TRUE(std::find(s.begin(), s.end(), "b") != s.end()); - DoLookup(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, "1"); + DoLookupIdentifier(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, ConstraintType_Equal, "1"); ASSERT_EQ(1u, s.size()); ASSERT_TRUE(std::find(s.begin(), s.end(), "b") != s.end()); - DoLookup(s, ResourceType_Series, DICOM_TAG_SERIES_INSTANCE_UID, "1"); + DoLookupIdentifier(s, ResourceType_Series, DICOM_TAG_SERIES_INSTANCE_UID, ConstraintType_Equal, "1"); + ASSERT_EQ(0u, s.size()); + + DoLookupIdentifier(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, ConstraintType_GreaterOrEqual, "0"); + ASSERT_EQ(3u, s.size()); + + DoLookupIdentifier(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, ConstraintType_GreaterOrEqual, "1"); + ASSERT_EQ(1u, s.size()); + + DoLookupIdentifier(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, ConstraintType_GreaterOrEqual, "2"); ASSERT_EQ(0u, s.size()); - { - LookupIdentifierQuery query(ResourceType_Study); - query.AddConstraint(DICOM_TAG_STUDY_INSTANCE_UID, IdentifierConstraintType_GreaterOrEqual, "0"); - query.Apply(s, *index_); - ASSERT_EQ(3u, s.size()); - } - - { - LookupIdentifierQuery query(ResourceType_Study); - query.AddConstraint(DICOM_TAG_STUDY_INSTANCE_UID, IdentifierConstraintType_GreaterOrEqual, "0"); - query.AddConstraint(DICOM_TAG_STUDY_INSTANCE_UID, IdentifierConstraintType_SmallerOrEqual, "0"); - query.Apply(s, *index_); - ASSERT_EQ(2u, s.size()); - } + DoLookupIdentifier2(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, + ConstraintType_GreaterOrEqual, "0", ConstraintType_SmallerOrEqual, "0"); + ASSERT_EQ(2u, s.size()); - { - LookupIdentifierQuery query(ResourceType_Study); - query.AddConstraint(DICOM_TAG_STUDY_INSTANCE_UID, IdentifierConstraintType_GreaterOrEqual, "1"); - query.AddConstraint(DICOM_TAG_STUDY_INSTANCE_UID, IdentifierConstraintType_SmallerOrEqual, "1"); - query.Apply(s, *index_); - ASSERT_EQ(1u, s.size()); - } + DoLookupIdentifier2(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, + ConstraintType_GreaterOrEqual, "1", ConstraintType_SmallerOrEqual, "1"); + ASSERT_EQ(1u, s.size()); - { - LookupIdentifierQuery query(ResourceType_Study); - query.AddConstraint(DICOM_TAG_STUDY_INSTANCE_UID, IdentifierConstraintType_GreaterOrEqual, "1"); - query.Apply(s, *index_); - ASSERT_EQ(1u, s.size()); - } - - { - LookupIdentifierQuery query(ResourceType_Study); - query.AddConstraint(DICOM_TAG_STUDY_INSTANCE_UID, IdentifierConstraintType_GreaterOrEqual, "2"); - query.Apply(s, *index_); - ASSERT_EQ(0u, s.size()); - } + DoLookupIdentifier2(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, + ConstraintType_GreaterOrEqual, "0", ConstraintType_SmallerOrEqual, "1"); + ASSERT_EQ(3u, s.size()); } @@ -858,7 +866,7 @@ } -TEST(LookupIdentifierQuery, NormalizeIdentifier) +TEST(ServerIndex, NormalizeIdentifier) { ASSERT_EQ("H^L.LO", ServerToolbox::NormalizeIdentifier(" Hé^l.LO %_ ")); ASSERT_EQ("1.2.840.113619.2.176.2025", ServerToolbox::NormalizeIdentifier(" 1.2.840.113619.2.176.2025 "));