changeset 1746:d143db00a794 db-changes

SetOfResources
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 26 Oct 2015 16:04:58 +0100
parents 38dda23c7d7d
children ca69082ab200
files CMakeLists.txt Core/Enumerations.cpp Core/Enumerations.h OrthancServer/DatabaseWrapper.h OrthancServer/DatabaseWrapperBase.cpp OrthancServer/DatabaseWrapperBase.h OrthancServer/IDatabaseWrapper.h OrthancServer/LookupIdentifierQuery.cpp OrthancServer/LookupIdentifierQuery.h OrthancServer/ServerEnumerations.h OrthancServer/ServerIndex.cpp OrthancServer/SetOfResources.cpp OrthancServer/SetOfResources.h OrthancServer/main.cpp Plugins/Engine/OrthancPluginDatabase.cpp Plugins/Engine/OrthancPluginDatabase.h Plugins/Include/orthanc/OrthancCDatabasePlugin.h Plugins/Include/orthanc/OrthancCPlugin.h Resources/ErrorCodes.json UnitTestsSources/ServerIndexTests.cpp
diffstat 20 files changed, 402 insertions(+), 94 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Mon Oct 26 13:47:50 2015 +0100
+++ b/CMakeLists.txt	Mon Oct 26 16:04:58 2015 +0100
@@ -185,6 +185,7 @@
   OrthancServer/ServerEnumerations.cpp
   OrthancServer/ServerIndex.cpp
   OrthancServer/ServerToolbox.cpp
+  OrthancServer/SetOfResources.cpp
   OrthancServer/SliceOrdering.cpp
   OrthancServer/ToDcmtkBridge.cpp
 
--- a/Core/Enumerations.cpp	Mon Oct 26 13:47:50 2015 +0100
+++ b/Core/Enumerations.cpp	Mon Oct 26 16:04:58 2015 +0100
@@ -148,6 +148,9 @@
       case ErrorCode_StorageAreaPlugin:
         return "Error in the plugin implementing a custom storage area";
 
+      case ErrorCode_EmptyRequest:
+        return "The request is empty";
+
       case ErrorCode_SQLiteNotOpened:
         return "SQLite: The database is not opened";
 
--- a/Core/Enumerations.h	Mon Oct 26 13:47:50 2015 +0100
+++ b/Core/Enumerations.h	Mon Oct 26 16:04:58 2015 +0100
@@ -79,6 +79,7 @@
     ErrorCode_BadFont = 30    /*!< Badly formatted font file */,
     ErrorCode_DatabasePlugin = 31    /*!< The plugin implementing a custom database back-end does not fulfill the proper interface */,
     ErrorCode_StorageAreaPlugin = 32    /*!< Error in the plugin implementing a custom storage area */,
+    ErrorCode_EmptyRequest = 33    /*!< The request is empty */,
     ErrorCode_SQLiteNotOpened = 1000    /*!< SQLite: The database is not opened */,
     ErrorCode_SQLiteAlreadyOpened = 1001    /*!< SQLite: Connection is already open */,
     ErrorCode_SQLiteCannotOpen = 1002    /*!< SQLite: Unable to open the database */,
--- a/OrthancServer/DatabaseWrapper.h	Mon Oct 26 13:47:50 2015 +0100
+++ b/OrthancServer/DatabaseWrapper.h	Mon Oct 26 16:04:58 2015 +0100
@@ -315,17 +315,13 @@
       return base_.IsExistingResource(internalId);
     }
 
-    virtual void LookupIdentifierExact(std::list<int64_t>& target,
-                                       ResourceType level,
-                                       const DicomTag& tag,
-                                       const std::string& value)
+    virtual void LookupIdentifier(std::list<int64_t>& result,
+                                  ResourceType level,
+                                  const DicomTag& tag,
+                                  IdentifierConstraintType type,
+                                  const std::string& value)
     {
-      base_.LookupIdentifierExact(target, level, tag, value);
-    }
-
-    virtual void LookupIdentifier(const LookupIdentifierQuery& query)
-    {
-      base_.LookupIdentifier(query);
+      base_.LookupIdentifier(result, level, tag, type, value);
     }
 
     virtual void GetAllMetadata(std::map<MetadataType, std::string>& target,
--- a/OrthancServer/DatabaseWrapperBase.cpp	Mon Oct 26 13:47:50 2015 +0100
+++ b/OrthancServer/DatabaseWrapperBase.cpp	Mon Oct 26 16:04:58 2015 +0100
@@ -34,6 +34,7 @@
 #include "DatabaseWrapperBase.h"
 
 #include <stdio.h>
+#include <memory>
 
 namespace Orthanc
 {
@@ -665,6 +666,11 @@
   }
 
 
+
+  /**
+
+     TODO REMOVE THIS
+
   void DatabaseWrapperBase::LookupIdentifierExact(std::list<int64_t>& target,
                                                   ResourceType level,
                                                   const DicomTag& tag,
@@ -692,12 +698,56 @@
       target.push_back(s.ColumnInt64(0));
     }
   }
+  */
 
 
 
-  void DatabaseWrapperBase::LookupIdentifier(const LookupIdentifierQuery& query)
+  void DatabaseWrapperBase::LookupIdentifier(std::list<int64_t>& target,
+                                             ResourceType level,
+                                             const DicomTag& tag,
+                                             IdentifierConstraintType type,
+                                             const std::string& value)
   {
-    // TODO
-    throw OrthancException(ErrorCode_NotImplemented);
+    static const char* COMMON = ("SELECT d.id FROM DicomIdentifiers AS d, Resources AS r WHERE "
+                                 "d.id = r.internalId AND r.resourceType=? AND "
+                                 "d.tagGroup=? AND d.tagElement=? AND ");
+
+    std::auto_ptr<SQLite::Statement> s;
+
+    switch (type)
+    {
+      case IdentifierConstraintType_Equal:
+        s.reset(new SQLite::Statement(db_, std::string(COMMON) + "d.value=?"));
+        break;
+
+      case IdentifierConstraintType_GreaterOrEqual:
+        s.reset(new SQLite::Statement(db_, std::string(COMMON) + "d.value>=?"));
+        break;
+
+      case IdentifierConstraintType_SmallerOrEqual:
+        s.reset(new SQLite::Statement(db_, std::string(COMMON) + "d.value<=?"));
+        break;
+
+      case IdentifierConstraintType_Wildcard:
+        s.reset(new SQLite::Statement(db_, std::string(COMMON) + "d.value LIKE ?"));
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    assert(s.get() != NULL);
+
+    s->BindInt(0, level);
+    s->BindInt(1, tag.GetGroup());
+    s->BindInt(2, tag.GetElement());
+    s->BindString(3, value);
+
+    target.clear();
+
+    while (s->Step())
+    {
+      target.push_back(s->ColumnInt64(0));
+    }    
   }
 }
--- a/OrthancServer/DatabaseWrapperBase.h	Mon Oct 26 13:47:50 2015 +0100
+++ b/OrthancServer/DatabaseWrapperBase.h	Mon Oct 26 16:04:58 2015 +0100
@@ -191,12 +191,11 @@
 
     bool IsExistingResource(int64_t internalId);
 
-    void LookupIdentifierExact(std::list<int64_t>& target,
-                               ResourceType level,
-                               const DicomTag& tag,
-                               const std::string& value);
-
-    void LookupIdentifier(const LookupIdentifierQuery& query);
+    void LookupIdentifier(std::list<int64_t>& result,
+                          ResourceType level,
+                          const DicomTag& tag,
+                          IdentifierConstraintType type,
+                          const std::string& value);
   };
 }
 
--- a/OrthancServer/IDatabaseWrapper.h	Mon Oct 26 13:47:50 2015 +0100
+++ b/OrthancServer/IDatabaseWrapper.h	Mon Oct 26 16:04:58 2015 +0100
@@ -44,8 +44,6 @@
 
 namespace Orthanc
 {
-  class LookupIdentifierQuery;
-
   class IDatabaseWrapper : public boost::noncopyable
   {
   public:
@@ -148,12 +146,11 @@
     virtual bool LookupGlobalProperty(std::string& target,
                                       GlobalProperty property) = 0;
 
-    virtual void LookupIdentifierExact(std::list<int64_t>& target,
-                                       ResourceType level,
-                                       const DicomTag& tag,
-                                       const std::string& value) = 0;
-
-    virtual void LookupIdentifier(const LookupIdentifierQuery& query) = 0;
+    virtual void LookupIdentifier(std::list<int64_t>& result,
+                                  ResourceType level,
+                                  const DicomTag& tag,
+                                  IdentifierConstraintType type,
+                                  const std::string& value) = 0;
 
     virtual bool LookupMetadata(std::string& target,
                                 int64_t id,
--- a/OrthancServer/LookupIdentifierQuery.cpp	Mon Oct 26 13:47:50 2015 +0100
+++ b/OrthancServer/LookupIdentifierQuery.cpp	Mon Oct 26 16:04:58 2015 +0100
@@ -34,7 +34,7 @@
 #include "LookupIdentifierQuery.h"
 
 #include "../Core/OrthancException.h"
-
+#include "SetOfResources.h"
 
 #include <cassert>
 
@@ -70,27 +70,6 @@
     DICOM_TAG_SOP_INSTANCE_UID
   };
 
-
-  LookupIdentifierQuery::~LookupIdentifierQuery()
-  {
-    for (Constraints::iterator it = constraints_.begin();
-         it != constraints_.end(); ++it)
-    {
-      delete *it;
-    }
-  }
-
-
-
-  void  LookupIdentifierQuery::CheckIndex(size_t index) const
-  {
-    if (index >= constraints_.size())
-    {
-      throw OrthancException(ErrorCode_ParameterOutOfRange);
-    }
-  }
-
-
   static void LoadIdentifiers(const DicomTag*& tags,
                               size_t& size,
                               ResourceType level)
@@ -123,6 +102,27 @@
   }
 
 
+
+  LookupIdentifierQuery::~LookupIdentifierQuery()
+  {
+    for (Constraints::iterator it = constraints_.begin();
+         it != constraints_.end(); ++it)
+    {
+      delete *it;
+    }
+  }
+
+
+
+  void  LookupIdentifierQuery::CheckIndex(size_t index) const
+  {
+    if (index >= constraints_.size())
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
   bool LookupIdentifierQuery::IsIdentifier(const DicomTag& tag) const
   {
     const DicomTag* tags;
@@ -202,4 +202,20 @@
       }
     }
   }
+
+
+  void LookupIdentifierQuery::Apply(std::list<std::string>& result,
+                                    IDatabaseWrapper& database)
+  {
+    SetOfResources resources(database, level_);
+    
+    for (size_t i = 0; i < GetSize(); i++)
+    {
+      std::list<int64_t> tmp;
+      database.LookupIdentifier(tmp, level_, GetTag(i), GetType(i), GetValue(i));
+      resources.Intersect(tmp);
+    }
+
+    resources.Flatten(result);
+  }
 }
--- a/OrthancServer/LookupIdentifierQuery.h	Mon Oct 26 13:47:50 2015 +0100
+++ b/OrthancServer/LookupIdentifierQuery.h	Mon Oct 26 16:04:58 2015 +0100
@@ -54,14 +54,12 @@
    * corresponds to "%" (resp. "_") in primitive LIKE of SQL. The
    * values "%", "_", "\" should in the user request should
    * respectively be escaped as "\%", "\_" and "\\".
-   * 
+   *
    * This matching must be case sensitive: The special case of PN VR
    * is taken into consideration by normalizing the query string in
    * method "NormalizeIdentifier()".
    **/
 
-
-
   class LookupIdentifierQuery : public boost::noncopyable
   {
   private:
@@ -103,7 +101,12 @@
                        IdentifierConstraintType type,
                        const std::string& value);
 
-    size_t GetSize()
+    ResourceType GetLevel() const
+    {
+      return level_;
+    }
+
+    size_t GetSize() const
     {
       return constraints_.size();
     }
@@ -118,6 +121,9 @@
                                  int64_t resource,
                                  ResourceType level,
                                  const DicomMap& map);
-    
+
+    // The database must be locked
+    void Apply(std::list<std::string>& result,
+               IDatabaseWrapper& database);
   };
 }
--- a/OrthancServer/ServerEnumerations.h	Mon Oct 26 13:47:50 2015 +0100
+++ b/OrthancServer/ServerEnumerations.h	Mon Oct 26 16:04:58 2015 +0100
@@ -128,9 +128,9 @@
   enum IdentifierConstraintType
   {
     IdentifierConstraintType_Equal,
-    IdentifierConstraintType_LessOrEqual,
-    IdentifierConstraintType_GeaterOrEqual,
-    IdentifierConstraintType_Wildcard        /* "*" or "?" are the only allowed wildcards */
+    IdentifierConstraintType_SmallerOrEqual,
+    IdentifierConstraintType_GreaterOrEqual,
+    IdentifierConstraintType_Wildcard        /* Case sensitive, "*" or "?" are the only allowed wildcards */
   };
 
 
--- a/OrthancServer/ServerIndex.cpp	Mon Oct 26 13:47:50 2015 +0100
+++ b/OrthancServer/ServerIndex.cpp	Mon Oct 26 16:04:58 2015 +0100
@@ -45,6 +45,7 @@
 #include "../Core/Logging.h"
 #include "../Core/Uuid.h"
 #include "../Core/DicomFormat/DicomArray.h"
+#include "LookupIdentifierQuery.h"
 
 #include "FromDcmtkBridge.h"
 #include "ServerContext.h"
@@ -1910,14 +1911,9 @@
 
     boost::mutex::scoped_lock lock(mutex_);
 
-    std::list<int64_t> id;
-    db_.LookupIdentifierExact(id, level, tag, value);
-
-    for (std::list<int64_t>::const_iterator 
-           it = id.begin(); it != id.end(); ++it)
-    {
-      result.push_back(db_.GetPublicId(*it));
-    }
+    LookupIdentifierQuery query(level);
+    query.AddConstraint(tag, IdentifierConstraintType_Equal, value);
+    query.Apply(result, db_);
   }
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/SetOfResources.cpp	Mon Oct 26 16:04:58 2015 +0100
@@ -0,0 +1,115 @@
+/**
+ * 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 "SetOfResources.h"
+
+#include "../Core/OrthancException.h"
+
+
+namespace Orthanc
+{
+  void SetOfResources::Intersect(const std::list<int64_t>& resources)
+  {
+    if (resources_.get() == NULL)
+    {
+      resources_.reset(new Resources);
+
+      for (std::list<int64_t>::const_iterator
+             it = resources.begin(); it != resources.end(); ++it)
+      {
+        resources_->insert(*it);
+      }
+    }
+    else
+    {
+      std::auto_ptr<Resources> filtered(new Resources);
+
+      for (std::list<int64_t>::const_iterator
+             it = resources.begin(); it != resources.end(); ++it)
+      {
+        if (resources_->find(*it) != resources_->end())
+        {
+          filtered->insert(*it);
+        }
+      }
+
+      resources_ = filtered;
+    }
+  }
+
+
+  void SetOfResources::GoDown()
+  {
+    if (level_ == ResourceType_Instance)
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+
+    std::auto_ptr<Resources> children(new Resources);
+
+    for (Resources::const_iterator it = resources_->begin(); 
+         it != resources_->end(); ++it)
+    {
+      std::list<int64_t> tmp;
+      database_.GetChildrenInternalId(tmp, *it);
+
+      for (std::list<int64_t>::const_iterator
+             child = tmp.begin(); child != tmp.end(); ++child)
+      {
+        children->insert(*child);
+      }
+    }
+
+    resources_ = children;
+  }
+
+
+  void SetOfResources::Flatten(std::list<std::string>& result)
+  {
+    result.clear();
+      
+    if (resources_.get() == NULL)
+    {
+      // All the resources of this level are part of the filter
+      database_.GetAllPublicIds(result, level_);
+    }
+    else
+    {
+      for (Resources::const_iterator it = resources_->begin(); 
+           it != resources_->end(); ++it)
+      {
+        result.push_back(database_.GetPublicId(*it));
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/SetOfResources.h	Mon Oct 26 16:04:58 2015 +0100
@@ -0,0 +1,71 @@
+/**
+ * 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 "IDatabaseWrapper.h"
+
+#include <set>
+#include <boost/noncopyable.hpp>
+#include <memory>
+
+namespace Orthanc
+{
+  class SetOfResources : public boost::noncopyable
+  {
+  private:
+    typedef std::set<int64_t>  Resources;
+
+    IDatabaseWrapper&         database_;
+    ResourceType              level_;
+    std::auto_ptr<Resources>  resources_;
+    
+  public:
+    SetOfResources(IDatabaseWrapper& database,
+                   ResourceType level) : 
+      database_(database),
+      level_(level)
+    {
+    }
+
+    ResourceType GetLevel() const
+    {
+      return level_;
+    }
+
+    void Intersect(const std::list<int64_t>& resources);
+
+    void GoDown();
+
+    void Flatten(std::list<std::string>& result);
+  };
+}
--- a/OrthancServer/main.cpp	Mon Oct 26 13:47:50 2015 +0100
+++ b/OrthancServer/main.cpp	Mon Oct 26 16:04:58 2015 +0100
@@ -491,6 +491,7 @@
     PrintErrorCode(ErrorCode_BadFont, "Badly formatted font file");
     PrintErrorCode(ErrorCode_DatabasePlugin, "The plugin implementing a custom database back-end does not fulfill the proper interface");
     PrintErrorCode(ErrorCode_StorageAreaPlugin, "Error in the plugin implementing a custom storage area");
+    PrintErrorCode(ErrorCode_EmptyRequest, "The request is empty");
     PrintErrorCode(ErrorCode_SQLiteNotOpened, "SQLite: The database is not opened");
     PrintErrorCode(ErrorCode_SQLiteAlreadyOpened, "SQLite: Connection is already open");
     PrintErrorCode(ErrorCode_SQLiteCannotOpen, "SQLite: Unable to open the database");
--- a/Plugins/Engine/OrthancPluginDatabase.cpp	Mon Oct 26 13:47:50 2015 +0100
+++ b/Plugins/Engine/OrthancPluginDatabase.cpp	Mon Oct 26 16:04:58 2015 +0100
@@ -594,6 +594,10 @@
   }
 
 
+  /*
+
+    TODO REMOVE THIS
+
   void OrthancPluginDatabase::LookupIdentifierExact(std::list<int64_t>& target,
                                                     ResourceType level,
                                                     const DicomTag& tag,
@@ -643,10 +647,14 @@
         }
       }
     }
-  }
+    }*/
 
 
-  void OrthancPluginDatabase::LookupIdentifier(const LookupIdentifierQuery& query)
+  void OrthancPluginDatabase::LookupIdentifier(std::list<int64_t>& result,
+                                               ResourceType level,
+                                               const DicomTag& tag,
+                                               IdentifierConstraintType type,
+                                               const std::string& value)
   {
     // TODO
     throw OrthancException(ErrorCode_NotImplemented);
--- a/Plugins/Engine/OrthancPluginDatabase.h	Mon Oct 26 13:47:50 2015 +0100
+++ b/Plugins/Engine/OrthancPluginDatabase.h	Mon Oct 26 16:04:58 2015 +0100
@@ -203,12 +203,11 @@
     virtual bool LookupGlobalProperty(std::string& target,
                                       GlobalProperty property);
 
-    virtual void LookupIdentifierExact(std::list<int64_t>& target,
-                                       ResourceType level,
-                                       const DicomTag& tag,
-                                       const std::string& value);
-
-    virtual void LookupIdentifier(const LookupIdentifierQuery& query);
+    virtual void LookupIdentifier(std::list<int64_t>& result,
+                                  ResourceType level,
+                                  const DicomTag& tag,
+                                  IdentifierConstraintType type,
+                                  const std::string& value);
 
     virtual bool LookupMetadata(std::string& target,
                                 int64_t id,
--- a/Plugins/Include/orthanc/OrthancCDatabasePlugin.h	Mon Oct 26 13:47:50 2015 +0100
+++ b/Plugins/Include/orthanc/OrthancCDatabasePlugin.h	Mon Oct 26 16:04:58 2015 +0100
@@ -522,7 +522,7 @@
       void* payload,
       int32_t property);
 
-    /* Use "OrthancPluginDatabaseExtensions::lookupIdentifierExact" 
+    /* Use "OrthancPluginDatabaseExtensions::lookupIdentifier2" 
        instead of this function as of Orthanc 0.9.5 (db v6), can be set to NULL.
        Output: Use OrthancPluginDatabaseAnswerInt64() */
     OrthancPluginErrorCode  (*lookupIdentifier) (
@@ -664,14 +664,17 @@
       void* payload,
       int64_t id);
 
+#if 0
     /* Output: Use OrthancPluginDatabaseAnswerInt64() */
-    OrthancPluginErrorCode  (*lookupIdentifierExact) (
+    OrthancPluginErrorCode  (*lookupIdentifier2) (
       /* outputs */
       OrthancPluginDatabaseContext* context,
       /* inputs */
       void* payload,
       OrthancPluginResourceType resourceType,
       const OrthancPluginDicomTag* tag);
+#endif
+
    } OrthancPluginDatabaseExtensions;
 
 /*<! @endcond */
--- a/Plugins/Include/orthanc/OrthancCPlugin.h	Mon Oct 26 13:47:50 2015 +0100
+++ b/Plugins/Include/orthanc/OrthancCPlugin.h	Mon Oct 26 16:04:58 2015 +0100
@@ -212,6 +212,7 @@
     OrthancPluginErrorCode_BadFont = 30    /*!< Badly formatted font file */,
     OrthancPluginErrorCode_DatabasePlugin = 31    /*!< The plugin implementing a custom database back-end does not fulfill the proper interface */,
     OrthancPluginErrorCode_StorageAreaPlugin = 32    /*!< Error in the plugin implementing a custom storage area */,
+    OrthancPluginErrorCode_EmptyRequest = 33    /*!< The request is empty */,
     OrthancPluginErrorCode_SQLiteNotOpened = 1000    /*!< SQLite: The database is not opened */,
     OrthancPluginErrorCode_SQLiteAlreadyOpened = 1001    /*!< SQLite: Connection is already open */,
     OrthancPluginErrorCode_SQLiteCannotOpen = 1002    /*!< SQLite: Unable to open the database */,
--- a/Resources/ErrorCodes.json	Mon Oct 26 13:47:50 2015 +0100
+++ b/Resources/ErrorCodes.json	Mon Oct 26 16:04:58 2015 +0100
@@ -185,6 +185,11 @@
     "Name": "StorageAreaPlugin", 
     "Description": "Error in the plugin implementing a custom storage area"
   },
+  {
+    "Code": 33,
+    "Name": "EmptyRequest",
+    "Description": "The request is empty"
+  },
 
 
 
--- a/UnitTestsSources/ServerIndexTests.cpp	Mon Oct 26 13:47:50 2015 +0100
+++ b/UnitTestsSources/ServerIndexTests.cpp	Mon Oct 26 16:04:58 2015 +0100
@@ -245,6 +245,18 @@
           throw OrthancException(ErrorCode_InternalError);
       }
     }
+
+
+    void DoLookup(std::list<std::string>& result,
+                  ResourceType level,
+                  const DicomTag& tag,
+                  const std::string& value)
+    {
+      LookupIdentifierQuery query(level);
+      query.AddConstraint(tag, IdentifierConstraintType_Equal, value);
+      query.Apply(result, *index_);
+    }
+
   };
 }
 
@@ -690,36 +702,64 @@
   index_->SetIdentifierTag(a[2], DICOM_TAG_STUDY_INSTANCE_UID, "0");
   index_->SetIdentifierTag(a[3], DICOM_TAG_SERIES_INSTANCE_UID, "0");
 
-  std::list<int64_t> s;
+  std::list<std::string> s;
 
-  index_->LookupIdentifierExact(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, "0");
+  DoLookup(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, "0");
   ASSERT_EQ(2u, s.size());
-  ASSERT_TRUE(std::find(s.begin(), s.end(), a[0]) != s.end());
-  ASSERT_TRUE(std::find(s.begin(), s.end(), a[2]) != s.end());
+  ASSERT_TRUE(std::find(s.begin(), s.end(), "a") != s.end());
+  ASSERT_TRUE(std::find(s.begin(), s.end(), "c") != s.end());
 
-  index_->LookupIdentifierExact(s, ResourceType_Series, DICOM_TAG_SERIES_INSTANCE_UID, "0");
+  DoLookup(s, ResourceType_Series, DICOM_TAG_SERIES_INSTANCE_UID, "0");
   ASSERT_EQ(1u, s.size());
-  ASSERT_TRUE(std::find(s.begin(), s.end(), a[3]) != s.end());
+  ASSERT_TRUE(std::find(s.begin(), s.end(), "d") != s.end());
 
-  index_->LookupIdentifierExact(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, "1");
+  DoLookup(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, "1");
   ASSERT_EQ(1u, s.size());
-  ASSERT_TRUE(std::find(s.begin(), s.end(), a[1]) != s.end());
+  ASSERT_TRUE(std::find(s.begin(), s.end(), "b") != s.end());
 
-  index_->LookupIdentifierExact(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, "1");
+  DoLookup(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, "1");
   ASSERT_EQ(1u, s.size());
-  ASSERT_TRUE(std::find(s.begin(), s.end(), a[1]) != s.end());
+  ASSERT_TRUE(std::find(s.begin(), s.end(), "b") != s.end());
 
-  index_->LookupIdentifierExact(s, ResourceType_Series, DICOM_TAG_SERIES_INSTANCE_UID, "1");
+  DoLookup(s, ResourceType_Series, DICOM_TAG_SERIES_INSTANCE_UID, "1");
   ASSERT_EQ(0u, s.size());
 
-  /*{
-    std::list<std::string> s;
-    context.GetIndex().LookupIdentifierExact(s, DICOM_TAG_STUDY_INSTANCE_UID, "1.2.250.1.74.20130819132500.29000036381059");
-    for (std::list<std::string>::iterator i = s.begin(); i != s.end(); i++)
-    {
-    std::cout << "*** " << *i << std::endl;;
-    }      
-    }*/
+  {
+    LookupIdentifierQuery query(ResourceType_Study);
+    query.AddConstraint(DICOM_TAG_STUDY_INSTANCE_UID, IdentifierConstraintType_GreaterOrEqual, "0");
+    query.Apply(s, *index_);
+    ASSERT_EQ(3u, s.size());
+  }
+
+  {
+    LookupIdentifierQuery query(ResourceType_Study);
+    query.AddConstraint(DICOM_TAG_STUDY_INSTANCE_UID, IdentifierConstraintType_GreaterOrEqual, "0");
+    query.AddConstraint(DICOM_TAG_STUDY_INSTANCE_UID, IdentifierConstraintType_SmallerOrEqual, "0");
+    query.Apply(s, *index_);
+    ASSERT_EQ(2u, s.size());
+  }
+
+  {
+    LookupIdentifierQuery query(ResourceType_Study);
+    query.AddConstraint(DICOM_TAG_STUDY_INSTANCE_UID, IdentifierConstraintType_GreaterOrEqual, "1");
+    query.AddConstraint(DICOM_TAG_STUDY_INSTANCE_UID, IdentifierConstraintType_SmallerOrEqual, "1");
+    query.Apply(s, *index_);
+    ASSERT_EQ(1u, s.size());
+  }
+
+  {
+    LookupIdentifierQuery query(ResourceType_Study);
+    query.AddConstraint(DICOM_TAG_STUDY_INSTANCE_UID, IdentifierConstraintType_GreaterOrEqual, "1");
+    query.Apply(s, *index_);
+    ASSERT_EQ(1u, s.size());
+  }
+
+  {
+    LookupIdentifierQuery query(ResourceType_Study);
+    query.AddConstraint(DICOM_TAG_STUDY_INSTANCE_UID, IdentifierConstraintType_GreaterOrEqual, "2");
+    query.Apply(s, *index_);
+    ASSERT_EQ(0u, s.size());
+  }
 }