changeset 1358:62d2d35b725e

cont
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 13 May 2015 17:56:27 +0200
parents 216db29c5aa9
children 4378a6636187
files CMakeLists.txt OrthancServer/BaseResourceFinder.cpp OrthancServer/BaseResourceFinder.h OrthancServer/ExactResourceFinder.cpp OrthancServer/ExactResourceFinder.h OrthancServer/OrthancRestApi/OrthancRestResources.cpp OrthancServer/ResourceFinder.cpp OrthancServer/ResourceFinder.h
diffstat 8 files changed, 664 insertions(+), 533 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Wed May 13 15:56:41 2015 +0200
+++ b/CMakeLists.txt	Wed May 13 17:56:27 2015 +0200
@@ -170,7 +170,8 @@
   OrthancServer/OrthancFindRequestHandler.cpp
   OrthancServer/OrthancMoveRequestHandler.cpp
   OrthancServer/ExportedResource.cpp
-  OrthancServer/ExactResourceFinder.cpp
+  OrthancServer/BaseResourceFinder.cpp
+  OrthancServer/ResourceFinder.cpp
 
   # From "lua-scripting" branch
   OrthancServer/DicomInstanceToStore.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/BaseResourceFinder.cpp	Wed May 13 17:56:27 2015 +0200
@@ -0,0 +1,397 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PrecompiledHeadersServer.h"
+#include "BaseResourceFinder.h"
+
+#include "FromDcmtkBridge.h"
+#include "ServerContext.h"
+
+#include <glog/logging.h>
+#include <boost/algorithm/string/predicate.hpp>
+
+namespace Orthanc
+{
+  class BaseResourceFinder::CandidateResources
+  {
+  private:
+    typedef std::map<DicomTag, std::string>  Query;
+
+    BaseResourceFinder&   finder_;
+    ServerIndex&           index_;
+    ResourceType           level_;
+    bool                   isFilterApplied_;
+    std::set<std::string>  filtered_;
+
+     
+    static void ListToSet(std::set<std::string>& target,
+                          const std::list<std::string>& source)
+    {
+      for (std::list<std::string>::const_iterator
+             it = source.begin(); it != source.end(); ++it)
+      {
+        target.insert(*it);
+      }
+    }
+
+
+    void RestrictIdentifier(const DicomTag& tag,
+                            const std::string& value)
+    {
+      assert((level_ == ResourceType_Patient && tag == DICOM_TAG_PATIENT_ID) ||
+             (level_ == ResourceType_Study && tag == DICOM_TAG_STUDY_INSTANCE_UID) ||
+             (level_ == ResourceType_Study && tag == DICOM_TAG_ACCESSION_NUMBER) ||
+             (level_ == ResourceType_Series && tag == DICOM_TAG_SERIES_INSTANCE_UID) ||
+             (level_ == ResourceType_Instance && tag == DICOM_TAG_SOP_INSTANCE_UID));
+
+      LOG(INFO) << "Lookup for identifier tag "
+                << FromDcmtkBridge::GetName(tag) << " (value: " << value << ")";
+
+      std::list<std::string> resources;
+      index_.LookupIdentifier(resources, tag, value, level_);
+
+      if (isFilterApplied_)
+      {
+        std::set<std::string>  s;
+        ListToSet(s, resources);
+
+        std::set<std::string> tmp = filtered_;
+        filtered_.clear();
+
+        for (std::set<std::string>::const_iterator 
+               it = tmp.begin(); it != tmp.end(); ++it)
+        {
+          if (s.find(*it) != s.end())
+          {
+            filtered_.insert(*it);
+          }
+        }
+      }
+      else
+      {
+        assert(filtered_.empty());
+        isFilterApplied_ = true;
+        ListToSet(filtered_, resources);
+      }
+    }
+
+
+  public:
+    CandidateResources(BaseResourceFinder& finder) : 
+      finder_(finder),
+      index_(finder.context_.GetIndex()),
+      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;
+          try
+          {
+            index_.GetChildren(children, *it);
+            ListToSet(filtered_, children);
+          }
+          catch (OrthancException&)
+          {
+            // The resource was removed in the meantime
+          }
+        }
+      }
+
+      switch (level_)
+      {
+        case ResourceType_Patient:
+          level_ = ResourceType_Study;
+          break;
+
+        case ResourceType_Study:
+          level_ = ResourceType_Series;
+          break;
+
+        case ResourceType_Series:
+          level_ = ResourceType_Instance;
+          break;
+
+        default:
+          throw OrthancException(ErrorCode_InternalError);
+      }
+    }
+
+
+    void Flatten(std::list<std::string>& resources) const
+    {
+      resources.clear();
+
+      if (isFilterApplied_)
+      {
+        for (std::set<std::string>::const_iterator 
+               it = filtered_.begin(); it != filtered_.end(); ++it)
+        {
+          resources.push_back(*it);
+        }
+      }
+      else
+      {
+        index_.GetAllUuids(resources, level_);
+      }
+    }
+
+    
+    void RestrictIdentifier(const DicomTag& tag)
+    {
+      Identifiers::const_iterator it = finder_.identifiers_.find(tag);
+      if (it != finder_.identifiers_.end())
+      {
+        RestrictIdentifier(it->first, it->second);
+      }
+    }
+
+
+    void RestrictMainDicomTags()
+    {
+      if (finder_.mainTagsFilter_ == NULL)
+      {
+        return;
+      }
+
+      std::list<std::string> resources;
+      Flatten(resources);
+
+      isFilterApplied_ = true;
+      filtered_.clear();
+
+      for (std::list<std::string>::const_iterator
+             it = resources.begin(); it != resources.end(); it++)
+      {
+        DicomMap mainTags;
+        if (index_.GetMainDicomTags(mainTags, *it, level_))
+        {
+          if (finder_.mainTagsFilter_->Apply(mainTags, level_))
+          {
+            filtered_.insert(*it);
+          }
+        }
+      }
+    }
+  };
+
+
+  BaseResourceFinder::BaseResourceFinder(ServerContext& context) : 
+    context_(context),
+    level_(ResourceType_Patient),
+    maxResults_(0)
+  {
+  }
+
+
+  void BaseResourceFinder::ApplyAtLevel(CandidateResources& candidates,
+                                        ResourceType level)
+  {
+    if (level != ResourceType_Patient)
+    {
+      candidates.GoDown();
+    }
+
+    switch (level_)
+    {
+      case ResourceType_Patient:
+      {
+        candidates.RestrictIdentifier(DICOM_TAG_PATIENT_ID);
+        break;
+      }
+
+      case ResourceType_Study:
+      {
+        candidates.RestrictIdentifier(DICOM_TAG_STUDY_INSTANCE_UID);
+        candidates.RestrictIdentifier(DICOM_TAG_ACCESSION_NUMBER);
+        break;
+      }
+
+      case ResourceType_Series:
+      {
+        candidates.RestrictIdentifier(DICOM_TAG_SERIES_INSTANCE_UID);
+        break;
+      }
+
+      case ResourceType_Instance:
+      {
+        candidates.RestrictIdentifier(DICOM_TAG_SOP_INSTANCE_UID);
+        break;
+      }
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+
+    candidates.RestrictMainDicomTags();
+  }
+
+
+
+  void BaseResourceFinder::SetIdentifier(const DicomTag& tag,
+                                         const std::string& value)
+  {
+    assert((level_ >= ResourceType_Patient && tag == DICOM_TAG_PATIENT_ID) ||
+           (level_ >= ResourceType_Study && tag == DICOM_TAG_STUDY_INSTANCE_UID) ||
+           (level_ >= ResourceType_Study && tag == DICOM_TAG_ACCESSION_NUMBER) ||
+           (level_ >= ResourceType_Series && tag == DICOM_TAG_SERIES_INSTANCE_UID) ||
+           (level_ >= ResourceType_Instance && tag == DICOM_TAG_SOP_INSTANCE_UID));
+
+    identifiers_[tag] = value;
+  }
+
+
+  static bool LookupOneInstance(std::string& result,
+                                ServerIndex& index,
+                                const std::string& id,
+                                ResourceType type)
+  {
+    if (type == ResourceType_Instance)
+    {
+      result = id;
+      return true;
+    }
+
+    std::string childId;
+    
+    {
+      std::list<std::string> children;
+      index.GetChildInstances(children, id);
+
+      if (children.empty())
+      {
+        return false;
+      }
+
+      childId = children.front();
+    }
+
+    return LookupOneInstance(result, index, childId, GetChildResourceType(type));
+  }
+
+
+  bool BaseResourceFinder::Apply(std::list<std::string>& result)
+  {
+    CandidateResources candidates(*this);
+
+    ApplyAtLevel(candidates, ResourceType_Patient);
+
+    if (level_ == ResourceType_Study ||
+        level_ == ResourceType_Series ||
+        level_ == ResourceType_Instance)
+    {
+      ApplyAtLevel(candidates, ResourceType_Study);
+    }
+        
+    if (level_ == ResourceType_Series ||
+        level_ == ResourceType_Instance)
+    {
+      ApplyAtLevel(candidates, ResourceType_Series);
+    }
+        
+    if (level_ == ResourceType_Instance)
+    {
+      ApplyAtLevel(candidates, ResourceType_Instance);
+    }
+
+    if (instanceFilter_ == NULL)
+    {
+      candidates.Flatten(result);
+
+      if (maxResults_ != 0 &&
+          result.size() >= maxResults_)
+      {
+        result.resize(maxResults_);
+        return false;
+      }
+      else
+      {
+        return true;
+      }
+    }
+    else
+    {
+      std::list<std::string> tmp;
+      candidates.Flatten(tmp);
+      
+      result.clear();
+      for (std::list<std::string>::const_iterator 
+             resource = tmp.begin(); resource != tmp.end(); ++resource)
+      {
+        try
+        {
+          std::string instance;
+          if (LookupOneInstance(instance, context_.GetIndex(), *resource, level_))
+          {
+            Json::Value content;
+            context_.ReadJson(content, instance);
+            if (instanceFilter_->Apply(*resource, content))
+            {
+              result.push_back(*resource);
+
+              if (maxResults_ != 0 &&
+                  result.size() >= maxResults_)
+              {
+                // Too many results, stop before recording this new match
+                return false;
+              }
+            }
+          }
+        }
+        catch (OrthancException&)
+        {
+          // This resource has been deleted since the search was started
+        }
+      }      
+    }
+
+    return true;  // All the matching resources have been returned
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/BaseResourceFinder.h	Wed May 13 17:56:27 2015 +0200
@@ -0,0 +1,125 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ServerIndex.h"
+
+#include <boost/noncopyable.hpp>
+
+namespace Orthanc
+{
+  class BaseResourceFinder : public boost::noncopyable
+  {
+  public:
+    class IMainTagsFilter : public boost::noncopyable
+    {
+    public:
+      virtual ~IMainTagsFilter()
+      {
+      }
+
+      virtual bool Apply(const DicomMap& mainTags,
+                         ResourceType level) = 0;
+    };
+
+
+    class IInstanceFilter : public boost::noncopyable
+    {
+    public:
+      virtual ~IInstanceFilter()
+      {
+      }
+
+      virtual bool Apply(const std::string& instanceId,
+                         const Json::Value& content) = 0;
+    };
+
+
+  private:
+    typedef std::map<DicomTag, std::string>  Identifiers;
+
+    class CandidateResources;
+
+    ServerContext&    context_;
+    ResourceType      level_;
+    size_t            maxResults_;
+    Identifiers       identifiers_;
+    IMainTagsFilter  *mainTagsFilter_;
+    IInstanceFilter  *instanceFilter_;
+
+    void ApplyAtLevel(CandidateResources& candidates,
+                      ResourceType level);
+
+  public:
+    BaseResourceFinder(ServerContext& context);
+
+    ResourceType GetLevel() const
+    {
+      return level_;
+    }
+
+    void SetLevel(ResourceType level)
+    {
+      level_ = level;
+    }
+
+    void SetIdentifier(const DicomTag& tag,
+                       const std::string& value);
+
+    void SetMainTagsFilter(IMainTagsFilter& filter)
+    {
+      mainTagsFilter_ = &filter;
+    }
+
+    void SetInstanceFilter(IInstanceFilter& filter)
+    {
+      instanceFilter_ = &filter;
+    }
+
+    void SetMaxResults(size_t value)
+    {
+      maxResults_ = value;
+    }
+
+    size_t GetMaxResults() const
+    {
+      return maxResults_;
+    }
+
+    // Returns "true" iff. all the matching resources have been
+    // returned. Will be "false" if the results were truncated by
+    // "SetMaxResults()".
+    bool Apply(std::list<std::string>& result);
+  };
+
+}
--- a/OrthancServer/ExactResourceFinder.cpp	Wed May 13 15:56:41 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,397 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "PrecompiledHeadersServer.h"
-#include "ExactResourceFinder.h"
-
-#include "FromDcmtkBridge.h"
-#include "ServerContext.h"
-
-#include <glog/logging.h>
-#include <boost/algorithm/string/predicate.hpp>
-
-namespace Orthanc
-{
-  class ExactResourceFinder::CandidateResources
-  {
-  private:
-    typedef std::map<DicomTag, std::string>  Query;
-
-    ExactResourceFinder&   finder_;
-    ServerIndex&           index_;
-    ResourceType           level_;
-    bool                   isFilterApplied_;
-    std::set<std::string>  filtered_;
-
-     
-    static void ListToSet(std::set<std::string>& target,
-                          const std::list<std::string>& source)
-    {
-      for (std::list<std::string>::const_iterator
-             it = source.begin(); it != source.end(); ++it)
-      {
-        target.insert(*it);
-      }
-    }
-
-
-    void RestrictIdentifier(const DicomTag& tag,
-                            const std::string& value)
-    {
-      assert((level_ == ResourceType_Patient && tag == DICOM_TAG_PATIENT_ID) ||
-             (level_ == ResourceType_Study && tag == DICOM_TAG_STUDY_INSTANCE_UID) ||
-             (level_ == ResourceType_Study && tag == DICOM_TAG_ACCESSION_NUMBER) ||
-             (level_ == ResourceType_Series && tag == DICOM_TAG_SERIES_INSTANCE_UID) ||
-             (level_ == ResourceType_Instance && tag == DICOM_TAG_SOP_INSTANCE_UID));
-
-      LOG(INFO) << "Lookup for identifier tag "
-                << FromDcmtkBridge::GetName(tag) << " (value: " << value << ")";
-
-      std::list<std::string> resources;
-      index_.LookupIdentifier(resources, tag, value, level_);
-
-      if (isFilterApplied_)
-      {
-        std::set<std::string>  s;
-        ListToSet(s, resources);
-
-        std::set<std::string> tmp = filtered_;
-        filtered_.clear();
-
-        for (std::set<std::string>::const_iterator 
-               it = tmp.begin(); it != tmp.end(); ++it)
-        {
-          if (s.find(*it) != s.end())
-          {
-            filtered_.insert(*it);
-          }
-        }
-      }
-      else
-      {
-        assert(filtered_.empty());
-        isFilterApplied_ = true;
-        ListToSet(filtered_, resources);
-      }
-    }
-
-
-  public:
-    CandidateResources(ExactResourceFinder& finder) : 
-      finder_(finder),
-      index_(finder.context_.GetIndex()),
-      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;
-          try
-          {
-            index_.GetChildren(children, *it);
-            ListToSet(filtered_, children);
-          }
-          catch (OrthancException&)
-          {
-            // The resource was removed in the meantime
-          }
-        }
-      }
-
-      switch (level_)
-      {
-        case ResourceType_Patient:
-          level_ = ResourceType_Study;
-          break;
-
-        case ResourceType_Study:
-          level_ = ResourceType_Series;
-          break;
-
-        case ResourceType_Series:
-          level_ = ResourceType_Instance;
-          break;
-
-        default:
-          throw OrthancException(ErrorCode_InternalError);
-      }
-    }
-
-
-    void Flatten(std::list<std::string>& resources) const
-    {
-      resources.clear();
-
-      if (isFilterApplied_)
-      {
-        for (std::set<std::string>::const_iterator 
-               it = filtered_.begin(); it != filtered_.end(); ++it)
-        {
-          resources.push_back(*it);
-        }
-      }
-      else
-      {
-        index_.GetAllUuids(resources, level_);
-      }
-    }
-
-    
-    void RestrictIdentifier(const DicomTag& tag)
-    {
-      Identifiers::const_iterator it = finder_.identifiers_.find(tag);
-      if (it != finder_.identifiers_.end())
-      {
-        RestrictIdentifier(it->first, it->second);
-      }
-    }
-
-
-    void RestrictMainDicomTags()
-    {
-      if (finder_.mainTagsFilter_ == NULL)
-      {
-        return;
-      }
-
-      std::list<std::string> resources;
-      Flatten(resources);
-
-      isFilterApplied_ = true;
-      filtered_.clear();
-
-      for (std::list<std::string>::const_iterator
-             it = resources.begin(); it != resources.end(); it++)
-      {
-        DicomMap mainTags;
-        if (index_.GetMainDicomTags(mainTags, *it, level_))
-        {
-          if (finder_.mainTagsFilter_->Apply(mainTags, level_))
-          {
-            filtered_.insert(*it);
-          }
-        }
-      }
-    }
-  };
-
-
-  ExactResourceFinder::ExactResourceFinder(ServerContext& context) : 
-    context_(context),
-    level_(ResourceType_Patient),
-    maxResults_(0)
-  {
-  }
-
-
-  void ExactResourceFinder::ApplyAtLevel(CandidateResources& candidates,
-                                         ResourceType level)
-  {
-    if (level != ResourceType_Patient)
-    {
-      candidates.GoDown();
-    }
-
-    switch (level_)
-    {
-      case ResourceType_Patient:
-      {
-        candidates.RestrictIdentifier(DICOM_TAG_PATIENT_ID);
-        break;
-      }
-
-      case ResourceType_Study:
-      {
-        candidates.RestrictIdentifier(DICOM_TAG_STUDY_INSTANCE_UID);
-        candidates.RestrictIdentifier(DICOM_TAG_ACCESSION_NUMBER);
-        break;
-      }
-
-      case ResourceType_Series:
-      {
-        candidates.RestrictIdentifier(DICOM_TAG_SERIES_INSTANCE_UID);
-        break;
-      }
-
-      case ResourceType_Instance:
-      {
-        candidates.RestrictIdentifier(DICOM_TAG_SOP_INSTANCE_UID);
-        break;
-      }
-
-      default:
-        throw OrthancException(ErrorCode_InternalError);
-    }
-
-    candidates.RestrictMainDicomTags();
-  }
-
-
-
-  void ExactResourceFinder::SetIdentifier(const DicomTag& tag,
-                                          const std::string& value)
-  {
-    assert((level_ >= ResourceType_Patient && tag == DICOM_TAG_PATIENT_ID) ||
-           (level_ >= ResourceType_Study && tag == DICOM_TAG_STUDY_INSTANCE_UID) ||
-           (level_ >= ResourceType_Study && tag == DICOM_TAG_ACCESSION_NUMBER) ||
-           (level_ >= ResourceType_Series && tag == DICOM_TAG_SERIES_INSTANCE_UID) ||
-           (level_ >= ResourceType_Instance && tag == DICOM_TAG_SOP_INSTANCE_UID));
-
-    identifiers_[tag] = value;
-  }
-
-
-  static bool LookupOneInstance(std::string& result,
-                                ServerIndex& index,
-                                const std::string& id,
-                                ResourceType type)
-  {
-    if (type == ResourceType_Instance)
-    {
-      result = id;
-      return true;
-    }
-
-    std::string childId;
-    
-    {
-      std::list<std::string> children;
-      index.GetChildInstances(children, id);
-
-      if (children.empty())
-      {
-        return false;
-      }
-
-      childId = children.front();
-    }
-
-    return LookupOneInstance(result, index, childId, GetChildResourceType(type));
-  }
-
-
-  bool ExactResourceFinder::Apply(std::list<std::string>& result)
-  {
-    CandidateResources candidates(*this);
-
-    ApplyAtLevel(candidates, ResourceType_Patient);
-
-    if (level_ == ResourceType_Study ||
-        level_ == ResourceType_Series ||
-        level_ == ResourceType_Instance)
-    {
-      ApplyAtLevel(candidates, ResourceType_Study);
-    }
-        
-    if (level_ == ResourceType_Series ||
-        level_ == ResourceType_Instance)
-    {
-      ApplyAtLevel(candidates, ResourceType_Series);
-    }
-        
-    if (level_ == ResourceType_Instance)
-    {
-      ApplyAtLevel(candidates, ResourceType_Instance);
-    }
-
-    if (instanceFilter_ == NULL)
-    {
-      candidates.Flatten(result);
-
-      if (maxResults_ != 0 &&
-          result.size() >= maxResults_)
-      {
-        result.resize(maxResults_);
-        return false;
-      }
-      else
-      {
-        return true;
-      }
-    }
-    else
-    {
-      std::list<std::string> tmp;
-      candidates.Flatten(tmp);
-      
-      result.clear();
-      for (std::list<std::string>::const_iterator 
-             resource = tmp.begin(); resource != tmp.end(); ++resource)
-      {
-        try
-        {
-          std::string instance;
-          if (LookupOneInstance(instance, context_.GetIndex(), *resource, level_))
-          {
-            Json::Value content;
-            context_.ReadJson(content, instance);
-            if (instanceFilter_->Apply(*resource, content))
-            {
-              result.push_back(*resource);
-
-              if (maxResults_ != 0 &&
-                  result.size() >= maxResults_)
-              {
-                // Too many results, stop before recording this new match
-                return false;
-              }
-            }
-          }
-        }
-        catch (OrthancException&)
-        {
-          // This resource has been deleted since the search was started
-        }
-      }      
-    }
-
-    return true;  // All the matching resources have been returned
-  }
-}
--- a/OrthancServer/ExactResourceFinder.h	Wed May 13 15:56:41 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "ServerIndex.h"
-
-#include <boost/noncopyable.hpp>
-
-namespace Orthanc
-{
-  class ExactResourceFinder : public boost::noncopyable
-  {
-  public:
-    class IMainTagsFilter : public boost::noncopyable
-    {
-    public:
-      virtual ~IMainTagsFilter()
-      {
-      }
-
-      bool Apply(const DicomMap& mainTags,
-                 ResourceType level);
-    };
-
-
-    class IInstanceFilter : public boost::noncopyable
-    {
-    public:
-      virtual ~IInstanceFilter()
-      {
-      }
-
-      bool Apply(const std::string& instanceId,
-                 const Json::Value& content);
-    };
-
-
-  private:
-    typedef std::map<DicomTag, std::string>  Identifiers;
-
-    class CandidateResources;
-
-    ServerContext&    context_;
-    ResourceType      level_;
-    size_t            maxResults_;
-    Identifiers       identifiers_;
-    IMainTagsFilter  *mainTagsFilter_;
-    IInstanceFilter  *instanceFilter_;
-
-    void ApplyAtLevel(CandidateResources& candidates,
-                      ResourceType level);
-
-  public:
-    ExactResourceFinder(ServerContext& context);
-
-    ResourceType GetLevel() const
-    {
-      return level_;
-    }
-
-    void SetLevel(ResourceType level)
-    {
-      level_ = level;
-    }
-
-    void SetIdentifier(const DicomTag& tag,
-                       const std::string& value);
-
-    void SetMainTagsFilter(IMainTagsFilter& filter)
-    {
-      mainTagsFilter_ = &filter;
-    }
-
-    void SetInstanceFilter(IInstanceFilter& filter)
-    {
-      instanceFilter_ = &filter;
-    }
-
-    void SetMaxResults(size_t value)
-    {
-      maxResults_ = value;
-    }
-
-    size_t GetMaxResults() const
-    {
-      return maxResults_;
-    }
-
-    // Returns "true" iff. all the matching resources have been
-    // returned. Will be "false" if the results were truncated by
-    // "SetMaxResults()".
-    bool Apply(std::list<std::string>& result);
-  };
-
-}
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed May 13 15:56:41 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed May 13 17:56:27 2015 +0200
@@ -35,7 +35,7 @@
 
 #include "../ServerToolbox.h"
 #include "../FromDcmtkBridge.h"
-#include "../ExactResourceFinder.h"
+#include "../ResourceFinder.h"
 
 #include <glog/logging.h>
 
@@ -850,7 +850,7 @@
 
   static void Find(RestApiPostCall& call)
   {
-    ServerIndex& index = OrthancRestApi::GetIndex(call);
+    ServerContext& context = OrthancRestApi::GetContext(call);
 
     Json::Value request;
     if (call.ParseJsonRequest(request) &&
@@ -862,20 +862,20 @@
     {
       std::string level = request["Level"].asString();
 
-      ExactResourceFinder finder(index);
+      ResourceFinder finder(context);
       finder.SetLevel(StringToResourceType(level.c_str()));
 
-      if (request.isMember("CaseSensitive"))
-      {
-        finder.SetCaseSensitive(request["CaseSensitive"].asBool());
-      }
-
       bool expand = false;
       if (request.isMember("Expand"))
       {
         expand = request["Expand"].asBool();
       }
 
+      /*if (request.isMember("CaseSensitive"))
+      {
+        finder.SetCaseSensitive(request["CaseSensitive"].asBool());
+      }
+
       Json::Value::Members members = request["Query"].getMemberNames();
       for (size_t i = 0; i < members.size(); i++)
       {
@@ -885,11 +885,11 @@
         }
 
         finder.AddTag(members[i], request["Query"][members[i]].asString());
-      }
+        }*/
 
       std::list<std::string> resources;
       finder.Apply(resources);
-      AnswerListOfResources(call.GetOutput(), index, resources, finder.GetLevel(), expand);
+      AnswerListOfResources(call.GetOutput(), context.GetIndex(), resources, finder.GetLevel(), expand);
     }
     else
     {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/ResourceFinder.cpp	Wed May 13 17:56:27 2015 +0200
@@ -0,0 +1,54 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PrecompiledHeadersServer.h"
+#include "ResourceFinder.h"
+
+namespace Orthanc
+{
+  namespace
+  {
+    class IConstraint : public boost::noncopyable
+    {
+    public:
+      virtual ~IConstraint()
+      {
+      }
+
+      virtual bool Apply(const std::string& value) = 0;
+    }; 
+
+
+    
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/ResourceFinder.h	Wed May 13 17:56:27 2015 +0200
@@ -0,0 +1,76 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "BaseResourceFinder.h"
+
+namespace Orthanc
+{
+  class ResourceFinder : public boost::noncopyable
+  {
+  private:
+    BaseResourceFinder  finder_;
+
+  public:
+    ResourceFinder(ServerContext& context) :
+      finder_(context)
+    {
+    }
+
+    ResourceType GetLevel() const
+    {
+      return finder_.GetLevel();
+    }
+
+    void SetLevel(ResourceType level)
+    {
+      finder_.SetLevel(level);
+    }
+
+    void SetMaxResults(size_t value)
+    {
+      finder_.SetMaxResults(value);
+    }
+
+    size_t GetMaxResults() const
+    {
+      return finder_.GetMaxResults();
+    }
+
+    bool Apply(std::list<std::string>& result)
+    {
+      return finder_.Apply(result);
+    }
+  };
+
+}