diff OrthancServer/Sources/ResourceFinder.cpp @ 5688:d0a264b803f1 find-refactoring

first implementation of database paging
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 09 Jul 2024 15:36:28 +0200
parents 11575590e493
children c14776d25491
line wrap: on
line diff
--- a/OrthancServer/Sources/ResourceFinder.cpp	Tue Jul 09 12:51:46 2024 +0200
+++ b/OrthancServer/Sources/ResourceFinder.cpp	Tue Jul 09 15:36:28 2024 +0200
@@ -422,15 +422,49 @@
   }
 
 
+  void ResourceFinder::UpdateRequestLimits()
+  {
+    // TODO-FIND: Check this
+
+    if (lookup_.get() == NULL ||
+        lookup_->HasOnlyMainDicomTags())
+    {
+      isDatabasePaging_ = true;
+
+      if (hasLimits_)
+      {
+        request_.SetLimits(limitsSince_, limitsCount_);
+      }
+    }
+    else if (databaseLimits_ != 0)
+    {
+      // The "+ 1" below is used to test the completeness of the response
+      request_.SetLimits(0, databaseLimits_ + 1);
+      isDatabasePaging_ = false;
+    }
+    else
+    {
+      isDatabasePaging_ = false;
+    }
+  }
+
+
   ResourceFinder::ResourceFinder(ResourceType level,
                                  bool expand) :
     request_(level),
+    databaseLimits_(0),
+    isDatabasePaging_(true),
+    hasLimits_(false),
+    limitsSince_(0),
+    limitsCount_(0),
     expand_(expand),
     format_(DicomToJsonFormat_Human),
     allowStorageAccess_(true),
     hasRequestedTags_(false),
     includeAllMetadata_(false)
   {
+    UpdateRequestLimits();
+
     if (expand)
     {
       request_.SetRetrieveMainDicomTags(true);
@@ -466,9 +500,34 @@
   }
 
 
+  void ResourceFinder::SetDatabaseLimits(uint64_t limits)
+  {
+    databaseLimits_ = limits;
+    UpdateRequestLimits();
+  }
+
+
+  void ResourceFinder::SetLimits(uint64_t since,
+                                 uint64_t count)
+  {
+    if (hasLimits_)
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      hasLimits_ = true;
+      limitsSince_ = since;
+      limitsCount_ = count;
+      UpdateRequestLimits();
+    }
+  }
+
+
   void ResourceFinder::SetDatabaseLookup(const DatabaseLookup& lookup)
   {
     lookup_.reset(lookup.Clone());
+    UpdateRequestLimits();
 
     for (size_t i = 0; i < lookup.GetConstraintsCount(); i++)
     {
@@ -748,13 +807,31 @@
   }
 
   
-  void ResourceFinder::Execute(Json::Value& target,
+  void ResourceFinder::Execute(IVisitor& visitor,
                                ServerContext& context) const
   {
     FindResponse response;
     context.GetIndex().ExecuteFind(response, request_);
 
-    target = Json::arrayValue;
+    bool complete;
+    if (isDatabasePaging_)
+    {
+      complete = true;
+    }
+    else
+    {
+      complete = (databaseLimits_ == 0 ||
+                  response.GetSize() <= databaseLimits_);
+    }
+
+    if (lookup_.get() != NULL &&
+        !lookup_->HasOnlyMainDicomTags())
+    {
+      LOG(INFO) << "Number of candidate resources after fast DB filtering on main DICOM tags: " << response.GetSize();
+    }
+
+    size_t countResults = 0;
+    size_t skipped = 0;
 
     for (size_t i = 0; i < response.GetSize(); i++)
     {
@@ -798,26 +875,93 @@
 
       if (match)
       {
-        if (expand_)
+        if (isDatabasePaging_)
+        {
+          visitor.Apply(resource, hasRequestedTags_, requestedTags);
+        }
+        else
+        {
+          if (hasLimits_ &&
+              skipped < limitsSince_)
+          {
+            skipped++;
+          }
+          else if (hasLimits_ &&
+                   countResults >= limitsCount_)
+          {
+            // Too many results, don't mark as complete
+            complete = false;
+            break;
+          }
+          else
+          {
+            visitor.Apply(resource, hasRequestedTags_, requestedTags);
+            countResults++;
+          }
+        }
+      }
+    }
+
+    if (complete)
+    {
+      visitor.MarkAsComplete();
+    }
+  }
+
+
+  void ResourceFinder::Execute(Json::Value& target,
+                               ServerContext& context) const
+  {
+    class Visitor : public IVisitor
+    {
+    private:
+      const ResourceFinder&  that_;
+      ServerIndex& index_;
+      Json::Value& target_;
+
+    public:
+      Visitor(const ResourceFinder& that,
+              ServerIndex& index,
+              Json::Value& target) :
+        that_(that),
+        index_(index),
+        target_(target)
+      {
+      }
+
+      virtual void Apply(const FindResponse::Resource& resource,
+                         bool hasRequestedTags,
+                         const DicomMap& requestedTags) ORTHANC_OVERRIDE
+      {
+        if (that_.expand_)
         {
           Json::Value item;
-          Expand(item, resource, context.GetIndex());
+          that_.Expand(item, resource, index_);
 
-          if (hasRequestedTags_)
+          if (hasRequestedTags)
           {
             static const char* const REQUESTED_TAGS = "RequestedTags";
             item[REQUESTED_TAGS] = Json::objectValue;
-            FromDcmtkBridge::ToJson(item[REQUESTED_TAGS], requestedTags, format_);
+            FromDcmtkBridge::ToJson(item[REQUESTED_TAGS], requestedTags, that_.format_);
           }
 
-          target.append(item);
+          target_.append(item);
         }
         else
         {
-          target.append(resource.GetIdentifier());
+          target_.append(resource.GetIdentifier());
         }
       }
-    }
+
+      virtual void MarkAsComplete() ORTHANC_OVERRIDE
+      {
+      }
+    };
+
+    target = Json::arrayValue;
+
+    Visitor visitor(*this, context.GetIndex(), target);
+    Execute(visitor, context);
   }