changeset 2697:e583478e0c6c jobs

New primitive in database SDK: "lookupIdentifierRange" to speed up range searches
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 03 Jul 2018 15:59:17 +0200
parents 1b736d151ea1
children 5fbfe55e9978
files NEWS OrthancServer/DatabaseWrapper.h OrthancServer/DatabaseWrapperBase.cpp OrthancServer/DatabaseWrapperBase.h OrthancServer/IDatabaseWrapper.h OrthancServer/Search/LookupIdentifierQuery.cpp OrthancServer/Search/LookupIdentifierQuery.h OrthancServer/Search/RangeConstraint.cpp Plugins/Engine/OrthancPluginDatabase.cpp Plugins/Engine/OrthancPluginDatabase.h Plugins/Include/orthanc/OrthancCDatabasePlugin.h
diffstat 11 files changed, 232 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- 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
 -----------
 
--- 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<int64_t>& 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<MetadataType, std::string>& target,
                                 int64_t id);
 
--- 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<int64_t>& 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));
+    }    
+  }
 }
--- 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<int64_t>& result,
+                               ResourceType level,
+                               const DicomTag& tag,
+                               const std::string& start,
+                               const std::string& end);
   };
 }
 
--- 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<int64_t>& 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;
--- 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<int64_t> 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<int64_t> 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<int64_t> 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())
--- 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<Constraint*>  constraints_;
+      std::vector<SingleConstraint*>  singleConstraints_;
+      std::vector<RangeConstraint*>   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<Disjunction*>  Disjuntions;
+    typedef std::vector<Disjunction*>  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<std::string>& result,
                IDatabaseWrapper& database);
--- 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_);
+      }
     }
   }
 
--- 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<int64_t>& 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<int64_t> 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)
--- 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<int64_t>& 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);
--- 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;
 
 /*<! @endcond */