changeset 1750:55d52567bebb db-changes

LookupResource implemented
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 27 Oct 2015 12:45:50 +0100
parents 99f4a05f39fa
children fb569ee09a69
files Core/FileStorage/StorageAccessor.cpp Core/FileStorage/StorageAccessor.h OrthancServer/DatabaseWrapper.h OrthancServer/DatabaseWrapperBase.cpp OrthancServer/DatabaseWrapperBase.h OrthancServer/IDatabaseWrapper.h OrthancServer/Search/IFindConstraint.h OrthancServer/Search/LookupIdentifierQuery.cpp OrthancServer/Search/LookupIdentifierQuery.h OrthancServer/Search/LookupResource.cpp OrthancServer/Search/LookupResource.h OrthancServer/Search/SetOfResources.cpp OrthancServer/Search/SetOfResources.h OrthancServer/ServerIndex.cpp OrthancServer/ServerIndex.h Plugins/Engine/OrthancPluginDatabase.cpp Plugins/Engine/OrthancPluginDatabase.h
diffstat 17 files changed, 365 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/Core/FileStorage/StorageAccessor.cpp	Tue Oct 27 10:54:51 2015 +0100
+++ b/Core/FileStorage/StorageAccessor.cpp	Tue Oct 27 12:45:50 2015 +0100
@@ -126,6 +126,20 @@
   }
 
 
+  void StorageAccessor::Read(Json::Value& content,
+                             const FileInfo& info)
+  {
+    std::string s;
+    Read(s, info);
+
+    Json::Reader reader;
+    if (!reader.parse(s, content))
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
+
   void StorageAccessor::SetupSender(BufferHttpSender& sender,
                                     const FileInfo& info)
   {
--- a/Core/FileStorage/StorageAccessor.h	Tue Oct 27 10:54:51 2015 +0100
+++ b/Core/FileStorage/StorageAccessor.h	Tue Oct 27 12:45:50 2015 +0100
@@ -41,6 +41,7 @@
 #include <string>
 #include <boost/noncopyable.hpp>
 #include <stdint.h>
+#include <json/value.h>
 
 namespace Orthanc
 {
@@ -75,6 +76,9 @@
     void Read(std::string& content,
               const FileInfo& info);
 
+    void Read(Json::Value& content,
+              const FileInfo& info);
+
     void Remove(const FileInfo& info)
     {
       area_.Remove(info.GetUuid(), info.GetContentType());
--- a/OrthancServer/DatabaseWrapper.h	Tue Oct 27 10:54:51 2015 +0100
+++ b/OrthancServer/DatabaseWrapper.h	Tue Oct 27 12:45:50 2015 +0100
@@ -249,6 +249,12 @@
       return base_.GetResourceCount(resourceType);
     }
 
+    virtual void GetAllInternalIds(std::list<int64_t>& target,
+                                   ResourceType resourceType)
+    {
+      base_.GetAllInternalIds(target, resourceType);
+    }
+
     virtual void GetAllPublicIds(std::list<std::string>& target,
                                  ResourceType resourceType)
     {
--- a/OrthancServer/DatabaseWrapperBase.cpp	Tue Oct 27 10:54:51 2015 +0100
+++ b/OrthancServer/DatabaseWrapperBase.cpp	Tue Oct 27 12:45:50 2015 +0100
@@ -533,6 +533,20 @@
     return static_cast<uint64_t>(s.ColumnInt64(0));
   }
 
+  void DatabaseWrapperBase::GetAllInternalIds(std::list<int64_t>& target,
+                                              ResourceType resourceType)
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT internalId FROM Resources WHERE resourceType=?");
+    s.BindInt(0, resourceType);
+
+    target.clear();
+    while (s.Step())
+    {
+      target.push_back(s.ColumnInt64(0));
+    }
+  }
+
+
   void DatabaseWrapperBase::GetAllPublicIds(std::list<std::string>& target,
                                             ResourceType resourceType)
   {
--- a/OrthancServer/DatabaseWrapperBase.h	Tue Oct 27 10:54:51 2015 +0100
+++ b/OrthancServer/DatabaseWrapperBase.h	Tue Oct 27 12:45:50 2015 +0100
@@ -168,6 +168,9 @@
     
     uint64_t GetTotalUncompressedSize();
 
+    void GetAllInternalIds(std::list<int64_t>& target,
+                           ResourceType resourceType);
+
     void GetAllPublicIds(std::list<std::string>& target,
                          ResourceType resourceType);
 
--- a/OrthancServer/IDatabaseWrapper.h	Tue Oct 27 10:54:51 2015 +0100
+++ b/OrthancServer/IDatabaseWrapper.h	Tue Oct 27 12:45:50 2015 +0100
@@ -83,6 +83,9 @@
     virtual void GetAllMetadata(std::map<MetadataType, std::string>& target,
                                 int64_t id) = 0;
 
+    virtual void GetAllInternalIds(std::list<int64_t>& target,
+                                   ResourceType resourceType) = 0;
+
     virtual void GetAllPublicIds(std::list<std::string>& target,
                                  ResourceType resourceType) = 0;
 
--- a/OrthancServer/Search/IFindConstraint.h	Tue Oct 27 10:54:51 2015 +0100
+++ b/OrthancServer/Search/IFindConstraint.h	Tue Oct 27 12:45:50 2015 +0100
@@ -55,6 +55,8 @@
       return tag_;
     }
 
+    virtual IFindConstraint* Clone() const = 0;
+
     virtual void Setup(LookupIdentifierQuery& lookup) const = 0;
 
     virtual bool Match(const std::string& value) const = 0;
--- a/OrthancServer/Search/LookupIdentifierQuery.cpp	Tue Oct 27 10:54:51 2015 +0100
+++ b/OrthancServer/Search/LookupIdentifierQuery.cpp	Tue Oct 27 12:45:50 2015 +0100
@@ -203,7 +203,15 @@
                                     IDatabaseWrapper& database)
   {
     SetOfResources resources(database, level_);
-    
+    Apply(resources, database);
+
+    resources.Flatten(result);
+  }
+
+
+  void LookupIdentifierQuery::Apply(SetOfResources& result,
+                                    IDatabaseWrapper& database)
+  {
     for (size_t i = 0; i < GetSize(); i++)
     {
       std::list<int64_t> a;
@@ -217,9 +225,8 @@
         a.splice(a.end(), b);
       }
 
-      resources.Intersect(a);
+      result.Intersect(a);
     }
+  }
 
-    resources.Flatten(result);
-  }
 }
--- a/OrthancServer/Search/LookupIdentifierQuery.h	Tue Oct 27 10:54:51 2015 +0100
+++ b/OrthancServer/Search/LookupIdentifierQuery.h	Tue Oct 27 12:45:50 2015 +0100
@@ -35,6 +35,8 @@
 #include "../ServerEnumerations.h"
 #include "../IDatabaseWrapper.h"
 
+#include "SetOfResources.h"
+
 #include <vector>
 #include <boost/noncopyable.hpp>
 
@@ -161,6 +163,9 @@
     void Apply(std::list<std::string>& result,
                IDatabaseWrapper& database);
 
+    void Apply(SetOfResources& result,
+               IDatabaseWrapper& database);
+
     static void LoadIdentifiers(const DicomTag*& tags,
                                 size_t& size,
                                 ResourceType level);
--- a/OrthancServer/Search/LookupResource.cpp	Tue Oct 27 10:54:51 2015 +0100
+++ b/OrthancServer/Search/LookupResource.cpp	Tue Oct 27 12:45:50 2015 +0100
@@ -34,10 +34,13 @@
 #include "LookupResource.h"
 
 #include "../../Core/OrthancException.h"
+#include "../../Core/FileStorage/StorageAccessor.h"
+#include "../ServerToolbox.h"
+
 
 namespace Orthanc
 {
-  LookupResource::Level::Level(ResourceType level)
+  LookupResource::Level::Level(ResourceType level) : level_(level)
   {
     const DicomTag* tags = NULL;
     size_t size;
@@ -79,12 +82,30 @@
   {
     if (identifiers_.find(constraint->GetTag()) != identifiers_.end())
     {
-      identifiersConstraints_.push_back(constraint.release());
+      if (level_ == ResourceType_Patient)
+      {
+        // The filters on the patient level must be cloned to the study level
+        identifiersConstraints_.push_back(constraint->Clone());
+      }
+      else
+      {
+        identifiersConstraints_.push_back(constraint.release());
+      }
+
       return true;
     }
     else if (mainTags_.find(constraint->GetTag()) != mainTags_.end())
     {
-      mainTagsConstraints_.push_back(constraint.release());
+      if (level_ == ResourceType_Patient)
+      {
+        // The filters on the patient level must be cloned to the study level
+        mainTagsConstraints_.push_back(constraint->Clone());
+      }
+      else
+      {
+        mainTagsConstraints_.push_back(constraint.release());
+      }
+
       return true;
     }
     else
@@ -167,28 +188,220 @@
   }
 
 
-  static int64_t ChooseOneInstance(IDatabaseWrapper& database,
-                                   int64_t parent,
-                                   ResourceType type)
+  static bool Match(const DicomMap& tags,
+                    const IFindConstraint& constraint)
   {
-    for (;;)
+    const DicomValue* value = tags.TestAndGetValue(constraint.GetTag());
+
+    if (value == NULL ||
+        value->IsNull() ||
+        value->IsBinary())
+    {
+      return false;
+    }
+    else
     {
-      if (type == ResourceType_Instance)
-      {
-        return parent;
-      }
+      return constraint.Match(value->GetContent());
+    }
+  }
+
+
+  void LookupResource::Level::Apply(SetOfResources& candidates,
+                                    IDatabaseWrapper& database) const
+  {
+    // First, use the indexed identifiers
+    LookupIdentifierQuery query(level_);
+
+    for (Constraints::const_iterator it = identifiersConstraints_.begin(); 
+         it != identifiersConstraints_.end(); ++it)
+    {
+      (*it)->Setup(query);
+    }
+
+    query.Apply(candidates, database);
 
-      std::list<int64_t>  children;
-      database.GetChildrenInternalId(children, parent);
+    // Secondly, filter using the main DICOM tags
+    if (!identifiersConstraints_.empty() ||
+        !mainTagsConstraints_.empty())
+    {
+      std::list<int64_t>  source;
+      candidates.Flatten(source);
+      candidates.Clear();
 
-      if (children.empty())
+      std::list<int64_t>  filtered;
+      for (std::list<int64_t>::const_iterator candidate = source.begin(); 
+           candidate != source.end(); ++candidate)
       {
-        throw OrthancException(ErrorCode_InternalError);
-      }
+        DicomMap tags;
+        database.GetMainDicomTags(tags, *candidate);
+
+        bool match = true;
+
+        // Re-apply the identifier constraints, as their "Setup"
+        // method is less restrictive than their "Match" method
+        for (Constraints::const_iterator it = identifiersConstraints_.begin(); 
+             match && it != identifiersConstraints_.end(); ++it)
+        {
+          if (!Match(tags, **it))
+          {
+            match = false;
+          }
+        }
 
-      parent = children.front();
-      type = GetChildResourceType(type);
+        for (Constraints::const_iterator it = mainTagsConstraints_.begin(); 
+             match && it != mainTagsConstraints_.end(); ++it)
+        {
+          if (!Match(tags, **it))
+          {
+            match = false;
+          }
+        }
+
+        if (match)
+        {
+          filtered.push_back(*candidate);
+        }
+      }
+      
+      candidates.Intersect(filtered);
     }
   }
 
+
+
+  void LookupResource::ApplyUnoptimizedConstraints(SetOfResources& candidates,
+                                                   IDatabaseWrapper& database,
+                                                   IStorageArea& storageArea) const
+  {
+    if (unoptimizedConstraints_.empty())
+    {
+      // Nothing to do
+      return;
+    }
+
+    std::list<int64_t>  source;
+    candidates.Flatten(source);
+    candidates.Clear();
+
+    StorageAccessor accessor(storageArea);
+
+    std::list<int64_t>  filtered;
+    for (std::list<int64_t>::const_iterator candidate = source.begin(); 
+         candidate != source.end(); ++candidate)
+    {
+      if (maxResults_ != 0 &&
+          filtered.size() >= maxResults_)
+      {
+        // We have enough results
+        break;
+      }
+
+      int64_t instance;
+      FileInfo attachment;
+      if (!Toolbox::FindOneChildInstance(instance, database, *candidate, level_) ||
+          !database.LookupAttachment(attachment, instance, FileContentType_DicomAsJson))
+      {
+        continue;
+      }
+
+      Json::Value content;
+      accessor.Read(content, attachment);
+
+      bool match = true;
+
+      for (Constraints::const_iterator it = unoptimizedConstraints_.begin(); 
+           match && it != unoptimizedConstraints_.end(); ++it)
+      {
+        std::string tag = (*it)->GetTag().Format();
+        if (content.isMember(tag) &&
+            content[tag]["Type"] == "String")
+        {
+          std::string value = content[tag]["Value"].asString();
+          if (!(*it)->Match(value))
+          {
+            match = false;
+          }
+        }
+        else
+        {
+          match = false;
+        }
+      }
+
+      if (match)
+      {
+        filtered.push_back(*candidate);
+      }
+    }
+
+    candidates.Intersect(filtered);
+  }
+
+
+  void LookupResource::ApplyLevel(SetOfResources& candidates,
+                                  ResourceType level,
+                                  IDatabaseWrapper& database) const
+  {
+    Levels::const_iterator it = levels_.find(level);
+    if (it != levels_.end())
+    {
+      it->second->Apply(candidates, database);
+    }
+  }
+
+
+  void LookupResource::Apply(std::list<int64_t>& result,
+                             IDatabaseWrapper& database,
+                             IStorageArea& storageArea) const
+  {
+    SetOfResources candidates(database, level_);
+
+    switch (level_)
+    {
+      case ResourceType_Patient:
+        ApplyLevel(candidates, ResourceType_Patient, database);
+        break;
+
+      case ResourceType_Study:
+        ApplyLevel(candidates, ResourceType_Study, database);
+        break;
+
+      case ResourceType_Series:
+        ApplyLevel(candidates, ResourceType_Study, database);
+        candidates.GoDown();
+        ApplyLevel(candidates, ResourceType_Series, database);
+        break;
+
+      case ResourceType_Instance:
+        ApplyLevel(candidates, ResourceType_Study, database);
+        candidates.GoDown();
+        ApplyLevel(candidates, ResourceType_Series, database);
+        candidates.GoDown();
+        ApplyLevel(candidates, ResourceType_Instance, database);
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+
+    ApplyUnoptimizedConstraints(candidates, database, storageArea);
+    candidates.Flatten(result);
+  }
+
+
+  void LookupResource::Apply(std::list<std::string>& result,
+                             IDatabaseWrapper& database,
+                             IStorageArea& storageArea) const
+  {
+    std::list<int64_t> tmp;
+    Apply(tmp, database, storageArea);
+
+    result.clear();
+
+    for (std::list<int64_t>::const_iterator
+           it = tmp.begin(); it != tmp.end(); ++it)
+    {
+      result.push_back(database.GetPublicId(*it));
+    }
+  }
 }
--- a/OrthancServer/Search/LookupResource.h	Tue Oct 27 10:54:51 2015 +0100
+++ b/OrthancServer/Search/LookupResource.h	Tue Oct 27 12:45:50 2015 +0100
@@ -47,6 +47,7 @@
     class Level
     {
     private:
+      ResourceType        level_;
       std::set<DicomTag>  identifiers_;
       std::set<DicomTag>  mainTags_;
       Constraints         identifiersConstraints_;
@@ -58,6 +59,9 @@
       ~Level();
 
       bool Add(std::auto_ptr<IFindConstraint>& constraint);
+
+      void Apply(SetOfResources& candidates,
+                 IDatabaseWrapper& database) const;
     };
 
     typedef std::map<ResourceType, Level*>  Levels;
@@ -70,7 +74,13 @@
     bool AddInternal(ResourceType level,
                      std::auto_ptr<IFindConstraint>& constraint);
 
-    void ApplyUnoptimizedConstraints(SetOfResources& result);
+    void ApplyLevel(SetOfResources& candidates,
+                    ResourceType level,
+                    IDatabaseWrapper& database) const;
+
+    void ApplyUnoptimizedConstraints(SetOfResources& candidates,
+                                     IDatabaseWrapper& database,
+                                     IStorageArea& storageArea) const;
 
   public:
     LookupResource(ResourceType level);
@@ -88,5 +98,13 @@
     {
       return maxResults_;
     }
+
+    void Apply(std::list<int64_t>& result,
+               IDatabaseWrapper& database,
+               IStorageArea& storageArea) const;
+
+    void Apply(std::list<std::string>& result,
+               IDatabaseWrapper& database,
+               IStorageArea& storageArea) const;
   };
 }
--- a/OrthancServer/Search/SetOfResources.cpp	Tue Oct 27 10:54:51 2015 +0100
+++ b/OrthancServer/Search/SetOfResources.cpp	Tue Oct 27 12:45:50 2015 +0100
@@ -130,4 +130,24 @@
       }
     }
   }
+
+
+  void SetOfResources::Flatten(std::list<int64_t>& result)
+  {
+    result.clear();
+      
+    if (resources_.get() == NULL)
+    {
+      // All the resources of this level are part of the filter
+      database_.GetAllInternalIds(result, level_);
+    }
+    else
+    {
+      for (Resources::const_iterator it = resources_->begin(); 
+           it != resources_->end(); ++it)
+      {
+        result.push_back(*it);
+      }
+    }
+  }
 }
--- a/OrthancServer/Search/SetOfResources.h	Tue Oct 27 10:54:51 2015 +0100
+++ b/OrthancServer/Search/SetOfResources.h	Tue Oct 27 12:45:50 2015 +0100
@@ -66,6 +66,13 @@
 
     void GoDown();
 
+    void Flatten(std::list<int64_t>& result);
+
     void Flatten(std::list<std::string>& result);
+
+    void Clear()
+    {
+      resources_.reset(NULL);
+    }
   };
 }
--- a/OrthancServer/ServerIndex.cpp	Tue Oct 27 10:54:51 2015 +0100
+++ b/OrthancServer/ServerIndex.cpp	Tue Oct 27 12:45:50 2015 +0100
@@ -46,6 +46,7 @@
 #include "../Core/Uuid.h"
 #include "../Core/DicomFormat/DicomArray.h"
 #include "Search/LookupIdentifierQuery.h"
+#include "Search/LookupResource.h"
 
 #include "FromDcmtkBridge.h"
 #include "ServerContext.h"
@@ -2112,4 +2113,13 @@
     boost::mutex::scoped_lock lock(mutex_);
     return db_.GetDatabaseVersion();
   }
+
+
+  void ServerIndex::Apply(std::list<std::string>& result,
+                          ::Orthanc::LookupResource& lookup,
+                          IStorageArea& area)
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+    lookup.Apply(result, db_, area);
+  }
 }
--- a/OrthancServer/ServerIndex.h	Tue Oct 27 10:54:51 2015 +0100
+++ b/OrthancServer/ServerIndex.h	Tue Oct 27 12:45:50 2015 +0100
@@ -45,6 +45,7 @@
 
 namespace Orthanc
 {
+  class LookupResource;
   class ServerContext;
 
   class ServerIndex : public boost::noncopyable
@@ -261,5 +262,9 @@
                             const std::string& publicId);
 
     unsigned int GetDatabaseVersion();
+
+    void Apply(std::list<std::string>& result,
+               ::Orthanc::LookupResource& lookup,
+               IStorageArea& area);
   };
 }
--- a/Plugins/Engine/OrthancPluginDatabase.cpp	Tue Oct 27 10:54:51 2015 +0100
+++ b/Plugins/Engine/OrthancPluginDatabase.cpp	Tue Oct 27 12:45:50 2015 +0100
@@ -274,6 +274,14 @@
   }
 
 
+  void OrthancPluginDatabase::GetAllInternalIds(std::list<int64_t>& target,
+                                                ResourceType resourceType)
+  {
+    // TODO
+    throw OrthancException(ErrorCode_NotImplemented);
+  }
+
+
   void OrthancPluginDatabase::GetAllPublicIds(std::list<std::string>& target,
                                               ResourceType resourceType)
   {
--- a/Plugins/Engine/OrthancPluginDatabase.h	Tue Oct 27 10:54:51 2015 +0100
+++ b/Plugins/Engine/OrthancPluginDatabase.h	Tue Oct 27 12:45:50 2015 +0100
@@ -140,6 +140,9 @@
     virtual void GetAllMetadata(std::map<MetadataType, std::string>& target,
                                 int64_t id);
 
+    virtual void GetAllInternalIds(std::list<int64_t>& target,
+                                   ResourceType resourceType);
+
     virtual void GetAllPublicIds(std::list<std::string>& target,
                                  ResourceType resourceType);