comparison Resources/Orthanc/Databases/ISqlLookupFormatter.cpp @ 405:1938ba8fba35

sync
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sat, 15 Apr 2023 13:49:53 +0200
parents 1280b40d6696
children de6de66d70b2
comparison
equal deleted inserted replaced
394:2fd272ea8f00 405:1938ba8fba35
1 /** 1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store 2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium 4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2022 Osimis S.A., Belgium 5 * Copyright (C) 2017-2023 Osimis S.A., Belgium
6 * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium 6 * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
7 * 7 *
8 * This program is free software: you can redistribute it and/or 8 * This program is free software: you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as 9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation, either version 3 of the 10 * published by the Free Software Foundation, either version 3 of the
11 * License, or (at your option) any later version. 11 * License, or (at your option) any later version.
37 #endif 37 #endif
38 38
39 #include "DatabaseConstraint.h" 39 #include "DatabaseConstraint.h"
40 40
41 #include <boost/lexical_cast.hpp> 41 #include <boost/lexical_cast.hpp>
42 #include <list>
42 43
43 44
44 namespace Orthanc 45 namespace Orthanc
45 { 46 {
46 static std::string FormatLevel(ResourceType level) 47 static std::string FormatLevel(ResourceType level)
266 ".internalId AND " + tag + ".tagGroup = " + 267 ".internalId AND " + tag + ".tagGroup = " +
267 boost::lexical_cast<std::string>(constraint.GetTag().GetGroup()) + 268 boost::lexical_cast<std::string>(constraint.GetTag().GetGroup()) +
268 " AND " + tag + ".tagElement = " + 269 " AND " + tag + ".tagElement = " +
269 boost::lexical_cast<std::string>(constraint.GetTag().GetElement())); 270 boost::lexical_cast<std::string>(constraint.GetTag().GetElement()));
270 } 271 }
272
273
274 static std::string Join(const std::list<std::string>& values,
275 const std::string& prefix,
276 const std::string& separator)
277 {
278 if (values.empty())
279 {
280 return "";
281 }
282 else
283 {
284 std::string s = prefix;
285
286 bool first = true;
287 for (std::list<std::string>::const_iterator it = values.begin(); it != values.end(); ++it)
288 {
289 if (first)
290 {
291 first = false;
292 }
293 else
294 {
295 s += separator;
296 }
297
298 s += *it;
299 }
300
301 return s;
302 }
303 }
271 304
272 305
273 void ISqlLookupFormatter::Apply(std::string& sql, 306 void ISqlLookupFormatter::Apply(std::string& sql,
274 ISqlLookupFormatter& formatter, 307 ISqlLookupFormatter& formatter,
275 const std::vector<DatabaseConstraint>& lookup, 308 const std::vector<DatabaseConstraint>& lookup,
276 ResourceType queryLevel, 309 ResourceType queryLevel,
310 const std::set<std::string>& labels,
311 LabelsConstraint labelsConstraint,
277 size_t limit) 312 size_t limit)
278 { 313 {
279 assert(ResourceType_Patient < ResourceType_Study && 314 assert(ResourceType_Patient < ResourceType_Study &&
280 ResourceType_Study < ResourceType_Series && 315 ResourceType_Study < ResourceType_Series &&
281 ResourceType_Series < ResourceType_Instance); 316 ResourceType_Series < ResourceType_Instance);
344 sql += (" INNER JOIN Resources " + 379 sql += (" INNER JOIN Resources " +
345 FormatLevel(static_cast<ResourceType>(level)) + " ON " + 380 FormatLevel(static_cast<ResourceType>(level)) + " ON " +
346 FormatLevel(static_cast<ResourceType>(level - 1)) + ".internalId=" + 381 FormatLevel(static_cast<ResourceType>(level - 1)) + ".internalId=" +
347 FormatLevel(static_cast<ResourceType>(level)) + ".parentId"); 382 FormatLevel(static_cast<ResourceType>(level)) + ".parentId");
348 } 383 }
384
385 std::list<std::string> where;
386 where.push_back(FormatLevel(queryLevel) + ".resourceType = " +
387 formatter.FormatResourceType(queryLevel) + comparisons);
388
389 if (!labels.empty())
390 {
391 /**
392 * "In SQL Server, NOT EXISTS and NOT IN predicates are the best
393 * way to search for missing values, as long as both columns in
394 * question are NOT NULL."
395 * https://explainextended.com/2009/09/15/not-in-vs-not-exists-vs-left-join-is-null-sql-server/
396 **/
397
398 std::list<std::string> formattedLabels;
399 for (std::set<std::string>::const_iterator it = labels.begin(); it != labels.end(); ++it)
400 {
401 formattedLabels.push_back(formatter.GenerateParameter(*it));
402 }
403
404 std::string condition;
405 switch (labelsConstraint)
406 {
407 case LabelsConstraint_Any:
408 condition = "> 0";
409 break;
410
411 case LabelsConstraint_All:
412 condition = "= " + boost::lexical_cast<std::string>(labels.size());
413 break;
414
415 case LabelsConstraint_None:
416 condition = "= 0";
417 break;
418
419 default:
420 throw OrthancException(ErrorCode_ParameterOutOfRange);
421 }
349 422
350 sql += (joins + " WHERE " + FormatLevel(queryLevel) + ".resourceType = " + 423 where.push_back("(SELECT COUNT(1) FROM Labels AS selectedLabels WHERE selectedLabels.id = " + FormatLevel(queryLevel) +
351 formatter.FormatResourceType(queryLevel) + comparisons); 424 ".internalId AND selectedLabels.label IN (" + Join(formattedLabels, "", ", ") + ")) " + condition);
425 }
426
427 sql += joins + Join(where, " WHERE ", " AND ");
352 428
353 if (limit != 0) 429 if (limit != 0)
354 { 430 {
355 sql += " LIMIT " + boost::lexical_cast<std::string>(limit); 431 sql += " LIMIT " + boost::lexical_cast<std::string>(limit);
356 } 432 }