changeset 714:6a1dbba0cca7

new implementation of C-Find handler
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 14 Feb 2014 11:26:12 +0100
parents 9d1973813d8b
children 78239878727a
files OrthancServer/OrthancFindRequestHandler.cpp OrthancServer/ServerIndex.cpp OrthancServer/ServerIndex.h
diffstat 3 files changed, 213 insertions(+), 75 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/OrthancFindRequestHandler.cpp	Fri Feb 14 09:21:23 2014 +0100
+++ b/OrthancServer/OrthancFindRequestHandler.cpp	Fri Feb 14 11:26:12 2014 +0100
@@ -286,90 +286,152 @@
   }
 
 
-  static bool LookupCandidateResourcesInternal(/* out */ std::list<std::string>& resources,
-                                               /* in */  ServerIndex& index,
-                                               /* in */  ResourceType level,
-                                               /* in */  const DicomMap& query,
-                                               /* in */  DicomTag tag)
+  namespace
   {
-    if (query.HasTag(tag))
+    class CandidateResources
     {
-      const DicomValue& value = query.GetValue(tag);
-      if (!value.IsNull())
+    private:
+      ServerIndex&  index_;
+      ModalityManufacturer manufacturer_;
+      ResourceType  level_;
+      bool  isFilterApplied_;
+      std::set<std::string>  filtered_;
+
+      static void ListToSet(std::set<std::string>& target,
+                            const std::list<std::string>& source)
       {
-        std::string str = query.GetValue(tag).AsString();
-        if (!IsWildcard(str))
+        for (std::list<std::string>::const_iterator
+               it = source.begin(); it != source.end(); it++)
         {
-          index.LookupTagValue(resources, tag, str/*, level*/);
-          return true;
+          target.insert(*it);
         }
       }
-    }
-
-    return false;
-  }
-
 
-  static void LookupCandidateResources(/* out */ std::list<std::string>& resources,
-                                       /* in */  ServerIndex& index,
-                                       /* in */  ResourceType level,
-                                       /* in */  const DicomMap& query,
-                                       /* in */  ModalityManufacturer manufacturer)
-  {
-    // TODO : Speed up using full querying against the MainDicomTags.
+      void ApplyExactFilter(const DicomTag& tag, const std::string& value)
+      {
+        LOG(INFO) << "Applying exact filter on tag "
+                  << FromDcmtkBridge::GetName(tag) << " (value: " << value << ")";
 
-    resources.clear();
-
-    bool done = false;
+        std::list<std::string> resources;
+        index_.LookupTagValue(resources, tag, value, level_);
 
-    switch (level)
-    {
-      case ResourceType_Patient:
-        done = LookupCandidateResourcesInternal(resources, index, level, query, DICOM_TAG_PATIENT_ID);
-        break;
+        if (isFilterApplied_)
+        {
+          std::set<std::string>  s;
+          ListToSet(s, resources);
 
-      case ResourceType_Study:
-        done = LookupCandidateResourcesInternal(resources, index, level, query, DICOM_TAG_STUDY_INSTANCE_UID);
-        break;
+          std::set<std::string> tmp = filtered_;
+          filtered_.clear();
 
-      case ResourceType_Series:
-        done = LookupCandidateResourcesInternal(resources, index, level, query, DICOM_TAG_SERIES_INSTANCE_UID);
-        break;
-
-      case ResourceType_Instance:
-        if (manufacturer == ModalityManufacturer_MedInria)
-        {
-          std::list<std::string> series;
-
-          if (LookupCandidateResourcesInternal(series, index, ResourceType_Series, query, DICOM_TAG_SERIES_INSTANCE_UID) &&
-              series.size() == 1)
+          for (std::set<std::string>::const_iterator 
+                 it = tmp.begin(); it != tmp.end(); it++)
           {
-            index.GetChildInstances(resources, series.front());
-            done = true;
-          }          
+            if (s.find(*it) != s.end())
+            {
+              filtered_.insert(*it);
+            }
+          }
         }
         else
         {
-          done = LookupCandidateResourcesInternal(resources, index, level, query, DICOM_TAG_SOP_INSTANCE_UID);
+          assert(filtered_.empty());
+          isFilterApplied_ = true;
+          ListToSet(filtered_, resources);
+        }
+      }
+
+    public:
+      CandidateResources(ServerIndex& index,
+                         ModalityManufacturer manufacturer) : 
+        index_(index), 
+        manufacturer_(manufacturer),
+        level_(ResourceType_Patient), 
+        isFilterApplied_(false)
+      {
+      }
+
+      ResourceType GetLevel() const
+      {
+        return level_;
+      }
+
+      void GoDown()
+      {
+        assert(level_ != ResourceType_Instance);
+
+        if (isFilterApplied_)
+        {
+          std::set<std::string> tmp = filtered_;
+
+          filtered_.clear();
+
+          for (std::set<std::string>::const_iterator 
+                 it = tmp.begin(); it != tmp.end(); it++)
+          {
+            std::list<std::string> children;
+            index_.GetChildren(children, *it);
+            ListToSet(filtered_, children);
+          }
         }
 
-        break;
+        switch (level_)
+        {
+          case ResourceType_Patient:
+            level_ = ResourceType_Study;
+            break;
+
+          case ResourceType_Study:
+            level_ = ResourceType_Series;
+            break;
 
-      default:
-        break;
-    }
+          case ResourceType_Series:
+            level_ = ResourceType_Instance;
+            break;
+
+          default:
+            throw OrthancException(ErrorCode_InternalError);
+        }
+      }
+
+      void Flatten(std::list<std::string>& resources) const
+      {
+        resources.clear();
 
-    if (!done)
-    {
-      Json::Value allResources;
-      index.GetAllUuids(allResources, level);
-      assert(allResources.type() == Json::arrayValue);
+        if (isFilterApplied_)
+        {
+          for (std::set<std::string>::const_iterator 
+                 it = filtered_.begin(); it != filtered_.end(); it++)
+          {
+            resources.push_back(*it);
+          }
+        }
+        else
+        {
+          Json::Value tmp;
+          index_.GetAllUuids(tmp, level_);
+          for (Json::Value::ArrayIndex i = 0; i < tmp.size(); i++)
+          {
+            resources.push_back(tmp[i].asString());
+          }
+        }
+      }
 
-      for (Json::Value::ArrayIndex i = 0; i < allResources.size(); i++)
+      void ApplyFilter(const DicomTag& tag, const DicomMap& query)
       {
-        resources.push_back(allResources[i].asString());
+        if (query.HasTag(tag))
+        {
+          const DicomValue& value = query.GetValue(tag);
+          if (!value.IsNull())
+          {
+            std::string value = query.GetValue(tag).AsString();
+            if (!IsWildcard(value))
+            {
+              ApplyExactFilter(tag, value);
+            }
+          }
+        }
       }
-    }
+    };
   }
 
 
@@ -406,19 +468,26 @@
 
     ResourceType level = StringToResourceType(levelTmp->AsString().c_str());
 
-    switch (manufacturer)
+    if (level != ResourceType_Patient &&
+        level != ResourceType_Study &&
+        level != ResourceType_Series &&
+        level != ResourceType_Instance)
     {
-      case ModalityManufacturer_MedInria:
-        // MedInria makes FIND requests at the instance level before starting MOVE
-        break;
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
 
-      default:
-        if (level != ResourceType_Patient &&
-            level != ResourceType_Study &&
-            level != ResourceType_Series)
-        {
-          throw OrthancException(ErrorCode_NotImplemented);
-        }
+    DicomArray query(input);
+    LOG(INFO) << "DICOM C-Find request at level: " << EnumerationToString(level);
+
+    for (size_t i = 0; i < query.GetSize(); i++)
+    {
+      if (!query.GetElement(i).GetValue().IsNull())
+      {
+        LOG(INFO) << "  " << query.GetElement(i).GetTag()
+                  << "  " << FromDcmtkBridge::GetName(query.GetElement(i).GetTag())
+                  << " = " << query.GetElement(i).GetValue().AsString();
+      }
     }
 
 
@@ -429,9 +498,45 @@
      * for each of them.
      **/
 
+    CandidateResources candidates(context_.GetIndex(), manufacturer);
+
+    for (;;)
+    {
+      switch (candidates.GetLevel())
+      {
+        case ResourceType_Patient:
+          candidates.ApplyFilter(DICOM_TAG_PATIENT_ID, input);
+          break;
+
+        case ResourceType_Study:
+          candidates.ApplyFilter(DICOM_TAG_STUDY_INSTANCE_UID, input);
+          candidates.ApplyFilter(DICOM_TAG_ACCESSION_NUMBER, input);
+          break;
+
+        case ResourceType_Series:
+          candidates.ApplyFilter(DICOM_TAG_SERIES_INSTANCE_UID, input);
+          break;
+
+        case ResourceType_Instance:
+          candidates.ApplyFilter(DICOM_TAG_SOP_INSTANCE_UID, input);
+          break;
+
+        default:
+          throw OrthancException(ErrorCode_InternalError);
+      }      
+
+      if (candidates.GetLevel() == level)
+      {
+        break;
+      }
+
+      candidates.GoDown();
+    }
+
     std::list<std::string>  resources;
-    LookupCandidateResources(resources, context_.GetIndex(), level, input, manufacturer);
+    candidates.Flatten(resources);
 
+    LOG(INFO) << "Number of candidate resources after exact filtering: " << resources.size();
 
     /**
      * Apply filtering on modalities for studies, if asked (this is an
@@ -454,7 +559,6 @@
      * Loop over all the resources for this query level.
      **/
 
-    DicomArray query(input);
     for (std::list<std::string>::const_iterator 
            resource = resources.begin(); resource != resources.end(); ++resource)
     {
--- a/OrthancServer/ServerIndex.cpp	Fri Feb 14 09:21:23 2014 +0100
+++ b/OrthancServer/ServerIndex.cpp	Fri Feb 14 11:26:12 2014 +0100
@@ -1107,6 +1107,37 @@
   }
 
 
+  void ServerIndex::GetChildren(std::list<std::string>& result,
+                                const std::string& publicId)
+  {
+    result.clear();
+
+    boost::mutex::scoped_lock lock(mutex_);
+
+    ResourceType type;
+    int64_t resource;
+    if (!db_->LookupResource(publicId, resource, type))
+    {
+      throw OrthancException(ErrorCode_UnknownResource);
+    }
+
+    if (type == ResourceType_Instance)
+    {
+      // An instance cannot have a child
+      throw OrthancException(ErrorCode_BadParameterType);
+    }
+
+    std::list<int64_t> tmp;
+    db_->GetChildrenInternalId(tmp, resource);
+
+    for (std::list<int64_t>::const_iterator 
+           it = tmp.begin(); it != tmp.end(); ++it)
+    {
+      result.push_back(db_->GetPublicId(*it));
+    }
+  }
+
+
   void ServerIndex::GetChildInstances(std::list<std::string>& result,
                                       const std::string& publicId)
   {
--- a/OrthancServer/ServerIndex.h	Fri Feb 14 09:21:23 2014 +0100
+++ b/OrthancServer/ServerIndex.h	Fri Feb 14 11:26:12 2014 +0100
@@ -163,6 +163,9 @@
     void SetProtectedPatient(const std::string& publicId,
                              bool isProtected);
 
+    void GetChildren(std::list<std::string>& result,
+                     const std::string& publicId);
+
     void GetChildInstances(std::list<std::string>& result,
                            const std::string& publicId);