changeset 5710:a786da7599d5 find-refactoring-clean

integration find-refactoring->find-refactoring-clean
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 12 Jul 2024 18:55:22 +0200
parents c8d21a09aae6 (diff) 476b1db52110 (current diff)
children 31eb66eaed86
files OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp OrthancServer/Sources/OrthancWebDav.cpp
diffstat 6 files changed, 100 insertions(+), 1251 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/Sources/OrthancFindRequestHandler.cpp	Fri Jul 12 18:41:44 2024 +0200
+++ b/OrthancServer/Sources/OrthancFindRequestHandler.cpp	Fri Jul 12 18:55:22 2024 +0200
@@ -82,102 +82,6 @@
   }
 
 
-  static void AddAnswer(DicomFindAnswers& answers,
-                        ServerContext& context,
-                        const std::string& publicId,
-                        const std::string& instanceId,
-                        const DicomMap& mainDicomTags,
-                        const Json::Value* dicomAsJson,
-                        ResourceType level,
-                        const DicomArray& query,
-                        const std::list<DicomTag>& sequencesToReturn,
-                        const std::string& defaultPrivateCreator,
-                        const std::map<uint16_t, std::string>& privateCreators,
-                        const std::string& retrieveAet,
-                        bool allowStorageAccess)
-  {
-    ExpandedResource resource;
-    std::set<DicomTag> requestedTags;
-    
-    query.GetTags(requestedTags);
-    requestedTags.erase(DICOM_TAG_QUERY_RETRIEVE_LEVEL); // this is not part of the answer
-
-    // reuse ExpandResource to get missing tags and computed tags (ModalitiesInStudy ...).  This code is therefore shared between C-Find, tools/find, list-resources and QIDO-RS
-    context.ExpandResource(resource, publicId, mainDicomTags, instanceId, dicomAsJson,
-                           level, requestedTags, ExpandResourceFlags_IncludeMainDicomTags, allowStorageAccess);
-
-    DicomMap result;
-
-    /**
-     * Add the mandatory "Retrieve AE Title (0008,0054)" tag, which was missing in Orthanc <= 1.7.2.
-     * http://dicom.nema.org/medical/dicom/current/output/html/part04.html#sect_C.4.1.1.3.2
-     * https://groups.google.com/g/orthanc-users/c/-7zNTKR_PMU/m/kfjwzEVNAgAJ
-     **/
-    result.SetValue(DICOM_TAG_RETRIEVE_AE_TITLE, retrieveAet, false /* not binary */);
-
-    for (size_t i = 0; i < query.GetSize(); i++)
-    {
-      if (query.GetElement(i).GetTag() == DICOM_TAG_QUERY_RETRIEVE_LEVEL)
-      {
-        // Fix issue 30 on Google Code (QR response missing "Query/Retrieve Level" (008,0052))
-        result.SetValue(query.GetElement(i).GetTag(), query.GetElement(i).GetValue());
-      }
-      else if (query.GetElement(i).GetTag() == DICOM_TAG_SPECIFIC_CHARACTER_SET)
-      {
-        // Do not include the encoding, this is handled by class ParsedDicomFile
-      }
-      else
-      {
-        const DicomTag& tag = query.GetElement(i).GetTag();
-        const DicomValue* value = resource.GetMainDicomTags().TestAndGetValue(tag);
-
-        if (value != NULL &&
-            !value->IsNull() &&
-            !value->IsBinary())
-        {
-          result.SetValue(tag, value->GetContent(), false);
-        }
-        else
-        {
-          result.SetValue(tag, "", false);
-        }
-      }
-    }
-
-    if (result.GetSize() == 0 &&
-        sequencesToReturn.empty())
-    {
-      CLOG(WARNING, DICOM) << "The C-FIND request does not return any DICOM tag";
-    }
-    else if (sequencesToReturn.empty())
-    {
-      answers.Add(result);
-    }
-    else if (dicomAsJson == NULL)
-    {
-      CLOG(WARNING, DICOM) << "C-FIND query requesting a sequence, but reading JSON from disk is disabled";
-      answers.Add(result);
-    }
-    else
-    {
-      ParsedDicomFile dicom(result, GetDefaultDicomEncoding(),
-                            true /* be permissive, cf. issue #136 */, defaultPrivateCreator, privateCreators);
-
-      for (std::list<DicomTag>::const_iterator tag = sequencesToReturn.begin();
-           tag != sequencesToReturn.end(); ++tag)
-      {
-        assert(dicomAsJson != NULL);
-        const Json::Value& source = (*dicomAsJson) [tag->Format()];
-
-        CopySequence(dicom, *tag, source, defaultPrivateCreator, privateCreators);
-      }
-
-      answers.Add(dicom);
-    }
-  }
-
-
-
   bool OrthancFindRequestHandler::FilterQueryTag(std::string& value /* can be modified */,
                                                  ResourceType level,
                                                  const DicomTag& tag,
@@ -248,88 +152,6 @@
   }
 
 
-  class OrthancFindRequestHandler::LookupVisitor : public ServerContext::ILookupVisitor
-  {
-  private:
-    DicomFindAnswers&           answers_;
-    ServerContext&              context_;
-    ResourceType                level_;
-    const DicomMap&             query_;
-    DicomArray                  queryAsArray_;
-    const std::list<DicomTag>&  sequencesToReturn_;
-    std::string                 defaultPrivateCreator_;       // the private creator to use if the group is not defined in the query itself
-    const std::map<uint16_t, std::string>& privateCreators_;  // the private creators defined in the query itself
-    std::string                 retrieveAet_;
-    FindStorageAccessMode       findStorageAccessMode_;
-
-  public:
-    LookupVisitor(DicomFindAnswers&  answers,
-                  ServerContext& context,
-                  ResourceType level,
-                  const DicomMap& query,
-                  const std::list<DicomTag>& sequencesToReturn,
-                  const std::map<uint16_t, std::string>& privateCreators,
-                  FindStorageAccessMode findStorageAccessMode) :
-      answers_(answers),
-      context_(context),
-      level_(level),
-      query_(query),
-      queryAsArray_(query),
-      sequencesToReturn_(sequencesToReturn),
-      privateCreators_(privateCreators),
-      findStorageAccessMode_(findStorageAccessMode)
-    {
-      answers_.SetComplete(false);
-
-      {
-        OrthancConfiguration::ReaderLock lock;
-        defaultPrivateCreator_ = lock.GetConfiguration().GetDefaultPrivateCreator();
-        retrieveAet_ = lock.GetConfiguration().GetOrthancAET();
-      }
-    }
-
-    virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE
-    {
-      // Ask the "DICOM-as-JSON" attachment only if sequences are to
-      // be returned OR if "query_" contains non-main DICOM tags!
-
-      DicomMap withoutSpecialTags;
-      withoutSpecialTags.Assign(query_);
-
-      // Check out "ComputeCounters()"
-      withoutSpecialTags.Remove(DICOM_TAG_MODALITIES_IN_STUDY);
-      withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES);
-      withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES);
-      withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES);
-      withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES);
-      withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES);
-      withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES);
-      withoutSpecialTags.Remove(DICOM_TAG_SOP_CLASSES_IN_STUDY);
-
-      // Check out "AddAnswer()"
-      withoutSpecialTags.Remove(DICOM_TAG_SPECIFIC_CHARACTER_SET);
-      withoutSpecialTags.Remove(DICOM_TAG_QUERY_RETRIEVE_LEVEL);
-      
-      return (!sequencesToReturn_.empty() ||
-              !withoutSpecialTags.HasOnlyMainDicomTags());
-    }
-      
-    virtual void MarkAsComplete() ORTHANC_OVERRIDE
-    {
-      answers_.SetComplete(true);
-    }
-
-    virtual void Visit(const std::string& publicId,
-                       const std::string& instanceId,
-                       const DicomMap& mainDicomTags,
-                       const Json::Value* dicomAsJson) ORTHANC_OVERRIDE
-    {
-      AddAnswer(answers_, context_, publicId, instanceId, mainDicomTags, dicomAsJson, level_, queryAsArray_, sequencesToReturn_,
-                defaultPrivateCreator_, privateCreators_, retrieveAet_, IsStorageAccessAllowedForAnswers(findStorageAccessMode_));
-    }
-  };
-
-
   namespace
   {
     class LookupVisitorV2 : public ResourceFinder::IVisitor
@@ -622,31 +444,12 @@
      * Run the query.
      **/
 
-    size_t limit = (level == ResourceType_Instance) ? maxInstances_ : maxResults_;
-
-
-    if (true)
-    {
-      /**
-       * EXPERIMENTAL VERSION
-       **/
-
-      ResourceFinder finder(level, false /* don't expand */);
-      finder.SetDatabaseLookup(lookup);
-      finder.AddRequestedTags(requestedTags);
+    ResourceFinder finder(level, false /* don't expand */);
+    finder.SetDatabaseLookup(lookup);
+    finder.AddRequestedTags(requestedTags);
 
-      LookupVisitorV2 visitor(answers, *filteredInput, sequencesToReturn, privateCreators);
-      finder.Execute(visitor, context_);
-    }
-    else
-    {
-      /**
-       * VERSION IN ORTHANC <= 1.12.4
-       **/
-
-      LookupVisitor visitor(answers, context_, level, *filteredInput, sequencesToReturn, privateCreators, context_.GetFindStorageAccessMode());
-      context_.Apply(visitor, lookup, level, 0 /* "since" is not relevant to C-FIND */, limit);
-    }
+    LookupVisitorV2 visitor(answers, *filteredInput, sequencesToReturn, privateCreators);
+    finder.Execute(visitor, context_);
   }
 
 
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Fri Jul 12 18:41:44 2024 +0200
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Fri Jul 12 18:55:22 2024 +0200
@@ -174,77 +174,6 @@
 
   // List all the patients, studies, series or instances ----------------------
  
-  static void AnswerListOfResources1(RestApiOutput& output,
-                                    ServerContext& context,
-                                    const std::list<std::string>& resources,
-                                    const std::map<std::string, std::string>& instancesIds, // optional: the id of an instance for each found resource.
-                                    const std::map<std::string, boost::shared_ptr<DicomMap> >& resourcesMainDicomTags,  // optional: all tags read from DB for a resource (current level and upper levels)
-                                    const std::map<std::string, boost::shared_ptr<Json::Value> >& resourcesDicomAsJson, // optional: the dicom-as-json for each resource
-                                    ResourceType level,
-                                    bool expand,
-                                    DicomToJsonFormat format,
-                                    const std::set<DicomTag>& requestedTags,
-                                    bool allowStorageAccess)
-  {
-    Json::Value answer = Json::arrayValue;
-
-    for (std::list<std::string>::const_iterator
-           resource = resources.begin(); resource != resources.end(); ++resource)
-    {
-      if (expand)
-      {
-        Json::Value expanded;
-
-        std::map<std::string, std::string>::const_iterator instanceId = instancesIds.find(*resource);
-        if (instanceId != instancesIds.end())  // if it is found in instancesIds, it is also in resourcesDicomAsJson and mainDicomTags
-        {
-          // reuse data already collected before (e.g during lookup)
-          std::map<std::string, boost::shared_ptr<DicomMap> >::const_iterator mainDicomTags = resourcesMainDicomTags.find(*resource);
-          std::map<std::string, boost::shared_ptr<Json::Value> >::const_iterator dicomAsJson = resourcesDicomAsJson.find(*resource);
-
-          context.ExpandResource(expanded, *resource, 
-                                 *(mainDicomTags->second.get()),
-                                 instanceId->second,
-                                 dicomAsJson->second.get(),
-                                 level, format, requestedTags, allowStorageAccess);
-        }
-        else
-        {
-          context.ExpandResource(expanded, *resource, level, format, requestedTags, allowStorageAccess);
-        }
-
-        if (expanded.type() == Json::objectValue)
-        {
-          answer.append(expanded);
-        }
-      }
-      else
-      {
-        answer.append(*resource);
-      }
-    }
-
-    output.AnswerJson(answer);
-  }
-
-
-  static void AnswerListOfResources2(RestApiOutput& output,
-                                    ServerContext& context,
-                                    const std::list<std::string>& resources,
-                                    ResourceType level,
-                                    bool expand,
-                                    DicomToJsonFormat format,
-                                    const std::set<DicomTag>& requestedTags,
-                                    bool allowStorageAccess)
-  {
-    std::map<std::string, std::string> unusedInstancesIds;
-    std::map<std::string, boost::shared_ptr<DicomMap> > unusedResourcesMainDicomTags;
-    std::map<std::string, boost::shared_ptr<Json::Value> > unusedResourcesDicomAsJson;
-
-    AnswerListOfResources1(output, context, resources, unusedInstancesIds, unusedResourcesMainDicomTags, unusedResourcesDicomAsJson, level, expand, format, requestedTags, allowStorageAccess);
-  }
-
-
   template <enum ResourceType resourceType>
   static void ListResources(RestApiGetCall& call)
   {
@@ -267,96 +196,44 @@
         .SetHttpGetSample("https://orthanc.uclouvain.be/demo/" + resources + "?since=0&limit=2", true);
       return;
     }
-    
-    ServerIndex& index = OrthancRestApi::GetIndex(call);
-    ServerContext& context = OrthancRestApi::GetContext(call);
-
-    if (true)
+
+    // TODO-FIND: include the FindRequest options parsing in a method (parse from get-arguments and from post payload)
+    // TODO-FIND: support other values for expand like expand=MainDicomTags,Labels,Parent,SeriesStatus
+    const bool expand = (call.HasArgument("expand") &&
+                         call.GetBooleanArgument("expand", true));
+
+    std::set<DicomTag> requestedTags;
+    OrthancRestApi::GetRequestedTags(requestedTags, call);
+
+    ResourceFinder finder(resourceType, expand);
+    finder.AddRequestedTags(requestedTags);
+
+    if (call.HasArgument("limit") ||
+        call.HasArgument("since"))
     {
-      /**
-       * EXPERIMENTAL VERSION
-       **/
-
-      // TODO-FIND: include the FindRequest options parsing in a method (parse from get-arguments and from post payload)
-      // TODO-FIND: support other values for expand like expand=MainDicomTags,Labels,Parent,SeriesStatus
-      const bool expand = (call.HasArgument("expand") &&
-                           call.GetBooleanArgument("expand", true));
-
-      std::set<DicomTag> requestedTags;
-      OrthancRestApi::GetRequestedTags(requestedTags, call);
-
-      ResourceFinder finder(resourceType, expand);
-      finder.AddRequestedTags(requestedTags);
-
-      if (call.HasArgument("limit") ||
-          call.HasArgument("since"))
+      if (!call.HasArgument("limit"))
       {
-        if (!call.HasArgument("limit"))
-        {
-          throw OrthancException(ErrorCode_BadRequest,
-                                 "Missing \"limit\" argument for GET request against: " +
-                                 call.FlattenUri());
-        }
-
-        if (!call.HasArgument("since"))
-        {
-          throw OrthancException(ErrorCode_BadRequest,
-                                 "Missing \"since\" argument for GET request against: " +
-                                 call.FlattenUri());
-        }
-
-        uint64_t since = boost::lexical_cast<uint64_t>(call.GetArgument("since", ""));
-        uint64_t limit = boost::lexical_cast<uint64_t>(call.GetArgument("limit", ""));
-        finder.SetLimitsSince(since);
-        finder.SetLimitsCount(limit);
+        throw OrthancException(ErrorCode_BadRequest,
+                               "Missing \"limit\" argument for GET request against: " +
+                               call.FlattenUri());
       }
 
-      Json::Value answer;
-      finder.Execute(answer, context, OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human));
-      call.GetOutput().AnswerJson(answer);
-    }
-    else
-    {
-      /**
-       * VERSION IN ORTHANC <= 1.12.4
-       **/
-
-      std::list<std::string> result;
-
-      std::set<DicomTag> requestedTags;
-      OrthancRestApi::GetRequestedTags(requestedTags, call);
-
-      if (call.HasArgument("limit") ||
-          call.HasArgument("since"))
+      if (!call.HasArgument("since"))
       {
-        if (!call.HasArgument("limit"))
-        {
-          throw OrthancException(ErrorCode_BadRequest,
-                                 "Missing \"limit\" argument for GET request against: " +
-                                 call.FlattenUri());
-        }
-
-        if (!call.HasArgument("since"))
-        {
-          throw OrthancException(ErrorCode_BadRequest,
-                                 "Missing \"since\" argument for GET request against: " +
-                                 call.FlattenUri());
-        }
-
-        size_t since = boost::lexical_cast<size_t>(call.GetArgument("since", ""));
-        size_t limit = boost::lexical_cast<size_t>(call.GetArgument("limit", ""));
-        index.GetAllUuids(result, resourceType, since, limit);
+        throw OrthancException(ErrorCode_BadRequest,
+                               "Missing \"since\" argument for GET request against: " +
+                               call.FlattenUri());
       }
-      else
-      {
-        index.GetAllUuids(result, resourceType);
-      }
-
-      AnswerListOfResources2(call.GetOutput(), context, result, resourceType, call.HasArgument("expand") && call.GetBooleanArgument("expand", true),
-                            OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human),
-                            requestedTags,
-                            true /* allowStorageAccess */);
+
+      uint64_t since = boost::lexical_cast<uint64_t>(call.GetArgument("since", ""));
+      uint64_t limit = boost::lexical_cast<uint64_t>(call.GetArgument("limit", ""));
+      finder.SetLimitsSince(since);
+      finder.SetLimitsCount(limit);
     }
+
+    Json::Value answer;
+    finder.Execute(answer, OrthancRestApi::GetContext(call), OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human));
+    call.GetOutput().AnswerJson(answer);
   }
 
 
@@ -380,39 +257,19 @@
       return;
     }
 
-    const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human);
-
     std::set<DicomTag> requestedTags;
     OrthancRestApi::GetRequestedTags(requestedTags, call);
 
-    if (true)
+    const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human);
+
+    ResourceFinder finder(resourceType, true /* expand */);
+    finder.AddRequestedTags(requestedTags);
+    finder.SetOrthancId(resourceType, call.GetUriComponent("id", ""));
+
+    Json::Value json;
+    if (finder.ExecuteOneResource(json, OrthancRestApi::GetContext(call), format))
     {
-      /**
-       * EXPERIMENTAL VERSION
-       **/
-
-      ResourceFinder finder(resourceType, true /* expand */);
-      finder.AddRequestedTags(requestedTags);
-      finder.SetOrthancId(resourceType, call.GetUriComponent("id", ""));
-
-      Json::Value json;
-      if (finder.ExecuteOneResource(json, OrthancRestApi::GetContext(call), format))
-      {
-        call.GetOutput().AnswerJson(json);
-      }
-    }
-    else
-    {
-      /**
-       * VERSION IN ORTHANC <= 1.12.4
-       **/
-
-      Json::Value json;
-      if (OrthancRestApi::GetContext(call).ExpandResource(
-            json, call.GetUriComponent("id", ""), resourceType, format, requestedTags, true /* allowStorageAccess */))
-      {
-        call.GetOutput().AnswerJson(json);
-      }
+      call.GetOutput().AnswerJson(json);
     }
   }
 
@@ -3201,70 +3058,6 @@
   }
 
 
-  namespace 
-  {
-    class FindVisitor : public ServerContext::ILookupVisitor
-    {
-    private:
-      bool                    isComplete_;
-      std::list<std::string>  resources_;
-      FindStorageAccessMode   findStorageAccessMode_;
-      
-      // cache the data we used during lookup and that we could reuse when building the answers
-      std::map<std::string, std::string> instancesIds_;         // the id of an instance for each found resource.
-      std::map<std::string, boost::shared_ptr<DicomMap> > resourcesMainDicomTags_;  // all tags read from DB for a resource (current level and upper levels)
-      std::map<std::string, boost::shared_ptr<Json::Value> > resourcesDicomAsJson_; // the dicom-as-json for a resource
-
-      DicomToJsonFormat       format_;
-
-    public:
-      explicit FindVisitor(DicomToJsonFormat format, FindStorageAccessMode findStorageAccessMode) :
-        isComplete_(false),
-        findStorageAccessMode_(findStorageAccessMode),
-        format_(format)
-      {
-      }
-      
-      virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE
-      {
-        return false;   // (*)
-      }
-      
-      virtual void MarkAsComplete() ORTHANC_OVERRIDE
-      {
-        isComplete_ = true;  // Unused information as of Orthanc 1.5.0
-      }
-
-      virtual void Visit(const std::string& publicId,
-                         const std::string& instanceId,
-                         const DicomMap& mainDicomTags,
-                         const Json::Value* dicomAsJson)  ORTHANC_OVERRIDE
-      {
-        resources_.push_back(publicId);
-        instancesIds_[publicId] = instanceId;
-        resourcesMainDicomTags_[publicId].reset(mainDicomTags.Clone());
-        if (dicomAsJson != NULL)
-        {
-          resourcesDicomAsJson_[publicId].reset(new Json::Value(*dicomAsJson));  // keep our own copy because we might reuse it between lookup and answers
-        }
-        else
-        {
-          resourcesDicomAsJson_[publicId] = boost::shared_ptr<Json::Value>();
-        }
-      }
-
-      void Answer(RestApiOutput& output,
-                  ServerContext& context,
-                  ResourceType level,
-                  bool expand,
-                  const std::set<DicomTag>& requestedTags) const
-      {
-        AnswerListOfResources1(output, context, resources_, instancesIds_, resourcesMainDicomTags_, resourcesDicomAsJson_, level, expand, format_, requestedTags, IsStorageAccessAllowedForAnswers(findStorageAccessMode_));
-      }
-    };
-  }
-
-
   static void Find(RestApiPostCall& call)
   {
     static const char* const KEY_CASE_SENSITIVE = "CaseSensitive";
@@ -3371,12 +3164,8 @@
       throw OrthancException(ErrorCode_BadRequest, 
                              "Field \"" + std::string(KEY_LABELS_CONSTRAINT) + "\" must be an array of strings");
     }
-    else if (true)
+    else
     {
-      /**
-       * EXPERIMENTAL VERSION
-       **/
-
       bool expand = false;
       if (request.isMember(KEY_EXPAND))
       {
@@ -3500,125 +3289,6 @@
       finder.Execute(answer, context, format);
       call.GetOutput().AnswerJson(answer);
     }
-    else
-    {
-      /**
-       * VERSION IN ORTHANC <= 1.12.4
-       **/
-      bool expand = false;
-      if (request.isMember(KEY_EXPAND))
-      {
-        expand = request[KEY_EXPAND].asBool();
-      }
-
-      bool caseSensitive = false;
-      if (request.isMember(KEY_CASE_SENSITIVE))
-      {
-        caseSensitive = request[KEY_CASE_SENSITIVE].asBool();
-      }
-
-      size_t limit = 0;
-      if (request.isMember(KEY_LIMIT))
-      {
-        int tmp = request[KEY_LIMIT].asInt();
-        if (tmp < 0)
-        {
-          throw OrthancException(ErrorCode_ParameterOutOfRange,
-                                 "Field \"" + std::string(KEY_LIMIT) + "\" must be a positive integer");
-        }
-
-        limit = static_cast<size_t>(tmp);
-      }
-
-      size_t since = 0;
-      if (request.isMember(KEY_SINCE))
-      {
-        int tmp = request[KEY_SINCE].asInt();
-        if (tmp < 0)
-        {
-          throw OrthancException(ErrorCode_ParameterOutOfRange,
-                                 "Field \"" + std::string(KEY_SINCE) + "\" must be a positive integer");
-        }
-
-        since = static_cast<size_t>(tmp);
-      }
-
-      std::set<DicomTag> requestedTags;
-
-      if (request.isMember(KEY_REQUESTED_TAGS))
-      {
-        FromDcmtkBridge::ParseListOfTags(requestedTags, request[KEY_REQUESTED_TAGS]);
-      }
-
-      ResourceType level = StringToResourceType(request[KEY_LEVEL].asCString());
-
-      DatabaseLookup query;
-
-      Json::Value::Members members = request[KEY_QUERY].getMemberNames();
-      for (size_t i = 0; i < members.size(); i++)
-      {
-        if (request[KEY_QUERY][members[i]].type() != Json::stringValue)
-        {
-          throw OrthancException(ErrorCode_BadRequest,
-                                 "Tag \"" + members[i] + "\" must be associated with a string");
-        }
-
-        const std::string value = request[KEY_QUERY][members[i]].asString();
-
-        if (!value.empty())
-        {
-          // An empty string corresponds to an universal constraint,
-          // so we ignore it. This mimics the behavior of class
-          // "OrthancFindRequestHandler"
-          query.AddRestConstraint(FromDcmtkBridge::ParseTag(members[i]), 
-                                  value, caseSensitive, true);
-        }
-      }
-
-      std::set<std::string> labels;
-
-      if (request.isMember(KEY_LABELS))  // New in Orthanc 1.12.0
-      {
-        for (Json::Value::ArrayIndex i = 0; i < request[KEY_LABELS].size(); i++)
-        {
-          if (request[KEY_LABELS][i].type() != Json::stringValue)
-          {
-            throw OrthancException(ErrorCode_BadRequest, "Field \"" + std::string(KEY_LABELS) + "\" must contain strings");
-          }
-          else
-          {
-            labels.insert(request[KEY_LABELS][i].asString());
-          }
-        }
-      }
-
-      LabelsConstraint labelsConstraint = LabelsConstraint_All;
-      
-      if (request.isMember(KEY_LABELS_CONSTRAINT))
-      {
-        const std::string& s = request[KEY_LABELS_CONSTRAINT].asString();
-        if (s == "All")
-        {
-          labelsConstraint = LabelsConstraint_All;
-        }
-        else if (s == "Any")
-        {
-          labelsConstraint = LabelsConstraint_Any;
-        }
-        else if (s == "None")
-        {
-          labelsConstraint = LabelsConstraint_None;
-        }
-        else
-        {
-          throw OrthancException(ErrorCode_BadRequest, "Field \"" + std::string(KEY_LABELS_CONSTRAINT) + "\" must be \"All\", \"Any\", or \"None\"");
-        }
-      }
-      
-      FindVisitor visitor(OrthancRestApi::GetDicomFormat(request, DicomToJsonFormat_Human), context.GetFindStorageAccessMode());
-      context.Apply(visitor, query, level, labels, labelsConstraint, since, limit);
-      visitor.Answer(call.GetOutput(), context, level, expand, requestedTags);
-    }
   }
 
 
@@ -3646,9 +3316,6 @@
       return;
     }
 
-    ServerIndex& index = OrthancRestApi::GetIndex(call);
-    ServerContext& context = OrthancRestApi::GetContext(call);
-
     const bool expand = (!call.HasArgument("expand") ||
                          // this "expand" is the only one to have a false default value to keep backward compatibility
                          call.GetBooleanArgument("expand", false));
@@ -3657,48 +3324,13 @@
     std::set<DicomTag> requestedTags;
     OrthancRestApi::GetRequestedTags(requestedTags, call);
 
-    if (true)
-    {
-      /**
-       * EXPERIMENTAL VERSION
-       **/
-
-      ResourceFinder finder(end, expand);
-      finder.SetOrthancId(start, call.GetUriComponent("id", ""));
-      finder.AddRequestedTags(requestedTags);
-
-      Json::Value answer;
-      finder.Execute(answer, context, format);
-      call.GetOutput().AnswerJson(answer);
-    }
-    else
-    {
-      /**
-       * VERSION IN ORTHANC <= 1.12.4
-       **/
-      std::list<std::string> a, b, c;
-      a.push_back(call.GetUriComponent("id", ""));
-
-      ResourceType type = start;
-      while (type != end)
-      {
-        b.clear();
-
-        for (std::list<std::string>::const_iterator
-               it = a.begin(); it != a.end(); ++it)
-        {
-          index.GetChildren(c, *it);
-          b.splice(b.begin(), c);
-        }
-
-        type = GetChildResourceType(type);
-
-        a.clear();
-        a.splice(a.begin(), b);
-      }
-
-      AnswerListOfResources2(call.GetOutput(), context, a, type, expand, format, requestedTags, true /* allowStorageAccess */);
-    }
+    ResourceFinder finder(end, expand);
+    finder.SetOrthancId(start, call.GetUriComponent("id", ""));
+    finder.AddRequestedTags(requestedTags);
+
+    Json::Value answer;
+    finder.Execute(answer, OrthancRestApi::GetContext(call), format);
+    call.GetOutput().AnswerJson(answer);
   }
 
 
@@ -3811,26 +3443,9 @@
     const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human);
 
     Json::Value resource;
-
-    if (true)
+    if (OrthancRestApi::GetContext(call).ExpandResource(resource, current, end, format, requestedTags, true /* allowStorageAccess */))
     {
-      /**
-       * EXPERIMENTAL VERSION
-       **/
-      if (ExpandResource(resource, OrthancRestApi::GetIndex(call), currentType, current, format, false))
-      {
-        call.GetOutput().AnswerJson(resource);
-      }
-    }
-    else
-    {
-      /**
-       * VERSION IN ORTHANC <= 1.12.4
-       **/
-      if (OrthancRestApi::GetContext(call).ExpandResource(resource, current, end, format, requestedTags, true /* allowStorageAccess */))
-      {
-        call.GetOutput().AnswerJson(resource);
-      }
+      call.GetOutput().AnswerJson(resource);
     }
   }
 
@@ -4261,34 +3876,17 @@
         for (std::set<std::string>::const_iterator
                it = interest.begin(); it != interest.end(); ++it)
         {
-          if (true)
-          {
-            /**
-             * EXPERIMENTAL VERSION
-             **/
-            Json::Value item;
-            if (ExpandResource(item, OrthancRestApi::GetIndex(call), level, *it, format, metadata))
-            {
-              answer.append(item);
-            }
-          }
-          else
+          Json::Value item;
+          std::set<DicomTag> emptyRequestedTags;  // not supported for bulk content
+
+          if (OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, true /* allowStorageAccess */))
           {
-            /**
-             * VERSION IN ORTHANC <= 1.12.4
-             **/
-            Json::Value item;
-            std::set<DicomTag> emptyRequestedTags;  // not supported for bulk content
-
-            if (OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, true /* allowStorageAccess */))
+            if (metadata)
             {
-              if (metadata)
-              {
-                AddMetadata(item[METADATA], index, *it, level);
-              }
-
-              answer.append(item);
+              AddMetadata(item[METADATA], index, *it, level);
             }
+
+            answer.append(item);
           }
         }
       }
@@ -4305,36 +3903,19 @@
           Json::Value item;
           std::set<DicomTag> emptyRequestedTags;  // not supported for bulk content
 
-          if (true)
+          if (index.LookupResourceType(level, *it) &&
+              OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, true /* allowStorageAccess */))
           {
-            /**
-             * EXPERIMENTAL VERSION
-             **/
-            if (index.LookupResourceType(level, *it) &&
-                ExpandResource(item, OrthancRestApi::GetIndex(call), level, *it, format, metadata))
+            if (metadata)
             {
-              answer.append(item);
+              AddMetadata(item[METADATA], index, *it, level);
             }
+
+            answer.append(item);
           }
           else
           {
-            /**
-             * VERSION IN ORTHANC <= 1.12.4
-             **/
-            if (index.LookupResourceType(level, *it) &&
-                OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, true /* allowStorageAccess */))
-            {
-              if (metadata)
-              {
-                AddMetadata(item[METADATA], index, *it, level);
-              }
-
-              answer.append(item);
-            }
-            else
-            {
-              CLOG(INFO, HTTP) << "Unknown resource during a bulk content retrieval: " << *it;
-            }
+            CLOG(INFO, HTTP) << "Unknown resource during a bulk content retrieval: " << *it;
           }
         }
       }
--- a/OrthancServer/Sources/OrthancWebDav.cpp	Fri Jul 12 18:41:44 2024 +0200
+++ b/OrthancServer/Sources/OrthancWebDav.cpp	Fri Jul 12 18:55:22 2024 +0200
@@ -86,99 +86,6 @@
   }
 
   
-  class OrthancWebDav::DicomIdentifiersVisitor : public ServerContext::ILookupVisitor
-  {
-  private:
-    ServerContext&  context_;
-    bool            isComplete_;
-    Collection&     target_;
-    ResourceType    level_;
-
-  public:
-    DicomIdentifiersVisitor(ServerContext& context,
-                            Collection&  target,
-                            ResourceType level) :
-      context_(context),
-      isComplete_(false),
-      target_(target),
-      level_(level)
-    {
-    }
-      
-    virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE
-    {
-      return false;   // (*)
-    }
-      
-    virtual void MarkAsComplete() ORTHANC_OVERRIDE
-    {
-      isComplete_ = true;  // TODO
-    }
-
-    virtual void Visit(const std::string& publicId,
-                       const std::string& instanceId   /* unused     */,
-                       const DicomMap& mainDicomTags,
-                       const Json::Value* dicomAsJson  /* unused (*) */)  ORTHANC_OVERRIDE
-    {
-      DicomTag tag(0, 0);
-      MetadataType timeMetadata;
-
-      switch (level_)
-      {
-        case ResourceType_Study:
-          tag = DICOM_TAG_STUDY_INSTANCE_UID;
-          timeMetadata = MetadataType_LastUpdate;
-          break;
-
-        case ResourceType_Series:
-          tag = DICOM_TAG_SERIES_INSTANCE_UID;
-          timeMetadata = MetadataType_LastUpdate;
-          break;
-        
-        case ResourceType_Instance:
-          tag = DICOM_TAG_SOP_INSTANCE_UID;
-          timeMetadata = MetadataType_Instance_ReceptionDate;
-          break;
-
-        default:
-          throw OrthancException(ErrorCode_InternalError);
-      }
-        
-      std::string s;
-      if (mainDicomTags.LookupStringValue(s, tag, false) &&
-          !s.empty())
-      {
-        std::unique_ptr<Resource> resource;
-
-        if (level_ == ResourceType_Instance)
-        {
-          FileInfo info;
-          int64_t revision;  // Ignored
-          if (context_.GetIndex().LookupAttachment(info, revision, publicId, FileContentType_Dicom))
-          {
-            std::unique_ptr<File> f(new File(s + ".dcm"));
-            f->SetMimeType(MimeType_Dicom);
-            f->SetContentLength(info.GetUncompressedSize());
-            resource.reset(f.release());
-          }
-        }
-        else
-        {
-          resource.reset(new Folder(s));
-        }
-
-        if (resource.get() != NULL)
-        {
-          boost::posix_time::ptime t;
-          LookupTime(t, context_, publicId, level_, timeMetadata);
-          resource->SetCreationTime(t);
-          target_.AddResource(resource.release());
-        }
-      }
-    }
-  };
-
-  
   class OrthancWebDav::DicomIdentifiersVisitorV2 : public ResourceFinder::IVisitor
   {
   private:
@@ -271,58 +178,6 @@
   };
 
 
-  class OrthancWebDav::DicomFileVisitor : public ServerContext::ILookupVisitor
-  {
-  private:
-    ServerContext&  context_;
-    bool            success_;
-    std::string&    target_;
-    boost::posix_time::ptime&  time_;
-
-  public:
-    DicomFileVisitor(ServerContext& context,
-                     std::string& target,
-                     boost::posix_time::ptime& time) :
-      context_(context),
-      success_(false),
-      target_(target),
-      time_(time)
-    {
-    }
-
-    bool IsSuccess() const
-    {
-      return success_;
-    }
-
-    virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE
-    {
-      return false;   // (*)
-    }
-      
-    virtual void MarkAsComplete() ORTHANC_OVERRIDE
-    {
-    }
-
-    virtual void Visit(const std::string& publicId,
-                       const std::string& instanceId   /* unused     */,
-                       const DicomMap& mainDicomTags,
-                       const Json::Value* dicomAsJson  /* unused (*) */)  ORTHANC_OVERRIDE
-    {
-      if (success_)
-      {
-        success_ = false;  // Two matches => Error
-      }
-      else
-      {
-        LookupTime(time_, context_, publicId, ResourceType_Instance, MetadataType_Instance_ReceptionDate);
-        context_.ReadDicom(target_, publicId);
-        success_ = true;
-      }
-    }
-  };
-  
-
   class OrthancWebDav::DicomFileVisitorV2 : public ResourceFinder::IVisitor
   {
   private:
@@ -377,67 +232,6 @@
   };
 
 
-  class OrthancWebDav::OrthancJsonVisitor : public ServerContext::ILookupVisitor
-  {
-  private:
-    ServerContext&  context_;
-    bool            success_;
-    std::string&    target_;
-    ResourceType    level_;
-
-  public:
-    OrthancJsonVisitor(ServerContext& context,
-                       std::string& target,
-                       ResourceType level) :
-      context_(context),
-      success_(false),
-      target_(target),
-      level_(level)
-    {
-    }
-
-    bool IsSuccess() const
-    {
-      return success_;
-    }
-
-    virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE
-    {
-      return false;   // (*)
-    }
-      
-    virtual void MarkAsComplete() ORTHANC_OVERRIDE
-    {
-    }
-
-    virtual void Visit(const std::string& publicId,
-                       const std::string& instanceId   /* unused     */,
-                       const DicomMap& mainDicomTags,
-                       const Json::Value* dicomAsJson  /* unused (*) */)  ORTHANC_OVERRIDE
-    {
-      Json::Value resource;
-      std::set<DicomTag> emptyRequestedTags;  // not supported for webdav
-
-      if (context_.ExpandResource(resource, publicId, level_, DicomToJsonFormat_Human, emptyRequestedTags, true /* allowStorageAccess */))
-      {
-        if (success_)
-        {
-          success_ = false;  // Two matches => Error
-        }
-        else
-        {
-          target_ = resource.toStyledString();
-
-          // Replace UNIX newlines with DOS newlines 
-          boost::replace_all(target_, "\n", "\r\n");
-
-          success_ = true;
-        }
-      }
-    }
-  };
-
-
   class OrthancWebDav::ResourcesIndex : public boost::noncopyable
   {
   public:
@@ -1569,12 +1363,11 @@
     {
       DatabaseLookup query;
       ResourceType level;
-      size_t limit = 0;  // By default, no limits
 
       if (path.size() == 1)
       {
         level = ResourceType_Study;
-        limit = 0;  // TODO - Should we limit here?
+        // TODO - Should we limit here?
       }
       else if (path.size() == 2)
       {
@@ -1599,47 +1392,31 @@
         return false;
       }
 
-      if (true)
-      {
-        /**
-         * EXPERIMENTAL VERSION
-         **/
-
-        ResourceFinder finder(level, false /* don't expand */);
-        finder.SetDatabaseLookup(query);
-        finder.SetRetrieveMetadata(true);
+      ResourceFinder finder(level, false /* don't expand */);
+      finder.SetDatabaseLookup(query);
+      finder.SetRetrieveMetadata(true);
 
-        switch (level)
-        {
-          case ResourceType_Study:
-            finder.AddRequestedTag(DICOM_TAG_STUDY_INSTANCE_UID);
-            break;
-
-          case ResourceType_Series:
-            finder.AddRequestedTag(DICOM_TAG_SERIES_INSTANCE_UID);
-            break;
+      switch (level)
+      {
+        case ResourceType_Study:
+          finder.AddRequestedTag(DICOM_TAG_STUDY_INSTANCE_UID);
+          break;
 
-          case ResourceType_Instance:
-            finder.AddRequestedTag(DICOM_TAG_SOP_INSTANCE_UID);
-            finder.SetRetrieveAttachments(true);
-            break;
-
-          default:
-            throw OrthancException(ErrorCode_InternalError);
-        }
+        case ResourceType_Series:
+          finder.AddRequestedTag(DICOM_TAG_SERIES_INSTANCE_UID);
+          break;
 
-        DicomIdentifiersVisitorV2 visitor(collection);
-        finder.Execute(visitor, context_);
+        case ResourceType_Instance:
+          finder.AddRequestedTag(DICOM_TAG_SOP_INSTANCE_UID);
+          finder.SetRetrieveAttachments(true);
+          break;
+
+        default:
+          throw OrthancException(ErrorCode_InternalError);
       }
-      else
-      {
-        /**
-         * VERSION IN ORTHANC <= 1.12.4
-         **/
 
-        DicomIdentifiersVisitor visitor(context_, collection, level);
-        context_.Apply(visitor, query, level, 0 /* since */, limit);
-      }
+      DicomIdentifiersVisitorV2 visitor(collection);
+      finder.Execute(visitor, context_);
 
       return true;
     }
@@ -1707,23 +1484,7 @@
                                 true /* case sensitive */, true /* mandatory tag */);
 
         mime = MimeType_Json;
-
-        if (true)
-        {
-          /**
-           * EXPERIMENTAL VERSION
-           **/
-          return GetOrthancJson(content, context_, ResourceType_Study, query);
-        }
-        else
-        {
-          /**
-           * VERSION IN ORTHANC <= 1.12.4
-           **/
-          OrthancJsonVisitor visitor(context_, content, ResourceType_Study);
-          context_.Apply(visitor, query, ResourceType_Study, 0 /* since */, 0 /* no limit */);
-          return visitor.IsSuccess();
-        }
+        return GetOrthancJson(content, context_, ResourceType_Study, query);
       }
       else if (path.size() == 4 &&
                path[3] == SERIES_INFO)
@@ -1735,23 +1496,7 @@
                                 true /* case sensitive */, true /* mandatory tag */);
       
         mime = MimeType_Json;
-
-        if (true)
-        {
-          /**
-           * EXPERIMENTAL VERSION
-           **/
-          return GetOrthancJson(content, context_, ResourceType_Series, query);
-        }
-        else
-        {
-          /**
-           * VERSION IN ORTHANC <= 1.12.4
-           **/
-          OrthancJsonVisitor visitor(context_, content, ResourceType_Series);
-          context_.Apply(visitor, query, ResourceType_Series, 0 /* since */, 0 /* no limit */);
-          return visitor.IsSuccess();
-        }
+        return GetOrthancJson(content, context_, ResourceType_Series, query);
       }
       else if (path.size() == 4 &&
                boost::ends_with(path[3], ".dcm"))
@@ -1768,30 +1513,15 @@
       
         mime = MimeType_Dicom;
 
-        if (true)
-        {
-          /**
-           * EXPERIMENTAL VERSION
-           **/
-          ResourceFinder finder(ResourceType_Instance, false /* no expand */);
-          finder.SetDatabaseLookup(query);
-          finder.SetRetrieveMetadata(true);
-          finder.SetRetrieveAttachments(true);
+        ResourceFinder finder(ResourceType_Instance, false /* no expand */);
+        finder.SetDatabaseLookup(query);
+        finder.SetRetrieveMetadata(true);
+        finder.SetRetrieveAttachments(true);
 
-          DicomFileVisitorV2 visitor(context_, content, modificationTime);
-          finder.Execute(visitor, context_);
+        DicomFileVisitorV2 visitor(context_, content, modificationTime);
+        finder.Execute(visitor, context_);
 
-          return visitor.IsSuccess();
-        }
-        else
-        {
-          /**
-           * VERSION IN ORTHANC <= 1.12.4
-           **/
-          DicomFileVisitor visitor(context_, content, modificationTime);
-          context_.Apply(visitor, query, ResourceType_Instance, 0 /* since */, 0 /* no limit */);
-          return visitor.IsSuccess();
-        }
+        return visitor.IsSuccess();
       }
       else
       {
--- a/OrthancServer/Sources/OrthancWebDav.h	Fri Jul 12 18:41:44 2024 +0200
+++ b/OrthancServer/Sources/OrthancWebDav.h	Fri Jul 12 18:55:22 2024 +0200
@@ -38,9 +38,7 @@
     typedef std::map<ResourceType, std::string>  Templates;
 
     class DicomDeleteVisitor;
-    class DicomFileVisitor;
     class DicomFileVisitorV2;
-    class DicomIdentifiersVisitor;  
     class DicomIdentifiersVisitorV2;
     class InstancesOfSeries;
     class InternalNode;
--- a/OrthancServer/Sources/ServerContext.cpp	Fri Jul 12 18:41:44 2024 +0200
+++ b/OrthancServer/Sources/ServerContext.cpp	Fri Jul 12 18:55:22 2024 +0200
@@ -1531,233 +1531,6 @@
   }
 
 
-  void ServerContext::Apply(ILookupVisitor& visitor,
-                            const DatabaseLookup& lookup,
-                            ResourceType queryLevel,
-                            const std::set<std::string>& labels,
-                            LabelsConstraint labelsConstraint,
-                            size_t since,
-                            size_t limit)
-  {    
-    const uint64_t databaseLimit = GetDatabaseLimits(queryLevel);
-      
-    std::vector<std::string> resources, instances;
-    const DicomTagConstraint* dicomModalitiesConstraint = NULL;
-
-    bool hasModalitiesInStudyLookup = (queryLevel == ResourceType_Study &&
-          lookup.GetConstraint(dicomModalitiesConstraint, DICOM_TAG_MODALITIES_IN_STUDY) &&
-          ((dicomModalitiesConstraint->GetConstraintType() == ConstraintType_Equal && !dicomModalitiesConstraint->GetValue().empty()) ||
-          (dicomModalitiesConstraint->GetConstraintType() == ConstraintType_List && !dicomModalitiesConstraint->GetValues().empty())));
-
-    std::unique_ptr<DatabaseLookup> fastLookup(lookup.Clone());
-    
-    if (hasModalitiesInStudyLookup)
-    {
-      fastLookup->RemoveConstraint(DICOM_TAG_MODALITIES_IN_STUDY);
-    }
-
-    if (true)
-    {
-      /**
-       * EXPERIMENTAL VERSION
-       **/
-
-      ResourceFinder finder(queryLevel, false /* TODO-FIND: don't expand for now */);
-      finder.SetDatabaseLimits(databaseLimit);
-      finder.SetDatabaseLookup(lookup);
-      finder.SetLabels(labels);
-      finder.SetLabelsConstraint(labelsConstraint);
-
-      if (queryLevel != ResourceType_Instance)
-      {
-        finder.SetRetrieveOneInstanceIdentifier(true);
-      }
-
-      FindResponse response;
-      finder.Execute(response, GetIndex());
-
-      resources.resize(response.GetSize());
-      instances.resize(response.GetSize());
-
-      for (size_t i = 0; i < response.GetSize(); i++)
-      {
-        const FindResponse::Resource& resource = response.GetResourceByIndex(i);
-        resources[i] = resource.GetIdentifier();
-
-        if (queryLevel == ResourceType_Instance)
-        {
-          instances[i] = resource.GetIdentifier();
-        }
-        else
-        {
-          instances[i] = resource.GetOneInstanceIdentifier();
-        }
-      }
-    }
-    else
-    {
-      /**
-       * VERSION IN ORTHANC <= 1.12.4
-       **/
-
-      const size_t lookupLimit = (databaseLimit == 0 ? 0 : databaseLimit + 1);
-      GetIndex().ApplyLookupResources(resources, &instances, *fastLookup, queryLevel, labels, labelsConstraint, lookupLimit);
-    }
-
-    bool complete = (databaseLimit == 0 ||
-                     resources.size() <= databaseLimit);
-
-    LOG(INFO) << "Number of candidate resources after fast DB filtering on main DICOM tags: " << resources.size();
-
-    /**
-     * "resources" contains the Orthanc ID of the resource at level
-     * "queryLevel", "instances" contains one the Orthanc ID of one
-     * sample instance from this resource.
-     **/
-    assert(resources.size() == instances.size());
-
-    size_t countResults = 0;
-    size_t skipped = 0;
-
-    const bool isDicomAsJsonNeeded = visitor.IsDicomAsJsonNeeded();
-    
-    for (size_t i = 0; i < instances.size(); i++)
-    {
-      // Optimization in Orthanc 1.5.1 - Don't read the full JSON from
-      // the disk if only "main DICOM tags" are to be returned
-
-      boost::shared_ptr<Json::Value> dicomAsJson;
-
-      bool hasOnlyMainDicomTags;
-      DicomMap dicom;
-      DicomMap allMainDicomTagsFromDB;
-      
-      if (!IsStorageAccessAllowedForAnswers(findStorageAccessMode_) 
-          || fastLookup->HasOnlyMainDicomTags())
-      {
-        // Case (1): The main DICOM tags, as stored in the database,
-        // are sufficient to look for match
-
-        if (!GetIndex().GetAllMainDicomTags(allMainDicomTagsFromDB, instances[i]))
-        {
-          // The instance has been removed during the execution of the
-          // lookup, ignore it
-          continue;
-        }
-
-        // New in Orthanc 1.6.0: Only keep the main DICOM tags at the
-        // level of interest for the query
-        switch (queryLevel)
-        {
-          // WARNING: Don't reorder cases below, and don't add "break"
-          case ResourceType_Instance:
-            dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Instance);
-
-          case ResourceType_Series:
-            dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Series);
-
-          case ResourceType_Study:
-            dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Study);
-            
-          case ResourceType_Patient:
-            dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Patient);
-            break;
-
-          default:
-            throw OrthancException(ErrorCode_InternalError);
-        }
-        
-        hasOnlyMainDicomTags = true;
-      }
-      else
-      {
-        // Case (2): Need to read the "DICOM-as-JSON" attachment from
-        // the storage area
-        dicomAsJson.reset(new Json::Value);
-        ReadDicomAsJson(*dicomAsJson, instances[i]);
-
-        dicom.FromDicomAsJson(*dicomAsJson);
-
-        // This map contains the entire JSON, i.e. more than the main DICOM tags
-        hasOnlyMainDicomTags = false;   
-      }
-      
-      if (fastLookup->IsMatch(dicom))
-      {
-        bool isMatch = true;
-
-        if (hasModalitiesInStudyLookup)
-        {
-          std::set<DicomTag> requestedTags;
-          requestedTags.insert(DICOM_TAG_MODALITIES_IN_STUDY);
-          ExpandedResource resource;
-          ComputeStudyTags(resource, *this, resources[i], requestedTags);
-
-          std::vector<std::string> modalities;
-          Toolbox::TokenizeString(modalities, resource.GetMainDicomTags().GetValue(DICOM_TAG_MODALITIES_IN_STUDY).GetContent(), '\\');
-          bool hasAtLeastOneModalityMatching = false;
-          for (size_t m = 0; m < modalities.size(); m++)
-          {
-            hasAtLeastOneModalityMatching |= dicomModalitiesConstraint->IsMatch(modalities[m]);
-          }
-
-          isMatch = isMatch && hasAtLeastOneModalityMatching;
-          // copy the value of ModalitiesInStudy such that it can be reused to build the answer
-          allMainDicomTagsFromDB.SetValue(DICOM_TAG_MODALITIES_IN_STUDY, resource.GetMainDicomTags().GetValue(DICOM_TAG_MODALITIES_IN_STUDY));
-        }
-
-        if (isMatch)
-        {
-          if (skipped < since)
-          {
-            skipped++;
-          }
-          else if (limit != 0 &&
-                  countResults >= limit)
-          {
-            // Too many results, don't mark as complete
-            complete = false;
-            break;
-          }
-          else
-          {
-            if (IsStorageAccessAllowedForAnswers(findStorageAccessMode_) &&
-                dicomAsJson.get() == NULL &&
-                isDicomAsJsonNeeded)
-            {
-              dicomAsJson.reset(new Json::Value);
-              ReadDicomAsJson(*dicomAsJson, instances[i]);
-            }
-
-            if (hasOnlyMainDicomTags)
-            {
-              // This is Case (1): The variable "dicom" only contains the main DICOM tags
-              visitor.Visit(resources[i], instances[i], allMainDicomTagsFromDB, dicomAsJson.get());
-            }
-            else
-            {
-              // Remove the non-main DICOM tags from "dicom" if Case (2)
-              // was used, for consistency with Case (1)
-
-              DicomMap mainDicomTags;
-              mainDicomTags.ExtractMainDicomTags(dicom);
-              visitor.Visit(resources[i], instances[i], mainDicomTags, dicomAsJson.get());            
-            }
-              
-            countResults ++;
-          }
-        }
-      }
-    }
-
-    if (complete)
-    {
-      visitor.MarkAsComplete();
-    }
-
-    LOG(INFO) << "Number of matching resources: " << countResults;
-  }
-
   bool ServerContext::LookupOrReconstructMetadata(std::string& target,
                                                   const std::string& publicId,
                                                   ResourceType level,
--- a/OrthancServer/Sources/ServerContext.h	Fri Jul 12 18:41:44 2024 +0200
+++ b/OrthancServer/Sources/ServerContext.h	Fri Jul 12 18:55:22 2024 +0200
@@ -66,25 +66,6 @@
     friend class ServerIndex;  // To access "RemoveFile()"
     
   public:
-    class ILookupVisitor : public boost::noncopyable
-    {
-    public:
-      virtual ~ILookupVisitor()
-      {
-      }
-
-      virtual bool IsDicomAsJsonNeeded() const = 0;
-      
-      virtual void MarkAsComplete() = 0;
-
-      // NB: "dicomAsJson" must *not* be deleted, and can be NULL if
-      // "!IsDicomAsJsonNeeded()"
-      virtual void Visit(const std::string& publicId,
-                         const std::string& instanceId,
-                         const DicomMap& mainDicomTags,
-                         const Json::Value* dicomAsJson) = 0;
-    };
-    
     struct StoreResult
     {
     private:
@@ -447,23 +428,6 @@
       return (level == ResourceType_Instance ? limitFindInstances_ : limitFindResults_);
     }
 
-    void Apply(ILookupVisitor& visitor,
-               const DatabaseLookup& lookup,
-               ResourceType queryLevel,
-               const std::set<std::string>& labels,
-               LabelsConstraint labelsConstraint,
-               size_t since,
-               size_t limit);
-
-    void Apply(ILookupVisitor& visitor,
-               const DatabaseLookup& lookup,
-               ResourceType queryLevel,
-               size_t since,
-               size_t limit)
-    {
-      Apply(visitor, lookup, queryLevel, std::set<std::string>(), LabelsConstraint_All, since, limit);
-    }
-
     bool LookupOrReconstructMetadata(std::string& target,
                                      const std::string& publicId,
                                      ResourceType level,