Mercurial > hg > orthanc-databases
comparison Resources/Orthanc/Databases/ISqlLookupFormatter.cpp @ 398:8dedfd982b83 db-protobuf
implemented lookup for labels in postgresql
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 06 Apr 2023 19:09:51 +0200 |
parents | c4f0f8087564 |
children | a8774581adfc |
comparison
equal
deleted
inserted
replaced
397:c4f0f8087564 | 398:8dedfd982b83 |
---|---|
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>& withLabels, | |
311 const std::set<std::string>& withoutLabels, | |
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 } |
349 | 384 |
350 sql += (joins + " WHERE " + FormatLevel(queryLevel) + ".resourceType = " + | 385 std::list<std::string> where; |
351 formatter.FormatResourceType(queryLevel) + comparisons); | 386 |
387 if (!withLabels.empty()) | |
388 { | |
389 std::list<std::string> labels; | |
390 for (std::set<std::string>::const_iterator it = withLabels.begin(); it != withLabels.end(); ++it) | |
391 { | |
392 labels.push_back(formatter.GenerateParameter(*it)); | |
393 } | |
394 | |
395 where.push_back(boost::lexical_cast<std::string>(withLabels.size()) + | |
396 " = (SELECT COUNT(1) FROM Labels WHERE internalId = " + FormatLevel(queryLevel) + | |
397 ".internalId AND label IN (" + Join(labels, "", ", ") + "))"); | |
398 } | |
399 | |
400 if (!withoutLabels.empty()) | |
401 { | |
402 /** | |
403 * "In SQL Server, NOT EXISTS and NOT IN predicates are the best | |
404 * way to search for missing values, as long as both columns in | |
405 * question are NOT NULL." | |
406 * https://explainextended.com/2009/09/15/not-in-vs-not-exists-vs-left-join-is-null-sql-server/ | |
407 **/ | |
408 std::list<std::string> labels; | |
409 for (std::set<std::string>::const_iterator it = withoutLabels.begin(); it != withoutLabels.end(); ++it) | |
410 { | |
411 labels.push_back(formatter.GenerateParameter(*it)); | |
412 } | |
413 | |
414 where.push_back("NOT EXISTS (SELECT 1 FROM Labels WHERE internalId = " + FormatLevel(queryLevel) + | |
415 ".internalId AND label IN (" + Join(labels, "", ", ") + "))"); | |
416 } | |
417 | |
418 where.push_back(FormatLevel(queryLevel) + ".resourceType = " + | |
419 formatter.FormatResourceType(queryLevel) + comparisons); | |
420 | |
421 sql += joins + Join(where, " WHERE ", " AND "); | |
352 | 422 |
353 if (limit != 0) | 423 if (limit != 0) |
354 { | 424 { |
355 sql += " LIMIT " + boost::lexical_cast<std::string>(limit); | 425 sql += " LIMIT " + boost::lexical_cast<std::string>(limit); |
356 } | 426 } |