# HG changeset patch # User Sebastien Jodogne # Date 1530626357 -7200 # Node ID e583478e0c6cd906e0cfa7afc58da7dabf881c49 # Parent 1b736d151ea1d51bfdbc3c3858910534413a00da New primitive in database SDK: "lookupIdentifierRange" to speed up range searches diff -r 1b736d151ea1 -r e583478e0c6c NEWS --- a/NEWS Tue Jul 03 15:07:41 2018 +0200 +++ b/NEWS Tue Jul 03 15:59:17 2018 +0200 @@ -33,6 +33,11 @@ sending a POST request to the ".../reconstruct" URI. This change triggered an update of ORTHANC_API_VERSION from 1.0 to 1.1 +Plugins +------- + +* New primitive in database SDK: "lookupIdentifierRange" to speed up range searches + Maintenance ----------- diff -r 1b736d151ea1 -r e583478e0c6c OrthancServer/DatabaseWrapper.h --- a/OrthancServer/DatabaseWrapper.h Tue Jul 03 15:07:41 2018 +0200 +++ b/OrthancServer/DatabaseWrapper.h Tue Jul 03 15:59:17 2018 +0200 @@ -331,6 +331,17 @@ base_.LookupIdentifier(result, level, tag, type, value); } + + virtual void LookupIdentifierRange(std::list& result, + ResourceType level, + const DicomTag& tag, + const std::string& start, + const std::string& end) + { + base_.LookupIdentifierRange(result, level, tag, start, end); + } + + virtual void GetAllMetadata(std::map& target, int64_t id); diff -r 1b736d151ea1 -r e583478e0c6c OrthancServer/DatabaseWrapperBase.cpp --- a/OrthancServer/DatabaseWrapperBase.cpp Tue Jul 03 15:07:41 2018 +0200 +++ b/OrthancServer/DatabaseWrapperBase.cpp Tue Jul 03 15:59:17 2018 +0200 @@ -728,4 +728,30 @@ target.push_back(s->ColumnInt64(0)); } } + + + void DatabaseWrapperBase::LookupIdentifierRange(std::list& target, + ResourceType level, + const DicomTag& tag, + const std::string& start, + const std::string& end) + { + SQLite::Statement statement(db_, SQLITE_FROM_HERE, + "SELECT d.id FROM DicomIdentifiers AS d, Resources AS r WHERE " + "d.id = r.internalId AND r.resourceType=? AND " + "d.tagGroup=? AND d.tagElement=? AND d.value>=? AND d.value<=?"); + + statement.BindInt(0, level); + statement.BindInt(1, tag.GetGroup()); + statement.BindInt(2, tag.GetElement()); + statement.BindString(3, start); + statement.BindString(4, end); + + target.clear(); + + while (statement.Step()) + { + target.push_back(statement.ColumnInt64(0)); + } + } } diff -r 1b736d151ea1 -r e583478e0c6c OrthancServer/DatabaseWrapperBase.h --- a/OrthancServer/DatabaseWrapperBase.h Tue Jul 03 15:07:41 2018 +0200 +++ b/OrthancServer/DatabaseWrapperBase.h Tue Jul 03 15:59:17 2018 +0200 @@ -199,6 +199,12 @@ const DicomTag& tag, IdentifierConstraintType type, const std::string& value); + + void LookupIdentifierRange(std::list& result, + ResourceType level, + const DicomTag& tag, + const std::string& start, + const std::string& end); }; } diff -r 1b736d151ea1 -r e583478e0c6c OrthancServer/IDatabaseWrapper.h --- a/OrthancServer/IDatabaseWrapper.h Tue Jul 03 15:07:41 2018 +0200 +++ b/OrthancServer/IDatabaseWrapper.h Tue Jul 03 15:59:17 2018 +0200 @@ -156,6 +156,12 @@ IdentifierConstraintType type, const std::string& value) = 0; + virtual void LookupIdentifierRange(std::list& result, + ResourceType level, + const DicomTag& tag, + const std::string& start, + const std::string& end) = 0; + virtual bool LookupMetadata(std::string& target, int64_t id, MetadataType type) = 0; diff -r 1b736d151ea1 -r e583478e0c6c OrthancServer/Search/LookupIdentifierQuery.cpp --- a/OrthancServer/Search/LookupIdentifierQuery.cpp Tue Jul 03 15:07:41 2018 +0200 +++ b/OrthancServer/Search/LookupIdentifierQuery.cpp Tue Jul 03 15:59:17 2018 +0200 @@ -46,9 +46,14 @@ { LookupIdentifierQuery::Disjunction::~Disjunction() { - for (size_t i = 0; i < constraints_.size(); i++) + for (size_t i = 0; i < singleConstraints_.size(); i++) { - delete constraints_[i]; + delete singleConstraints_[i]; + } + + for (size_t i = 0; i < rangeConstraints_.size(); i++) + { + delete rangeConstraints_[i]; } } @@ -57,14 +62,22 @@ IdentifierConstraintType type, const std::string& value) { - constraints_.push_back(new Constraint(tag, type, 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 (Disjuntions::iterator it = disjuntions_.begin(); - it != disjuntions_.end(); ++it) + for (Disjunctions::iterator it = disjunctions_.begin(); + it != disjunctions_.end(); ++it) { delete *it; } @@ -76,15 +89,25 @@ const std::string& value) { assert(IsIdentifier(tag)); - disjuntions_.push_back(new Disjunction); - disjuntions_.back()->Add(tag, type, value); + 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() { - disjuntions_.push_back(new Disjunction); - return *disjuntions_.back(); + disjunctions_.push_back(new Disjunction); + return *disjunctions_.back(); } @@ -101,15 +124,26 @@ void LookupIdentifierQuery::Apply(SetOfResources& result, IDatabaseWrapper& database) { - for (size_t i = 0; i < GetSize(); i++) + for (size_t i = 0; i < disjunctions_.size(); i++) { std::list a; - for (size_t j = 0; j < disjuntions_[i]->GetSize(); j++) + for (size_t j = 0; j < disjunctions_[i]->GetSingleConstraintsCount(); j++) { - const Constraint& constraint = disjuntions_[i]->GetConstraint(j); + const SingleConstraint& constraint = disjunctions_[i]->GetSingleConstraint(j); std::list b; - database.LookupIdentifier(b, level_, constraint.GetTag(), constraint.GetType(), constraint.GetValue()); + 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); } @@ -122,17 +156,17 @@ void LookupIdentifierQuery::Print(std::ostream& s) const { s << "Constraint: " << std::endl; - for (Disjuntions::const_iterator - it = disjuntions_.begin(); it != disjuntions_.end(); ++it) + for (Disjunctions::const_iterator + it = disjunctions_.begin(); it != disjunctions_.end(); ++it) { - if (it == disjuntions_.begin()) + if (it == disjunctions_.begin()) s << " "; else s << "OR "; - for (size_t j = 0; j < (*it)->GetSize(); j++) + for (size_t j = 0; j < (*it)->GetSingleConstraintsCount(); j++) { - const Constraint& c = (*it)->GetConstraint(j); + const SingleConstraint& c = (*it)->GetSingleConstraint(j); s << FromDcmtkBridge::GetTagName(c.GetTag(), ""); switch (c.GetType()) diff -r 1b736d151ea1 -r e583478e0c6c OrthancServer/Search/LookupIdentifierQuery.h --- a/OrthancServer/Search/LookupIdentifierQuery.h Tue Jul 03 15:07:41 2018 +0200 +++ b/OrthancServer/Search/LookupIdentifierQuery.h Tue Jul 03 15:59:17 2018 +0200 @@ -65,8 +65,11 @@ class LookupIdentifierQuery : public boost::noncopyable { + // This class encodes a conjunction ("AND") of disjunctions. Each + // disjunction represents an "OR" of several constraints. + public: - class Constraint + class SingleConstraint { private: DicomTag tag_; @@ -74,9 +77,9 @@ std::string value_; public: - Constraint(const DicomTag& tag, - IdentifierConstraintType type, - const std::string& value) : + SingleConstraint(const DicomTag& tag, + IdentifierConstraintType type, + const std::string& value) : tag_(tag), type_(type), value_(ServerToolbox::NormalizeIdentifier(value)) @@ -100,10 +103,45 @@ }; + class RangeConstraint + { + private: + DicomTag tag_; + std::string start_; + std::string end_; + + public: + RangeConstraint(const DicomTag& tag, + const std::string& start, + const std::string& end) : + tag_(tag), + start_(ServerToolbox::NormalizeIdentifier(start)), + end_(ServerToolbox::NormalizeIdentifier(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 constraints_; + std::vector singleConstraints_; + std::vector rangeConstraints_; public: ~Disjunction(); @@ -112,23 +150,37 @@ IdentifierConstraintType type, const std::string& value); - size_t GetSize() const + void AddRange(const DicomTag& tag, + const std::string& start, + const std::string& end); + + size_t GetSingleConstraintsCount() const { - return constraints_.size(); + return singleConstraints_.size(); } - const Constraint& GetConstraint(size_t i) const + const SingleConstraint& GetSingleConstraint(size_t i) const + { + return *singleConstraints_[i]; + } + + size_t GetRangeConstraintsCount() const { - return *constraints_[i]; + return rangeConstraints_.size(); + } + + const RangeConstraint& GetRangeConstraint(size_t i) const + { + return *rangeConstraints_[i]; } }; private: - typedef std::vector Disjuntions; + typedef std::vector Disjunctions; ResourceType level_; - Disjuntions disjuntions_; + Disjunctions disjunctions_; public: LookupIdentifierQuery(ResourceType level) : level_(level) @@ -146,6 +198,10 @@ IdentifierConstraintType type, const std::string& value); + void AddRange(DicomTag tag, + const std::string& start, + const std::string& end); + Disjunction& AddDisjunction(); ResourceType GetLevel() const @@ -153,11 +209,6 @@ return level_; } - size_t GetSize() const - { - return disjuntions_.size(); - } - // The database must be locked void Apply(std::list& result, IDatabaseWrapper& database); diff -r 1b736d151ea1 -r e583478e0c6c OrthancServer/Search/RangeConstraint.cpp --- a/OrthancServer/Search/RangeConstraint.cpp Tue Jul 03 15:07:41 2018 +0200 +++ b/OrthancServer/Search/RangeConstraint.cpp Tue Jul 03 15:59:17 2018 +0200 @@ -59,14 +59,22 @@ void RangeConstraint::Setup(LookupIdentifierQuery& lookup, const DicomTag& tag) const { - if (!lower_.empty()) + if (!lower_.empty() && + !upper_.empty()) { - lookup.AddConstraint(tag, IdentifierConstraintType_GreaterOrEqual, lower_); + lookup.AddRange(tag, lower_, upper_); } + else + { + if (!lower_.empty()) + { + lookup.AddConstraint(tag, IdentifierConstraintType_GreaterOrEqual, lower_); + } - if (!upper_.empty()) - { - lookup.AddConstraint(tag, IdentifierConstraintType_SmallerOrEqual, upper_); + if (!upper_.empty()) + { + lookup.AddConstraint(tag, IdentifierConstraintType_SmallerOrEqual, upper_); + } } } diff -r 1b736d151ea1 -r e583478e0c6c Plugins/Engine/OrthancPluginDatabase.cpp --- a/Plugins/Engine/OrthancPluginDatabase.cpp Tue Jul 03 15:07:41 2018 +0200 +++ b/Plugins/Engine/OrthancPluginDatabase.cpp Tue Jul 03 15:59:17 2018 +0200 @@ -634,6 +634,34 @@ } + void OrthancPluginDatabase::LookupIdentifierRange(std::list& result, + ResourceType level, + const DicomTag& tag, + const std::string& start, + const std::string& end) + { + if (extensions_.lookupIdentifierRange == NULL) + { + // Default implementation, for plugins using Orthanc SDK <= 1.3.2 + + LookupIdentifier(result, level, tag, IdentifierConstraintType_GreaterOrEqual, start); + + std::list b; + LookupIdentifier(result, level, tag, IdentifierConstraintType_SmallerOrEqual, end); + + result.splice(result.end(), b); + } + else + { + ResetAnswers(); + CheckSuccess(extensions_.lookupIdentifierRange(GetContext(), payload_, Plugins::Convert(level), + tag.GetGroup(), tag.GetElement(), + start.c_str(), end.c_str())); + ForwardAnswers(result); + } + } + + bool OrthancPluginDatabase::LookupMetadata(std::string& target, int64_t id, MetadataType type) diff -r 1b736d151ea1 -r e583478e0c6c Plugins/Engine/OrthancPluginDatabase.h --- a/Plugins/Engine/OrthancPluginDatabase.h Tue Jul 03 15:07:41 2018 +0200 +++ b/Plugins/Engine/OrthancPluginDatabase.h Tue Jul 03 15:59:17 2018 +0200 @@ -213,6 +213,12 @@ IdentifierConstraintType type, const std::string& value); + virtual void LookupIdentifierRange(std::list& result, + ResourceType level, + const DicomTag& tag, + const std::string& start, + const std::string& end); + virtual bool LookupMetadata(std::string& target, int64_t id, MetadataType type); diff -r 1b736d151ea1 -r e583478e0c6c Plugins/Include/orthanc/OrthancCDatabasePlugin.h --- a/Plugins/Include/orthanc/OrthancCDatabasePlugin.h Tue Jul 03 15:07:41 2018 +0200 +++ b/Plugins/Include/orthanc/OrthancCDatabasePlugin.h Tue Jul 03 15:59:17 2018 +0200 @@ -682,6 +682,18 @@ OrthancPluginResourceType resourceType, const OrthancPluginDicomTag* tag, OrthancPluginIdentifierConstraint constraint); + + /* Output: Use OrthancPluginDatabaseAnswerInt64() */ + OrthancPluginErrorCode (*lookupIdentifierRange) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + OrthancPluginResourceType resourceType, + uint16_t group, + uint16_t element, + const char* start, + const char* end); } OrthancPluginDatabaseExtensions; /*