diff OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp @ 5568:b0b5546f1b9f find-refactoring

find refactor: re-use existing code. /studies?expand is almost fully implemented with new code
author Alain Mazy <am@orthanc.team>
date Thu, 25 Apr 2024 09:22:07 +0200
parents f3562c1a150d
children 5a13483d12c5
line wrap: on
line diff
--- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Tue Apr 23 16:49:44 2024 +0200
+++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Thu Apr 25 09:22:07 2024 +0200
@@ -1141,9 +1141,10 @@
 
 
     virtual void ExecuteFind(FindResponse& response,
-                             const FindRequest& request) ORTHANC_OVERRIDE
+                             const FindRequest& request, 
+                             const std::vector<DatabaseConstraint>& normalized) ORTHANC_OVERRIDE
     {
-#if 1
+#if 0
       Compatibility::GenericFind find(*this);
       find.Execute(response, request);
 #else
@@ -1153,11 +1154,142 @@
       }
 
       {
-        std::string sql;
-        // sql = "CREATE TEMPORARY TABLE FilteredResourcesIds AS ";
-        sql = "..";
-        SQLite::Statement s(db_, SQLITE_FROM_HERE_DYNAMIC(sql), sql);
+
+        LookupFormatter formatter;
+
+        std::string sqlLookup;
+        LookupFormatter::Apply(sqlLookup, 
+                               formatter, 
+                               normalized, 
+                               request.GetLevel(),
+                               request.GetLabels(),
+                               request.GetLabelsConstraint(),
+                               (request.HasLimits() ? request.GetLimitsCount() : 0));  // TODO: handles since and count
+
+        if (request.IsResponseIdentifiersOnly())
+        {
+          SQLite::Statement statement(db_, SQLITE_FROM_HERE_DYNAMIC(sqlLookup), sqlLookup);
+          formatter.Bind(statement);
+
+          while (statement.Step())
+          {
+            FindResponse::Item* item = new FindResponse::Item(request.GetResponseContent(),
+                                                              request.GetLevel(),
+                                                              statement.ColumnString(0));
+            response.Add(item);
+          }
+        }
+        else
+        {
+          std::map<std::string, FindResponse::Item*> items;  // cache to the response items
+
+          {// first create a temporary table that with the filtered and ordered results
+            sqlLookup = "CREATE TEMPORARY TABLE FilteredResourcesIds AS " + sqlLookup;
+
+            SQLite::Statement statement(db_, SQLITE_FROM_HERE_DYNAMIC(sqlLookup), sqlLookup);
+            formatter.Bind(statement);
+            statement.Run();
+          }
+
+          {
+            // create the response item with the public ids only
+            SQLite::Statement statement(db_, SQLITE_FROM_HERE, "SELECT publicId FROM FilteredResourcesIds");
+            formatter.Bind(statement);
+
+            while (statement.Step())
+            {
+              const std::string& resourceId = statement.ColumnString(0);
+              FindResponse::Item* item = new FindResponse::Item(request.GetResponseContent(),
+                                                                request.GetLevel(),
+                                                                resourceId);
+              items[resourceId] = item;
+              response.Add(item);
+            }
+          }
+
+          // request Each response content through INNER JOIN with the temporary table
+          if (request.HasResponseContent(FindRequest::ResponseContent_MainDicomTags))
+          {
+            // TODO-FIND: handle the case where we request tags from multiple levels
+            SQLite::Statement statement(db_, SQLITE_FROM_HERE, 
+                                        "SELECT publicId, tagGroup, tagElement, value FROM MainDicomTags AS tags "
+                                        "  INNER JOIN FilteredResourcesIds  ON tags.id = FilteredResourcesIds.internalId");
+            formatter.Bind(statement);
+
+            while (statement.Step())
+            {
+              const std::string& resourceId = statement.ColumnString(0);
+              items[resourceId]->AddDicomTag(statement.ColumnInt(1),
+                                             statement.ColumnInt(2),
+                                             statement.ColumnString(3), false);
+            }
+          }
+
+          if (request.HasResponseContent(FindRequest::ResponseContent_Children))
+          {
+            SQLite::Statement statement(db_, SQLITE_FROM_HERE, 
+                "SELECT filtered.publicId, childLevel.publicId AS childPublicId "
+                "FROM Resources as currentLevel "
+                "    INNER JOIN FilteredResourcesIds filtered ON filtered.internalId = currentLevel.internalId "
+                "    INNER JOIN Resources childLevel ON childLevel.parentId = currentLevel.internalId");
+            formatter.Bind(statement);
+
+            while (statement.Step())
+            {
+              const std::string& resourceId = statement.ColumnString(0);
+              items[resourceId]->AddChild(statement.ColumnString(1));
+            }
+          }
+
+          if (request.HasResponseContent(FindRequest::ResponseContent_Parent))
+          {
+            SQLite::Statement statement(db_, SQLITE_FROM_HERE, 
+                "SELECT filtered.publicId, parentLevel.publicId AS parentPublicId "
+                "FROM Resources as currentLevel "
+                "    INNER JOIN FilteredResourcesIds filtered ON filtered.internalId = currentLevel.internalId "
+                "    INNER JOIN Resources parentLevel ON currentLevel.parentId = parentLevel.internalId");
+
+            while (statement.Step())
+            {
+              const std::string& resourceId = statement.ColumnString(0);
+              items[resourceId]->SetParent(statement.ColumnString(1));
+            }
+          }
+
+          if (request.HasResponseContent(FindRequest::ResponseContent_Metadata))
+          {
+            SQLite::Statement statement(db_, SQLITE_FROM_HERE, 
+                "SELECT filtered.publicId, metadata.type, metadata.value "
+                "FROM Metadata "
+                "  INNER JOIN FilteredResourcesIds filtered ON filtered.internalId = Metadata.id");
+
+            while (statement.Step())
+            {
+              const std::string& resourceId = statement.ColumnString(0);
+              items[resourceId]->AddMetadata(static_cast<MetadataType>(statement.ColumnInt(1)),
+                                             statement.ColumnString(2));
+            }
+          }
+
+          if (request.HasResponseContent(FindRequest::ResponseContent_Labels))
+          {
+            SQLite::Statement statement(db_, SQLITE_FROM_HERE, 
+                "SELECT filtered.publicId, label "
+                "FROM Labels "
+                "  INNER JOIN FilteredResourcesIds filtered ON filtered.internalId = Labels.id");
+
+            while (statement.Step())
+            {
+              const std::string& resourceId = statement.ColumnString(0);
+              items[resourceId]->AddLabel(statement.ColumnString(1));
+            }
+          }
+
+          // TODO-FIND: implement other responseContent: ResponseContent_ChildInstanceId, ResponseContent_Attachments, (later: ResponseContent_IsStable)
+
+        }
       }
+
 #endif
     }
   };