# HG changeset patch
# User Sebastien Jodogne <s.jodogne@gmail.com>
# Date 1444751310 -7200
# Node ID 2ad22b2970a2f0f1ca352fddf96faab635177d8a
# Parent  275780da54aeaa5f779c243e95bb7b190275b455
SearchableStudies

diff -r 275780da54ae -r 2ad22b2970a2 CMakeLists.txt
--- a/CMakeLists.txt	Tue Oct 13 16:57:55 2015 +0200
+++ b/CMakeLists.txt	Tue Oct 13 17:48:30 2015 +0200
@@ -240,6 +240,7 @@
   PREPARE_DATABASE            ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/PrepareDatabase.sql
   UPGRADE_DATABASE_3_TO_4     ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/Upgrade3To4.sql
   UPGRADE_DATABASE_4_TO_5     ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/Upgrade4To5.sql
+  UPGRADE_DATABASE_5_TO_6     ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/Upgrade5To6.sql
   CONFIGURATION_SAMPLE        ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Configuration.json
   DICOM_CONFORMANCE_STATEMENT ${CMAKE_CURRENT_SOURCE_DIR}/Resources/DicomConformanceStatement.txt
   LUA_TOOLBOX                 ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Toolbox.lua
diff -r 275780da54ae -r 2ad22b2970a2 OrthancServer/DatabaseWrapper.cpp
--- a/OrthancServer/DatabaseWrapper.cpp	Tue Oct 13 16:57:55 2015 +0200
+++ b/OrthancServer/DatabaseWrapper.cpp	Tue Oct 13 17:48:30 2015 +0200
@@ -359,11 +359,13 @@
     if (version_ == 5)
     {
       LOG(WARNING) << "Upgrading database version from 5 to 6";
-      // No change in the DB schema, the step from version 5 to 6 only
-      // consists in reconstructing the main DICOM tags information.
       db_.BeginTransaction();
+      ExecuteUpgradeScript(db_, EmbeddedResources::UPGRADE_DATABASE_5_TO_6);      
+      // Reconstruct the main DICOM tags information.
+      Toolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Patient);
       Toolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Study);
       Toolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Series);
+      Toolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Instance);
       db_.CommitTransaction();
       version_ = 6;
     }    
@@ -484,5 +486,4 @@
       target[key] = s.ColumnString(1);
     }
   }
-
 }
diff -r 275780da54ae -r 2ad22b2970a2 OrthancServer/DatabaseWrapper.h
--- a/OrthancServer/DatabaseWrapper.h	Tue Oct 13 16:57:55 2015 +0200
+++ b/OrthancServer/DatabaseWrapper.h	Tue Oct 13 17:48:30 2015 +0200
@@ -329,6 +329,11 @@
     virtual void Upgrade(unsigned int targetVersion,
                          IStorageArea& storageArea);
 
+    virtual void StoreStudyModule(int64_t id,
+                                  const DicomMap& module)
+    {
+      base_.StoreStudyModule(id, module);
+    }
 
 
     /**
diff -r 275780da54ae -r 2ad22b2970a2 OrthancServer/DatabaseWrapperBase.cpp
--- a/OrthancServer/DatabaseWrapperBase.cpp	Tue Oct 13 16:57:55 2015 +0200
+++ b/OrthancServer/DatabaseWrapperBase.cpp	Tue Oct 13 17:48:30 2015 +0200
@@ -33,6 +33,8 @@
 #include "PrecompiledHeadersServer.h"
 #include "DatabaseWrapperBase.h"
 
+#include "../Core/DicomFormat/DicomArray.h"
+
 #include <stdio.h>
 
 namespace Orthanc
@@ -309,6 +311,13 @@
       s.BindInt64(0, id);
       s.Run();
     }
+
+    // New in DB v6 (Orthanc >= 0.9.5)
+    {
+      SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM SearchableStudies WHERE id=?");
+      s.BindInt64(0, id);
+      s.Run();
+    }
   }
 
 
@@ -712,4 +721,27 @@
       target.push_back(s.ColumnInt64(0));
     }
   }
+
+
+  void DatabaseWrapperBase::StoreStudyModule(int64_t id,
+                                             const DicomMap& module)
+  {
+    DicomArray a(module);
+
+    for (size_t i = 0; i < a.GetSize(); i++)
+    {
+      const DicomTag& tag = a.GetElement(i).GetTag();
+      const DicomValue& value = a.GetElement(i).GetValue();
+
+      if (!value.IsNull())
+      {
+        SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO SearchableStudies VALUES(?, ?, ?, ?)");
+        s.BindInt64(0, id);
+        s.BindInt(1, tag.GetGroup());
+        s.BindInt(2, tag.GetElement());
+        s.BindString(3, value.AsString());
+        s.Run();
+      }
+    }
+  }
 }
diff -r 275780da54ae -r 2ad22b2970a2 OrthancServer/DatabaseWrapperBase.h
--- a/OrthancServer/DatabaseWrapperBase.h	Tue Oct 13 16:57:55 2015 +0200
+++ b/OrthancServer/DatabaseWrapperBase.h	Tue Oct 13 17:48:30 2015 +0200
@@ -42,6 +42,7 @@
 #include "ServerEnumerations.h"
 
 #include <list>
+#include <boost/noncopyable.hpp>
 
 
 namespace Orthanc
@@ -51,7 +52,7 @@
    * database plugin whose code is in
    * "../Plugins/Samples/DatabasePlugin".
    **/
-  class DatabaseWrapperBase
+  class DatabaseWrapperBase : public boost::noncopyable
   {
   private:
     SQLite::Connection&  db_;
@@ -192,6 +193,9 @@
 
     void LookupIdentifier(std::list<int64_t>& target,
                           const std::string& value);
+
+    void StoreStudyModule(int64_t id,
+                          const DicomMap& module);
   };
 }
 
diff -r 275780da54ae -r 2ad22b2970a2 OrthancServer/IDatabaseWrapper.h
--- a/OrthancServer/IDatabaseWrapper.h	Tue Oct 13 16:57:55 2015 +0200
+++ b/OrthancServer/IDatabaseWrapper.h	Tue Oct 13 17:48:30 2015 +0200
@@ -193,5 +193,8 @@
 
     virtual void Upgrade(unsigned int targetVersion,
                          IStorageArea& storageArea) = 0;
+
+    virtual void StoreStudyModule(int64_t id,
+                                  const DicomMap& module) = 0;
   };
 }
diff -r 275780da54ae -r 2ad22b2970a2 OrthancServer/PrepareDatabase.sql
--- a/OrthancServer/PrepareDatabase.sql	Tue Oct 13 16:57:55 2015 +0200
+++ b/OrthancServer/PrepareDatabase.sql	Tue Oct 13 17:48:30 2015 +0200
@@ -27,6 +27,15 @@
        PRIMARY KEY(id, tagGroup, tagElement)
        );
 
+-- The following table was added in Orthanc 0.9.5 (database v6)
+CREATE TABLE SearchableStudies(
+       id INTEGER REFERENCES Resources(internalId) ON DELETE CASCADE,
+       tagGroup INTEGER,
+       tagElement INTEGER,
+       value TEXT,  -- assumed to be in upper case
+       PRIMARY KEY(id, tagGroup, tagElement)
+       );
+
 CREATE TABLE Metadata(
        id INTEGER REFERENCES Resources(internalId) ON DELETE CASCADE,
        type INTEGER,
@@ -86,6 +95,10 @@
 CREATE INDEX DicomIdentifiersIndex2 ON DicomIdentifiers(tagGroup, tagElement);
 CREATE INDEX DicomIdentifiersIndexValues ON DicomIdentifiers(value COLLATE BINARY);
 
+-- The 2 following indexes were added in Orthanc 0.9.5 (database v6)
+CREATE INDEX SearchableStudiesIndex1 ON SearchableStudies(id);
+CREATE INDEX SearchableStudiesIndexValues ON SearchableStudies(value COLLATE BINARY);
+
 CREATE INDEX ChangesIndex ON Changes(internalId);
 
 CREATE TRIGGER AttachedFileDeleted
diff -r 275780da54ae -r 2ad22b2970a2 OrthancServer/ServerIndex.cpp
--- a/OrthancServer/ServerIndex.cpp	Tue Oct 13 16:57:55 2015 +0200
+++ b/OrthancServer/ServerIndex.cpp	Tue Oct 13 17:48:30 2015 +0200
@@ -687,7 +687,14 @@
       {
         study = CreateResource(hasher.HashStudy(), ResourceType_Study);
         Toolbox::SetMainDicomTags(db_, study, ResourceType_Study, dicomSummary, true);
-        Toolbox::SetMainDicomTags(db_, study, ResourceType_Patient, dicomSummary, false);  // New in version 0.9.5 (db v6)
+
+        // New in version 0.9.5 (db v6)
+        Toolbox::SetMainDicomTags(db_, study, ResourceType_Patient, dicomSummary, false);
+
+        DicomMap module;
+        Toolbox::ExtractModule(module, dicomSummary, DicomModule_Patient, true  /* normalize */);
+        Toolbox::ExtractModule(module, dicomSummary, DicomModule_Study, true  /* normalize */);
+        db_.StoreStudyModule(study, module);
       }
 
       // Create the patient if needed
diff -r 275780da54ae -r 2ad22b2970a2 OrthancServer/ServerToolbox.cpp
--- a/OrthancServer/ServerToolbox.cpp	Tue Oct 13 16:57:55 2015 +0200
+++ b/OrthancServer/ServerToolbox.cpp	Tue Oct 13 17:48:30 2015 +0200
@@ -307,11 +307,19 @@
             break;
 
           case ResourceType_Study:
+          {
             Toolbox::SetMainDicomTags(database, resource, ResourceType_Study, dicomSummary, true);
 
             // Duplicate the patient tags at the study level (new in Orthanc 0.9.5 - db v6)
             Toolbox::SetMainDicomTags(database, resource, ResourceType_Patient, dicomSummary, false);
+
+            DicomMap module;
+            Toolbox::ExtractModule(module, dicomSummary, DicomModule_Patient, true  /* normalize */);
+            Toolbox::ExtractModule(module, dicomSummary, DicomModule_Study, true  /* normalize */);
+            database.StoreStudyModule(resource, module);
+
             break;
+          }
 
           case ResourceType_Series:
             Toolbox::SetMainDicomTags(database, resource, ResourceType_Series, dicomSummary, true);
@@ -326,5 +334,34 @@
         }
       }
     }
+
+
+    void ExtractModule(DicomMap& result,   // WARNING: Will not be cleared!
+                       const DicomMap& summary,
+                       DicomModule module,
+                       bool normalize)
+    {
+      typedef std::set<DicomTag> ModuleTags;
+      ModuleTags moduleTags;
+      DicomTag::AddTagsForModule(moduleTags, module);
+
+      for (ModuleTags::const_iterator tag = moduleTags.begin(); tag != moduleTags.end(); ++tag)
+      {
+        const DicomValue* value = summary.TestAndGetValue(*tag);
+        if (value != NULL &&
+            !value->IsNull())
+        {
+          std::string t = value->AsString();
+
+          if (normalize)
+          {
+            t = StripSpaces(ConvertToAscii(t));
+            ToUpperCase(t);
+          }
+
+          result.SetValue(*tag, t);
+        }      
+      }
+    }
   }
 }
diff -r 275780da54ae -r 2ad22b2970a2 OrthancServer/ServerToolbox.h
--- a/OrthancServer/ServerToolbox.h	Tue Oct 13 16:57:55 2015 +0200
+++ b/OrthancServer/ServerToolbox.h	Tue Oct 13 17:48:30 2015 +0200
@@ -60,5 +60,10 @@
     void ReconstructMainDicomTags(IDatabaseWrapper& database,
                                   IStorageArea& storageArea,
                                   ResourceType level);
+
+    void ExtractModule(DicomMap& result,   // WARNING: Will not be cleared!
+                       const DicomMap& summary,
+                       DicomModule module,
+                       bool normalize);
   }
 }
diff -r 275780da54ae -r 2ad22b2970a2 OrthancServer/Upgrade5To6.sql
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/Upgrade5To6.sql	Tue Oct 13 17:48:30 2015 +0200
@@ -0,0 +1,21 @@
+-- This SQLite script updates the version of the Orthanc database from 5 to 6.
+
+
+-- Add a new table to enable full-text indexed search over studies
+
+CREATE TABLE SearchableStudies(
+       id INTEGER REFERENCES Resources(internalId) ON DELETE CASCADE,
+       tagGroup INTEGER,
+       tagElement INTEGER,
+       value TEXT,  -- assumed to be in upper case
+       PRIMARY KEY(id, tagGroup, tagElement)
+       );
+
+CREATE INDEX SearchableStudiesIndex1 ON SearchableStudies(id);
+CREATE INDEX SearchableStudiesIndexValues ON SearchableStudies(value COLLATE BINARY);
+
+
+-- Change the database version
+-- The "1" corresponds to the "GlobalProperty_DatabaseSchemaVersion" enumeration
+
+UPDATE GlobalProperties SET value="6" WHERE property=1;
diff -r 275780da54ae -r 2ad22b2970a2 Plugins/Engine/OrthancPluginDatabase.cpp
--- a/Plugins/Engine/OrthancPluginDatabase.cpp	Tue Oct 13 16:57:55 2015 +0200
+++ b/Plugins/Engine/OrthancPluginDatabase.cpp	Tue Oct 13 17:48:30 2015 +0200
@@ -869,6 +869,14 @@
   }
 
 
+  void OrthancPluginDatabase::StoreStudyModule(int64_t id,
+                                               const DicomMap& module)
+  {
+    // TODO
+    throw OrthancException(ErrorCode_NotImplemented);
+  }
+
+
   void OrthancPluginDatabase::AnswerReceived(const _OrthancPluginDatabaseAnswer& answer)
   {
     if (answer.type == _OrthancPluginDatabaseAnswerType_None)
diff -r 275780da54ae -r 2ad22b2970a2 Plugins/Engine/OrthancPluginDatabase.h
--- a/Plugins/Engine/OrthancPluginDatabase.h	Tue Oct 13 16:57:55 2015 +0200
+++ b/Plugins/Engine/OrthancPluginDatabase.h	Tue Oct 13 17:48:30 2015 +0200
@@ -254,6 +254,9 @@
     virtual void Upgrade(unsigned int targetVersion,
                          IStorageArea& storageArea);
 
+    virtual void StoreStudyModule(int64_t id,
+                                  const DicomMap& module);
+
     void AnswerReceived(const _OrthancPluginDatabaseAnswer& answer);
   };
 }