comparison OrthancServer/Sources/Search/ISqlLookupFormatter.cpp @ 5825:881cd0965146 find-refactoring

added OrderBy support in SQLite
author Alain Mazy <am@orthanc.team>
date Fri, 04 Oct 2024 19:03:14 +0200
parents 16ce3c920f71
children d73dfb4548c6
comparison
equal deleted inserted replaced
5819:7c2b4fa94633 5825:881cd0965146
26 26
27 #include "../../../OrthancFramework/Sources/OrthancException.h" 27 #include "../../../OrthancFramework/Sources/OrthancException.h"
28 #include "../../../OrthancFramework/Sources/Toolbox.h" 28 #include "../../../OrthancFramework/Sources/Toolbox.h"
29 #include "../Database/FindRequest.h" 29 #include "../Database/FindRequest.h"
30 #include "DatabaseConstraint.h" 30 #include "DatabaseConstraint.h"
31 #include "../Database/MainDicomTagsRegistry.h"
31 32
32 #include <cassert> 33 #include <cassert>
33 #include <boost/lexical_cast.hpp> 34 #include <boost/lexical_cast.hpp>
34 #include <list> 35 #include <list>
35 36
261 " AND " + tag + ".tagElement = " + 262 " AND " + tag + ".tagElement = " +
262 boost::lexical_cast<std::string>(constraint.GetTag().GetElement())); 263 boost::lexical_cast<std::string>(constraint.GetTag().GetElement()));
263 } 264 }
264 265
265 266
267 static void FormatJoinForOrdering(std::string& target,
268 const DicomTag& tag,
269 size_t index,
270 ResourceType requestLevel)
271 {
272 std::string orderArg = "order" + boost::lexical_cast<std::string>(index);
273
274 target.clear();
275
276 ResourceType tagLevel;
277 DicomTagType tagType;
278 MainDicomTagsRegistry registry;
279
280 registry.LookupTag(tagLevel, tagType, tag);
281
282 if (tagLevel == ResourceType_Patient && requestLevel == ResourceType_Study)
283 { // Patient tags are copied at study level
284 tagLevel = ResourceType_Study;
285 }
286
287 std::string tagTable;
288 if (tagType == DicomTagType_Identifier)
289 {
290 tagTable = "DicomIdentifiers ";
291 }
292 else
293 {
294 tagTable = "MainDicomTags ";
295 }
296
297 std::string tagFilter = orderArg + ".tagGroup = " + boost::lexical_cast<std::string>(tag.GetGroup()) + " AND " + orderArg + ".tagElement = " + boost::lexical_cast<std::string>(tag.GetElement());
298
299 if (tagLevel == requestLevel)
300 {
301 target = " LEFT JOIN " + tagTable + " " + orderArg + " ON " + orderArg + ".id = " + FormatLevel(requestLevel) +
302 ".internalId AND " + tagFilter;
303 }
304 else if (static_cast<int32_t>(requestLevel) - static_cast<int32_t>(tagLevel) == 1)
305 {
306 target = " INNER JOIN Resources " + orderArg + "parent ON " + orderArg + "parent.internalId = " + FormatLevel(requestLevel) + ".parentId "
307 " LEFT JOIN " + tagTable + " " + orderArg + " ON " + orderArg + ".id = " + orderArg + "parent.internalId AND " + tagFilter;
308 }
309 else if (static_cast<int32_t>(requestLevel) - static_cast<int32_t>(tagLevel) == 2)
310 {
311 target = " INNER JOIN Resources " + orderArg + "parent ON " + orderArg + "parent.internalId = " + FormatLevel(requestLevel) + ".parentId "
312 " INNER JOIN Resources " + orderArg + "grandparent ON " + orderArg + "grandparent.internalId = " + orderArg + "parent.parentId "
313 " LEFT JOIN " + tagTable + " " + orderArg + " ON " + orderArg + ".id = " + orderArg + "grandparent.internalId AND " + tagFilter;
314 }
315 else if (static_cast<int32_t>(requestLevel) - static_cast<int32_t>(tagLevel) == 3)
316 {
317 target = " INNER JOIN Resources " + orderArg + "parent ON " + orderArg + "parent.internalId = " + FormatLevel(requestLevel) + ".parentId "
318 " INNER JOIN Resources " + orderArg + "grandparent ON " + orderArg + "grandparent.internalId = " + orderArg + "parent.parentId "
319 " INNER JOIN Resources " + orderArg + "grandgrandparent ON " + orderArg + "grandgrandparent.internalId = " + orderArg + "grandparent.parentId "
320 " LEFT JOIN " + tagTable + " " + orderArg + " ON " + orderArg + ".id = " + orderArg + "grandgrandparent.internalId AND " + tagFilter;
321 }
322 }
323
324 static void FormatJoinForOrdering(std::string& target,
325 const MetadataType& metadata,
326 size_t index,
327 ResourceType requestLevel)
328 {
329 std::string arg = "order" + boost::lexical_cast<std::string>(index);
330
331
332 target = " INNER JOIN Metadata " + arg + " ON " + arg + ".id = " + FormatLevel(requestLevel) +
333 ".internalId AND " + arg + ".type = " +
334 boost::lexical_cast<std::string>(metadata);
335 }
336
266 static std::string Join(const std::list<std::string>& values, 337 static std::string Join(const std::list<std::string>& values,
267 const std::string& prefix, 338 const std::string& prefix,
268 const std::string& separator) 339 const std::string& separator)
269 { 340 {
270 if (values.empty()) 341 if (values.empty())
617 GetLookupLevels(lowerLevel, upperLevel, queryLevel, request.GetDicomTagConstraints()); 688 GetLookupLevels(lowerLevel, upperLevel, queryLevel, request.GetDicomTagConstraints());
618 689
619 assert(upperLevel <= queryLevel && 690 assert(upperLevel <= queryLevel &&
620 queryLevel <= lowerLevel); 691 queryLevel <= lowerLevel);
621 692
622 std::string ordering = "row_number() over (order by " + strQueryLevel + ".publicId) as rowNumber"; // we need a default ordering in order to make default queries repeatable when using since&limit 693 std::string ordering;
623 694 std::string orderingJoins;
695
696 if (request.GetOrdering().size() > 0)
697 {
698 int counter = 0;
699 std::vector<std::string> orderByFields;
700 for (std::deque<FindRequest::Ordering*>::const_iterator it = request.GetOrdering().begin(); it != request.GetOrdering().end(); ++it)
701 {
702 std::string orderingJoin;
703
704 switch ((*it)->GetKeyType())
705 {
706 case FindRequest::KeyType_DicomTag:
707 FormatJoinForOrdering(orderingJoin, (*it)->GetDicomTag(), counter, request.GetLevel());
708 break;
709 case FindRequest::KeyType_Metadata:
710 FormatJoinForOrdering(orderingJoin, (*it)->GetMetadataType(), counter, request.GetLevel());
711 break;
712 default:
713 throw OrthancException(ErrorCode_InternalError);
714 }
715 orderingJoins += orderingJoin;
716
717 std::string orderByField = "order" + boost::lexical_cast<std::string>(counter) + ".value";
718 if ((*it)->GetDirection() == FindRequest::OrderingDirection_Ascending)
719 {
720 orderByField += " ASC";
721 }
722 else
723 {
724 orderByField += " DESC";
725 }
726 orderByFields.push_back(orderByField);
727 ++counter;
728 }
729
730 std::string orderByFieldsString;
731 Toolbox::JoinStrings(orderByFieldsString, orderByFields, ", ");
732 ordering = "ROW_NUMBER() OVER (ORDER BY " + orderByFieldsString + ") AS rowNumber";
733 }
734 else
735 {
736 ordering = "ROW_NUMBER() OVER (ORDER BY " + strQueryLevel + ".publicId) AS rowNumber"; // we need a default ordering in order to make default queries repeatable when using since&limit
737 }
624 738
625 sql = ("SELECT " + 739 sql = ("SELECT " +
626 strQueryLevel + ".publicId, " + 740 strQueryLevel + ".publicId, " +
627 strQueryLevel + ".internalId, " + 741 strQueryLevel + ".internalId, " +
628 ordering + 742 ordering +
736 850
737 where.push_back("(SELECT COUNT(1) FROM Labels AS selectedLabels WHERE selectedLabels.id = " + strQueryLevel + 851 where.push_back("(SELECT COUNT(1) FROM Labels AS selectedLabels WHERE selectedLabels.id = " + strQueryLevel +
738 ".internalId AND selectedLabels.label IN (" + Join(formattedLabels, "", ", ") + ")) " + condition); 852 ".internalId AND selectedLabels.label IN (" + Join(formattedLabels, "", ", ") + ")) " + condition);
739 } 853 }
740 854
741 sql += joins + Join(where, " WHERE ", " AND "); 855 sql += joins + orderingJoins + Join(where, " WHERE ", " AND ");
742 856
743 if (request.HasLimits()) 857 if (request.HasLimits())
744 { 858 {
745 sql += formatter.FormatLimits(request.GetLimitsSince(), request.GetLimitsCount()); 859 sql += formatter.FormatLimits(request.GetLimitsSince(), request.GetLimitsCount());
746 } 860 }