changeset 5772:093a8693ba16 find-refactoring

replaced SetRetrieveOneInstanceIdentifier() by SetRetrieveOneInstanceMetadataAndAttachments()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 11 Sep 2024 20:49:34 +0200
parents 4db9f897df58
children 3b7dce0e43c6
files OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto OrthancServer/Sources/Database/Compatibility/GenericFind.cpp OrthancServer/Sources/Database/FindRequest.cpp OrthancServer/Sources/Database/FindRequest.h OrthancServer/Sources/Database/FindResponse.cpp OrthancServer/Sources/Database/FindResponse.h OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp OrthancServer/Sources/Database/SQLiteDatabaseWrapper.h OrthancServer/Sources/ResourceFinder.cpp OrthancServer/Sources/ResourceFinder.h OrthancServer/Sources/ServerContext.cpp OrthancServer/Sources/ServerContext.h
diffstat 13 files changed, 324 insertions(+), 76 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp	Wed Sep 11 16:31:11 2024 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp	Wed Sep 11 20:49:34 2024 +0200
@@ -1437,7 +1437,7 @@
         dbRequest.mutable_find()->set_retrieve_labels(request.IsRetrieveLabels());
         dbRequest.mutable_find()->set_retrieve_attachments(request.IsRetrieveAttachments());
         dbRequest.mutable_find()->set_retrieve_parent_identifier(request.IsRetrieveParentIdentifier());
-        dbRequest.mutable_find()->set_retrieve_at_least_one_instance(request.IsRetrieveOneInstanceIdentifier());
+        dbRequest.mutable_find()->set_retrieve_one_instance_metadata_and_attachments(request.IsRetrieveOneInstanceMetadataAndAttachments());
 
         if (request.GetLevel() == ResourceType_Study ||
             request.GetLevel() == ResourceType_Series ||
@@ -1541,6 +1541,40 @@
             Convert(*target, ResourceType_Series, source.children_instances_content());
           }
 
+          if (request.IsRetrieveOneInstanceMetadataAndAttachments())
+          {
+            std::map<MetadataType, std::string> metadata;
+            for (int i = 0; i < source.one_instance_metadata().size(); i++)
+            {
+              MetadataType key = static_cast<MetadataType>(source.one_instance_metadata(i).key());
+              if (metadata.find(key) == metadata.end())
+              {
+                metadata[key] = source.one_instance_metadata(i).value();
+              }
+              else
+              {
+                throw OrthancException(ErrorCode_DatabasePlugin);
+              }
+            }
+
+            std::map<FileContentType, FileInfo> attachments;
+
+            for (int i = 0; i < source.one_instance_attachments().size(); i++)
+            {
+              FileInfo info(Convert(source.attachments(i)));
+              if (attachments.find(info.GetContentType()) == attachments.end())
+              {
+                attachments[info.GetContentType()] = info;
+              }
+              else
+              {
+                throw OrthancException(ErrorCode_DatabasePlugin);
+              }
+            }
+
+            target->SetOneInstanceMetadataAndAttachments(source.one_instance_public_id(), metadata, attachments);
+          }
+
           response.Add(target.release());
         }
 
--- a/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto	Wed Sep 11 16:31:11 2024 +0200
+++ b/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto	Wed Sep 11 20:49:34 2024 +0200
@@ -881,7 +881,7 @@
     bool retrieve_labels = 102;
     bool retrieve_attachments = 103;
     bool retrieve_parent_identifier = 104;
-    bool retrieve_at_least_one_instance = 105;
+    bool retrieve_one_instance_metadata_and_attachments = 105;
     ParentSpecification parent_patient = 106;
     ParentSpecification parent_study = 107;
     ParentSpecification parent_series = 108;
@@ -931,6 +931,9 @@
     ChildrenContent children_studies_content = 10;
     ChildrenContent children_series_content = 11;
     ChildrenContent children_instances_content = 12;
+    string one_instance_public_id = 13;
+    repeated Metadata one_instance_metadata = 14;
+    repeated FileInfo one_instance_attachments = 15;
   }
 }
 
--- a/OrthancServer/Sources/Database/Compatibility/GenericFind.cpp	Wed Sep 11 16:31:11 2024 +0200
+++ b/OrthancServer/Sources/Database/Compatibility/GenericFind.cpp	Wed Sep 11 20:49:34 2024 +0200
@@ -566,8 +566,8 @@
         }
       }
 
-      if (request.IsRetrieveOneInstanceIdentifier() &&
-          !request.GetChildrenSpecification(ResourceType_Instance).IsRetrieveIdentifiers())
+      if (request.GetLevel() != ResourceType_Instance &&
+          request.IsRetrieveOneInstanceMetadataAndAttachments())
       {
         int64_t currentId = internalId;
         ResourceType currentLevel = level;
@@ -587,7 +587,28 @@
           }
         }
 
-        resource->AddChildIdentifier(ResourceType_Instance, transaction_.GetPublicId(currentId));
+        std::map<MetadataType, std::string> metadata;
+        transaction_.GetAllMetadata(metadata, currentId);
+
+        std::set<FileContentType> attachmentsType;
+        transaction_.ListAvailableAttachments(attachmentsType, currentId);
+
+        std::map<FileContentType, FileInfo> attachments;
+        for (std::set<FileContentType>::const_iterator it = attachmentsType.begin(); it != attachmentsType.end(); ++it)
+        {
+          FileInfo info;
+          int64_t revision;  // Unused in this case
+          if (transaction_.LookupAttachment(info, revision, currentId, *it))
+          {
+            attachments[*it] = info;
+          }
+          else
+          {
+            throw OrthancException(ErrorCode_DatabasePlugin);
+          }
+        }
+
+        resource->SetOneInstanceMetadataAndAttachments(transaction_.GetPublicId(currentId), metadata, attachments);
       }
 
       response.Add(resource.release());
--- a/OrthancServer/Sources/Database/FindRequest.cpp	Wed Sep 11 16:31:11 2024 +0200
+++ b/OrthancServer/Sources/Database/FindRequest.cpp	Wed Sep 11 20:49:34 2024 +0200
@@ -93,7 +93,7 @@
     retrieveLabels_(false),
     retrieveAttachments_(false),
     retrieveParentIdentifier_(false),
-    retrieveOneInstanceIdentifier_(false)
+    retrieveOneInstanceMetadataAndAttachments_(false)
   {
   }
 
@@ -246,7 +246,7 @@
   }
 
 
-  void FindRequest::SetRetrieveOneInstanceIdentifier(bool retrieve)
+  void FindRequest::SetRetrieveOneInstanceMetadataAndAttachments(bool retrieve)
   {
     if (level_ == ResourceType_Instance)
     {
@@ -254,7 +254,20 @@
     }
     else
     {
-      retrieveOneInstanceIdentifier_ = retrieve;
+      retrieveOneInstanceMetadataAndAttachments_ = retrieve;
+    }
+  }
+
+
+  bool FindRequest::IsRetrieveOneInstanceMetadataAndAttachments() const
+  {
+    if (level_ == ResourceType_Instance)
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      return retrieveOneInstanceMetadataAndAttachments_;
     }
   }
 }
--- a/OrthancServer/Sources/Database/FindRequest.h	Wed Sep 11 16:31:11 2024 +0200
+++ b/OrthancServer/Sources/Database/FindRequest.h	Wed Sep 11 20:49:34 2024 +0200
@@ -254,7 +254,7 @@
     ChildrenSpecification                retrieveChildrenStudies_;
     ChildrenSpecification                retrieveChildrenSeries_;
     ChildrenSpecification                retrieveChildrenInstances_;
-    bool                                 retrieveOneInstanceIdentifier_;
+    bool                                 retrieveOneInstanceMetadataAndAttachments_;
 
     std::unique_ptr<MainDicomTagsRegistry>  mainDicomTagsRegistry_;
 
@@ -413,13 +413,8 @@
       return const_cast<FindRequest&>(*this).GetChildrenSpecification(level);
     }
 
-    void SetRetrieveOneInstanceIdentifier(bool retrieve);
+    void SetRetrieveOneInstanceMetadataAndAttachments(bool retrieve);
 
-    bool IsRetrieveOneInstanceIdentifier() const
-    {
-      return (retrieveOneInstanceIdentifier_ ||
-              (level_ != ResourceType_Instance &&
-               GetChildrenSpecification(ResourceType_Instance).IsRetrieveIdentifiers()));
-    }
+    bool IsRetrieveOneInstanceMetadataAndAttachments() const;
   };
 }
--- a/OrthancServer/Sources/Database/FindResponse.cpp	Wed Sep 11 16:31:11 2024 +0200
+++ b/OrthancServer/Sources/Database/FindResponse.cpp	Wed Sep 11 20:49:34 2024 +0200
@@ -483,17 +483,63 @@
   }
 
 
-  const std::string& FindResponse::Resource::GetOneInstanceIdentifier() const
+  void FindResponse::Resource::SetOneInstanceMetadataAndAttachments(const std::string& instancePublicId,
+                                                                    const std::map<MetadataType, std::string>& metadata,
+                                                                    const std::map<FileContentType, FileInfo>& attachments)
   {
-    const std::set<std::string>& instances = GetChildrenInformation(ResourceType_Instance).GetIdentifiers();
-
-    if (instances.size() == 0)
+    if (hasOneInstanceMetadataAndAttachments_)
     {
-      throw OrthancException(ErrorCode_BadSequenceOfCalls);  // HasOneInstanceIdentifier() should have been called
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+    else if (instancePublicId.empty())
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
     else
     {
-      return *instances.begin();
+      hasOneInstanceMetadataAndAttachments_ = true;
+      oneInstancePublicId_ = instancePublicId;
+      oneInstanceMetadata_ = metadata;
+      oneInstanceAttachments_ = attachments;
+    }
+  }
+
+
+  const std::string& FindResponse::Resource::GetOneInstancePublicId() const
+  {
+    if (hasOneInstanceMetadataAndAttachments_)
+    {
+      return oneInstancePublicId_;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+  }
+
+
+  const std::map<MetadataType, std::string>& FindResponse::Resource::GetOneInstanceMetadata() const
+  {
+    if (hasOneInstanceMetadataAndAttachments_)
+    {
+      return oneInstanceMetadata_;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+  }
+
+
+  const std::map<FileContentType, FileInfo>& FindResponse::Resource::GetOneInstanceAttachments() const
+  {
+    if (hasOneInstanceMetadataAndAttachments_)
+    {
+      return oneInstanceAttachments_;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
     }
   }
 
@@ -542,6 +588,25 @@
   }
 
 
+  static void DebugAttachments(Json::Value& target,
+                               const std::map<FileContentType, FileInfo>& attachments)
+  {
+    Json::Value v = Json::objectValue;
+    for (std::map<FileContentType, FileInfo>::const_iterator it = attachments.begin();
+         it != attachments.end(); ++it)
+    {
+      if (it->first != it->second.GetContentType())
+      {
+        throw OrthancException(ErrorCode_DatabasePlugin);
+      }
+      else
+      {
+        DebugAddAttachment(v, it->second);
+      }
+    }
+  }
+
+
   static void DebugSetOfStrings(Json::Value& target,
                                 const std::set<std::string>& values)
   {
@@ -633,25 +698,13 @@
 
     if (request.IsRetrieveAttachments())
     {
-      Json::Value v = Json::objectValue;
-      for (std::map<FileContentType, FileInfo>::const_iterator it = attachments_.begin();
-           it != attachments_.end(); ++it)
-      {
-        if (it->first != it->second.GetContentType())
-        {
-          throw OrthancException(ErrorCode_DatabasePlugin);
-        }
-        else
-        {
-          DebugAddAttachment(v, it->second);
-        }
-      }
-      target["Attachments"] = v;
+      DebugAttachments(target["Attachments"], attachments_);
     }
 
-    if (request.IsRetrieveOneInstanceIdentifier())
+    if (request.IsRetrieveOneInstanceMetadataAndAttachments())
     {
-      target["OneInstance"] = GetOneInstanceIdentifier();
+      DebugMetadata(target["OneInstance"]["Metadata"], GetOneInstanceMetadata());
+      DebugAttachments(target["OneInstance"]["Attachments"], GetOneInstanceAttachments());
     }
   }
 
--- a/OrthancServer/Sources/Database/FindResponse.h	Wed Sep 11 16:31:11 2024 +0200
+++ b/OrthancServer/Sources/Database/FindResponse.h	Wed Sep 11 20:49:34 2024 +0200
@@ -123,6 +123,10 @@
       ChildrenInformation                   childrenInstancesInformation_;
       std::set<std::string>                 labels_;
       std::map<FileContentType, FileInfo>   attachments_;
+      bool                                  hasOneInstanceMetadataAndAttachments_;
+      std::string                           oneInstancePublicId_;
+      std::map<MetadataType, std::string>   oneInstanceMetadata_;
+      std::map<FileContentType, FileInfo>   oneInstanceAttachments_;
 
       MainDicomTagsAtLevel& GetMainDicomTagsAtLevel(ResourceType level);
 
@@ -144,7 +148,8 @@
                const std::string& identifier) :
         level_(level),
         internalId_(internalId),
-        identifier_(identifier)
+        identifier_(identifier),
+        hasOneInstanceMetadataAndAttachments_(false)
       {
       }
 
@@ -268,13 +273,21 @@
         return attachments_;
       }
 
-      const std::string& GetOneInstanceIdentifier() const;
+      void SetOneInstanceMetadataAndAttachments(const std::string& instancePublicId,
+                                                const std::map<MetadataType, std::string>& metadata,
+                                                const std::map<FileContentType, FileInfo>& attachments);
 
-      bool HasOneInstanceIdentifier() const
+      bool HasOneInstanceMetadataAndAttachments() const
       {
-        return !GetChildrenIdentifiers(ResourceType_Instance).empty();
+        return hasOneInstanceMetadataAndAttachments_;
       }
 
+      const std::string& GetOneInstancePublicId() const;
+
+      const std::map<MetadataType, std::string>& GetOneInstanceMetadata() const;
+
+      const std::map<FileContentType, FileInfo>& GetOneInstanceAttachments() const;
+
       void DebugExport(Json::Value& target,
                        const FindRequest& request) const;
     };
--- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Wed Sep 11 16:31:11 2024 +0200
+++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Wed Sep 11 20:49:34 2024 +0200
@@ -588,9 +588,12 @@
         }
       }
 
-      // need one instance identifier ?  TODO: it might be actually more interesting to retrieve directly the attachment ids ....
-      if (request.IsRetrieveOneInstanceIdentifier())
+      if (request.IsRetrieveOneInstanceMetadataAndAttachments())
       {
+        throw OrthancException(ErrorCode_NotImplemented);
+
+#if 0
+        // need one instance identifier ?  TODO: it might be actually more interesting to retrieve directly the attachment ids ....
         if (requestLevel == ResourceType_Series)
         {
           sql = "SELECT Lookup.internalId, childLevel.publicId "
@@ -637,6 +640,7 @@
         {
           throw OrthancException(ErrorCode_InternalError);
         }
+#endif
       }
 
       // need children metadata ?
--- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.h	Wed Sep 11 16:31:11 2024 +0200
+++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.h	Wed Sep 11 20:49:34 2024 +0200
@@ -102,7 +102,8 @@
 
     virtual bool HasIntegratedFind() const ORTHANC_OVERRIDE
     {
-      return true;
+      //return true;   // => This uses optimized SQL commands
+      return false;   // => This uses Compatibility/GenericFind
     }
 
     /**
--- a/OrthancServer/Sources/ResourceFinder.cpp	Wed Sep 11 16:31:11 2024 +0200
+++ b/OrthancServer/Sources/ResourceFinder.cpp	Wed Sep 11 20:49:34 2024 +0200
@@ -661,7 +661,7 @@
         LOG(WARNING) << "Requested tag " << tag.Format()
                      << " should only be read at the study, series, or instance level";
         requestedTagsFromFileStorage_.insert(tag);
-        request_.SetRetrieveOneInstanceIdentifier(true);
+        request_.SetRetrieveOneInstanceMetadataAndAttachments(true);
       }
       else
       {
@@ -687,7 +687,7 @@
         LOG(WARNING) << "Requested tag " << tag.Format()
                      << " should only be read at the series or instance level";
         requestedTagsFromFileStorage_.insert(tag);
-        request_.SetRetrieveOneInstanceIdentifier(true);
+        request_.SetRetrieveOneInstanceMetadataAndAttachments(true);
       }
       else
       {
@@ -714,7 +714,7 @@
         LOG(WARNING) << "Requested tag " << tag.Format()
                      << " should only be read at the instance level";
         requestedTagsFromFileStorage_.insert(tag);
-        request_.SetRetrieveOneInstanceIdentifier(true);
+        request_.SetRetrieveOneInstanceMetadataAndAttachments(true);
       }
       else
       {
@@ -780,7 +780,7 @@
 
       if (request_.GetLevel() != ResourceType_Instance)
       {
-        request_.SetRetrieveOneInstanceIdentifier(true);
+        request_.SetRetrieveOneInstanceMetadataAndAttachments(true);
       }
 
       hasRequestedTags_ = true;
@@ -843,44 +843,71 @@
                    << ": " << missings;
     }
 
-    std::string instancePublicId;
+    // TODO-FIND: What do we do if the DICOM has been removed since the request?
+    // Do we fail, or do we skip the resource?
 
-    if (request.IsRetrieveOneInstanceIdentifier())
+    Json::Value tmpDicomAsJson;
+
+    if (request.GetLevel() == ResourceType_Instance &&
+        request.IsRetrieveMetadata() &&
+        request.IsRetrieveAttachments())
     {
-      instancePublicId = resource.GetOneInstanceIdentifier();
+      LOG(INFO) << "Will retrieve missing DICOM tags from instance: " << resource.GetIdentifier();
+
+      context.ReadDicomAsJson(tmpDicomAsJson, resource.GetIdentifier(), resource.GetMetadata(ResourceType_Instance),
+                              resource.GetAttachments(), missingTags /* ignoreTagLength */);
     }
-    else if (request.GetLevel() == ResourceType_Instance)
+    else if (request.GetLevel() != ResourceType_Instance &&
+             request.IsRetrieveOneInstanceMetadataAndAttachments())
     {
-      instancePublicId = resource.GetIdentifier();
+      LOG(INFO) << "Will retrieve missing DICOM tags from instance: " << resource.GetOneInstancePublicId();
+
+      context.ReadDicomAsJson(tmpDicomAsJson, resource.GetOneInstancePublicId(), resource.GetOneInstanceMetadata(),
+                              resource.GetOneInstanceAttachments(), missingTags /* ignoreTagLength */);
     }
     else
     {
+      // TODO-FIND: This fallback shouldn't be necessary
+
       FindRequest requestDicomAttachment(request.GetLevel());
       requestDicomAttachment.SetOrthancId(request.GetLevel(), resource.GetIdentifier());
-      requestDicomAttachment.SetRetrieveOneInstanceIdentifier(true);
+
+      if (request.GetLevel() == ResourceType_Instance)
+      {
+        requestDicomAttachment.SetRetrieveMetadata(true);
+        requestDicomAttachment.SetRetrieveAttachments(true);
+      }
+      else
+      {
+        requestDicomAttachment.SetRetrieveOneInstanceMetadataAndAttachments(true);
+      }
 
       FindResponse responseDicomAttachment;
       context.GetIndex().ExecuteFind(responseDicomAttachment, requestDicomAttachment);
 
-      if (responseDicomAttachment.GetSize() != 1 ||
-          !responseDicomAttachment.GetResourceByIndex(0).HasOneInstanceIdentifier())
+      if (responseDicomAttachment.GetSize() != 1)
       {
         throw OrthancException(ErrorCode_InexistentFile);
       }
       else
       {
-        instancePublicId = responseDicomAttachment.GetResourceByIndex(0).GetOneInstanceIdentifier();
+        const FindResponse::Resource& response = responseDicomAttachment.GetResourceByIndex(0);
+        const std::string instancePublicId = response.GetIdentifier();
+        LOG(INFO) << "Will retrieve missing DICOM tags from instance: " << instancePublicId;
+
+        if (request.GetLevel() == ResourceType_Instance)
+        {
+          context.ReadDicomAsJson(tmpDicomAsJson, response.GetIdentifier(), response.GetMetadata(ResourceType_Instance),
+                                  response.GetAttachments(), missingTags /* ignoreTagLength */);
+        }
+        else
+        {
+          context.ReadDicomAsJson(tmpDicomAsJson, response.GetOneInstancePublicId(), response.GetOneInstanceMetadata(),
+                                  response.GetOneInstanceAttachments(), missingTags /* ignoreTagLength */);
+        }
       }
     }
 
-    LOG(INFO) << "Will retrieve missing DICOM tags from instance: " << instancePublicId;
-
-    // TODO-FIND: What do we do if the DICOM has been removed since the request?
-    // Do we fail, or do we skip the resource?
-
-    Json::Value tmpDicomAsJson;
-    context.ReadDicomAsJson(tmpDicomAsJson, instancePublicId, missingTags /* ignoreTagLength */);
-
     DicomMap tmpDicomMap;
     tmpDicomMap.FromDicomAsJson(tmpDicomAsJson, false /* append */, true /* parseSequences*/);
 
--- a/OrthancServer/Sources/ResourceFinder.h	Wed Sep 11 16:31:11 2024 +0200
+++ b/OrthancServer/Sources/ResourceFinder.h	Wed Sep 11 20:49:34 2024 +0200
@@ -144,9 +144,9 @@
       request_.SetLabelsConstraint(constraint);
     }
 
-    void SetRetrieveOneInstanceIdentifier(bool retrieve)
+    void SetRetrieveOneInstanceMetadataAndAttachments(bool retrieve)
     {
-      request_.SetRetrieveOneInstanceIdentifier(retrieve);
+      request_.SetRetrieveOneInstanceMetadataAndAttachments(retrieve);
     }
 
     void SetRetrieveMetadata(bool retrieve)
--- a/OrthancServer/Sources/ServerContext.cpp	Wed Sep 11 16:31:11 2024 +0200
+++ b/OrthancServer/Sources/ServerContext.cpp	Wed Sep 11 20:49:34 2024 +0200
@@ -1036,11 +1036,91 @@
     dicomAsJson["7fe0,0010"] = pixelData;
   }
 
+
+  static bool LookupMetadata(std::string& value,
+                             MetadataType key,
+                             const std::map<MetadataType, std::string>& instanceMetadata)
+  {
+    std::map<MetadataType, std::string>::const_iterator found = instanceMetadata.find(key);
+
+    if (found == instanceMetadata.end())
+    {
+      return false;
+    }
+    else
+    {
+      value = found->second;
+      return true;
+    }
+  }
+
+
+  static bool LookupAttachment(FileInfo& target,
+                               FileContentType type,
+                               const std::map<FileContentType, FileInfo>& attachments)
+  {
+    std::map<FileContentType, FileInfo>::const_iterator found = attachments.find(type);
+
+    if (found == attachments.end())
+    {
+      return false;
+    }
+    else if (found->second.GetContentType() == type)
+    {
+      target = found->second;
+      return true;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_DatabasePlugin);
+    }
+  }
+
   
   void ServerContext::ReadDicomAsJson(Json::Value& result,
                                       const std::string& instancePublicId,
                                       const std::set<DicomTag>& ignoreTagLength)
   {
+    // TODO-FIND: This is a compatibility method, should be removed
+
+    std::map<MetadataType, std::string> metadata;
+    std::map<FileContentType, FileInfo> attachments;
+
+    FileInfo attachment;
+    int64_t revision;  // Ignored
+
+    if (index_.LookupAttachment(attachment, revision, instancePublicId, FileContentType_Dicom))
+    {
+      attachments[FileContentType_Dicom] = attachment;
+    }
+
+    if (index_.LookupAttachment(attachment, revision, instancePublicId, FileContentType_DicomUntilPixelData))
+    {
+      attachments[FileContentType_DicomUntilPixelData] = attachment;
+    }
+
+    if (index_.LookupAttachment(attachment, revision, instancePublicId, FileContentType_DicomAsJson))
+    {
+      attachments[FileContentType_DicomAsJson] = attachment;
+    }
+
+    std::string s;
+    if (index_.LookupMetadata(s, revision, instancePublicId, ResourceType_Instance,
+                              MetadataType_Instance_PixelDataOffset))
+    {
+      metadata[MetadataType_Instance_PixelDataOffset] = s;
+    }
+
+    ReadDicomAsJson(result, instancePublicId, metadata, attachments, ignoreTagLength);
+  }
+
+
+  void ServerContext::ReadDicomAsJson(Json::Value& result,
+                                      const std::string& instancePublicId,
+                                      const std::map<MetadataType, std::string>& instanceMetadata,
+                                      const std::map<FileContentType, FileInfo>& instanceAttachments,
+                                      const std::set<DicomTag>& ignoreTagLength)
+  {
     /**
      * CASE 1: The DICOM file, truncated at pixel data, is available
      * as an attachment (it was created either because the storage
@@ -1049,9 +1129,8 @@
      **/
     
     FileInfo attachment;
-    int64_t revision;  // Ignored
-
-    if (index_.LookupAttachment(attachment, revision, instancePublicId, FileContentType_DicomUntilPixelData))
+
+    if (LookupAttachment(attachment, FileContentType_DicomUntilPixelData, instanceAttachments))
     {
       std::string dicom;
 
@@ -1077,8 +1156,7 @@
 
       {
         std::string s;
-        if (index_.LookupMetadata(s, revision, instancePublicId, ResourceType_Instance,
-                                  MetadataType_Instance_PixelDataOffset))
+        if (LookupMetadata(s, MetadataType_Instance_PixelDataOffset, instanceMetadata))
         {
           hasPixelDataOffset = false;
 
@@ -1109,7 +1187,7 @@
 
       if (hasPixelDataOffset &&
           area_.HasReadRange() &&
-          index_.LookupAttachment(attachment, revision, instancePublicId, FileContentType_Dicom) &&
+          LookupAttachment(attachment, FileContentType_Dicom, instanceAttachments) &&
           attachment.GetCompressionType() == CompressionType_None)
       {
         /**
@@ -1132,7 +1210,7 @@
         InjectEmptyPixelData(result);
       }
       else if (ignoreTagLength.empty() &&
-               index_.LookupAttachment(attachment, revision, instancePublicId, FileContentType_DicomAsJson))
+               LookupAttachment(attachment, FileContentType_DicomAsJson, instanceAttachments))
       {
         /**
          * CASE 3: This instance was created using Orthanc <=
--- a/OrthancServer/Sources/ServerContext.h	Wed Sep 11 16:31:11 2024 +0200
+++ b/OrthancServer/Sources/ServerContext.h	Wed Sep 11 20:49:34 2024 +0200
@@ -373,10 +373,16 @@
 
     void ReadDicomAsJson(Json::Value& result,
                          const std::string& instancePublicId,
+                         const std::map<MetadataType, std::string>& instanceMetadata,
+                         const std::map<FileContentType, FileInfo>& instanceAttachments,
                          const std::set<DicomTag>& ignoreTagLength);
 
     void ReadDicomAsJson(Json::Value& result,
-                         const std::string& instancePublicId);
+                         const std::string& instancePublicId,
+                         const std::set<DicomTag>& ignoreTagLength);  // TODO-FIND: Can this be removed?
+
+    void ReadDicomAsJson(Json::Value& result,
+                         const std::string& instancePublicId);  // TODO-FIND: Can this be removed?
 
     void ReadDicom(std::string& dicom,
                    const std::string& instancePublicId);