Mercurial > hg > orthanc-databases
comparison Framework/Plugins/IndexBackend.cpp @ 501:594859656a06 large-queries
Added support for ExtendedApiV1: /changes
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Thu, 11 Apr 2024 18:52:42 +0200 |
parents | f0976163dbe1 |
children | 2ab3d45c0b3c |
comparison
equal
deleted
inserted
replaced
500:c27071770c04 | 501:594859656a06 |
---|---|
114 void IndexBackend::ReadChangesInternal(IDatabaseBackendOutput& output, | 114 void IndexBackend::ReadChangesInternal(IDatabaseBackendOutput& output, |
115 bool& done, | 115 bool& done, |
116 DatabaseManager& manager, | 116 DatabaseManager& manager, |
117 DatabaseManager::CachedStatement& statement, | 117 DatabaseManager::CachedStatement& statement, |
118 const Dictionary& args, | 118 const Dictionary& args, |
119 uint32_t limit) | 119 uint32_t limit, |
120 { | 120 bool returnFirstResults) |
121 { | |
122 struct Change | |
123 { | |
124 int64_t seq_; | |
125 int32_t changeType_; | |
126 OrthancPluginResourceType resourceType_; | |
127 std::string publicId_; | |
128 std::string changeDate_; | |
129 | |
130 Change(int64_t seq, int32_t changeType, OrthancPluginResourceType resourceType, const std::string& publicId, const std::string& changeDate) | |
131 : seq_(seq), changeType_(changeType), resourceType_(resourceType), publicId_(publicId), changeDate_(changeDate) | |
132 { | |
133 } | |
134 }; | |
135 | |
121 statement.Execute(args); | 136 statement.Execute(args); |
122 | 137 |
123 uint32_t count = 0; | 138 std::list<Change> changes; |
124 | 139 while (!statement.IsDone()) |
125 while (count < limit && | 140 { |
126 !statement.IsDone()) | 141 changes.push_back(Change( |
127 { | |
128 output.AnswerChange( | |
129 statement.ReadInteger64(0), | 142 statement.ReadInteger64(0), |
130 statement.ReadInteger32(1), | 143 statement.ReadInteger32(1), |
131 static_cast<OrthancPluginResourceType>(statement.ReadInteger32(2)), | 144 static_cast<OrthancPluginResourceType>(statement.ReadInteger32(2)), |
132 statement.ReadString(3), | 145 statement.ReadString(3), |
133 statement.ReadString(4)); | 146 statement.ReadString(4) |
147 )); | |
134 | 148 |
135 statement.Next(); | 149 statement.Next(); |
136 count++; | 150 } |
137 } | 151 |
138 | 152 done = changes.size() <= limit; // 'done' means we have returned all requested changes |
139 done = (count < limit || | 153 |
140 statement.IsDone()); | 154 // if we have retrieved more changes than requested -> cleanup |
155 if (changes.size() > limit) | |
156 { | |
157 assert(changes.size() == limit+1); // the statement should only request 1 element more | |
158 | |
159 if (returnFirstResults) | |
160 { | |
161 changes.pop_back(); | |
162 } | |
163 else | |
164 { | |
165 changes.pop_front(); | |
166 } | |
167 } | |
168 | |
169 for (std::list<Change>::const_iterator it = changes.begin(); it != changes.end(); ++it) | |
170 { | |
171 output.AnswerChange(it->seq_, it->changeType_, it->resourceType_, it->publicId_, it->changeDate_); | |
172 } | |
141 } | 173 } |
142 | 174 |
143 | 175 |
144 void IndexBackend::ReadExportedResourcesInternal(IDatabaseBackendOutput& output, | 176 void IndexBackend::ReadExportedResourcesInternal(IDatabaseBackendOutput& output, |
145 bool& done, | 177 bool& done, |
551 args.SetIntegerValue("since", since); | 583 args.SetIntegerValue("since", since); |
552 | 584 |
553 ReadListOfStrings(target, statement, args); | 585 ReadListOfStrings(target, statement, args); |
554 } | 586 } |
555 | 587 |
556 | |
557 /* Use GetOutput().AnswerChange() */ | |
558 void IndexBackend::GetChanges(IDatabaseBackendOutput& output, | 588 void IndexBackend::GetChanges(IDatabaseBackendOutput& output, |
559 bool& done /*out*/, | 589 bool& done /*out*/, |
560 DatabaseManager& manager, | 590 DatabaseManager& manager, |
561 int64_t since, | 591 int64_t since, |
562 uint32_t limit) | 592 uint32_t limit) |
563 { | 593 { |
564 std::string suffix; | 594 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 13, 0) |
595 GetChanges2(output, done, manager, since, -1, _OrthancPluginChangeType_All, limit); | |
596 #else | |
597 GetChanges2(output, done, manager, since, -1, 65535, limit); | |
598 #endif | |
599 } | |
600 | |
601 /* Use GetOutput().AnswerChange() */ | |
602 void IndexBackend::GetChanges2(IDatabaseBackendOutput& output, | |
603 bool& done /*out*/, | |
604 DatabaseManager& manager, | |
605 int64_t since, | |
606 int64_t to, | |
607 int32_t changeType, | |
608 uint32_t limit) | |
609 { | |
610 std::string limitSuffix; | |
565 if (manager.GetDialect() == Dialect_MSSQL) | 611 if (manager.GetDialect() == Dialect_MSSQL) |
566 { | 612 { |
567 suffix = "OFFSET 0 ROWS FETCH FIRST ${limit} ROWS ONLY"; | 613 limitSuffix = "OFFSET 0 ROWS FETCH FIRST ${limit} ROWS ONLY"; |
568 } | 614 } |
569 else | 615 else |
570 { | 616 { |
571 suffix = "LIMIT ${limit}"; | 617 limitSuffix = "LIMIT ${limit}"; |
572 } | 618 } |
573 | 619 |
574 DatabaseManager::CachedStatement statement( | 620 std::vector<std::string> filters; |
575 STATEMENT_FROM_HERE, manager, | 621 bool hasSince = false; |
576 "SELECT Changes.seq, Changes.changeType, Changes.resourceType, Resources.publicId, " | 622 bool hasTo = false; |
577 "Changes.date FROM Changes INNER JOIN Resources " | 623 bool hasFilterType = false; |
578 "ON Changes.internalId = Resources.internalId WHERE seq>${since} ORDER BY seq " + suffix); | 624 |
579 | 625 if (since > 0) |
626 { | |
627 hasSince = true; | |
628 filters.push_back("seq>${since}"); | |
629 } | |
630 if (to != -1) | |
631 { | |
632 hasTo = true; | |
633 filters.push_back("seq<=${to}"); | |
634 } | |
635 if (changeType != _OrthancPluginChangeType_All) | |
636 { | |
637 hasFilterType = true; | |
638 filters.push_back("changeType=${changeType}"); | |
639 } | |
640 | |
641 std::string filtersString; | |
642 if (filters.size() > 0) | |
643 { | |
644 Orthanc::Toolbox::JoinStrings(filtersString, filters, " AND "); | |
645 filtersString = "WHERE " + filtersString; | |
646 } | |
647 | |
648 std::string sql; | |
649 bool returnFirstResults; | |
650 if (hasTo && !hasSince) | |
651 { | |
652 // in this case, we want the largest values but we want them ordered in ascending order | |
653 sql = "SELECT * FROM (SELECT Changes.seq, Changes.changeType, Changes.resourceType, Resources.publicId, Changes.date " | |
654 "FROM Changes INNER JOIN Resources " | |
655 "ON Changes.internalId = Resources.internalId " + filtersString + " ORDER BY seq DESC " + limitSuffix + | |
656 ") AS FilteredChanges ORDER BY seq ASC"; | |
657 | |
658 returnFirstResults = false; | |
659 } | |
660 else | |
661 { | |
662 // default query: we want the smallest values ordered in ascending order | |
663 sql = "SELECT Changes.seq, Changes.changeType, Changes.resourceType, Resources.publicId, " | |
664 "Changes.date FROM Changes INNER JOIN Resources " | |
665 "ON Changes.internalId = Resources.internalId " + filtersString + " ORDER BY seq ASC " + limitSuffix; | |
666 returnFirstResults = true; | |
667 } | |
668 | |
669 DatabaseManager::CachedStatement statement(STATEMENT_FROM_HERE_DYNAMIC(sql), manager, sql); | |
580 statement.SetReadOnly(true); | 670 statement.SetReadOnly(true); |
671 Dictionary args; | |
672 | |
581 statement.SetParameterType("limit", ValueType_Integer64); | 673 statement.SetParameterType("limit", ValueType_Integer64); |
582 statement.SetParameterType("since", ValueType_Integer64); | 674 args.SetIntegerValue("limit", limit + 1); // we take limit+1 because we use the +1 to know if "Done" must be set to true |
583 | 675 |
584 Dictionary args; | 676 if (hasSince) |
585 args.SetIntegerValue("limit", limit + 1); | 677 { |
586 args.SetIntegerValue("since", since); | 678 statement.SetParameterType("since", ValueType_Integer64); |
587 | 679 args.SetIntegerValue("since", since); |
588 ReadChangesInternal(output, done, manager, statement, args, limit); | 680 } |
681 | |
682 if (hasTo) | |
683 { | |
684 statement.SetParameterType("to", ValueType_Integer64); | |
685 args.SetIntegerValue("to", to); | |
686 } | |
687 | |
688 if (hasFilterType) | |
689 { | |
690 statement.SetParameterType("changeType", ValueType_Integer64); | |
691 args.SetIntegerValue("changeType", changeType); | |
692 } | |
693 | |
694 ReadChangesInternal(output, done, manager, statement, args, limit, returnFirstResults); | |
589 } | 695 } |
590 | 696 |
591 | 697 |
592 void IndexBackend::GetChildrenInternalId(std::list<int64_t>& target /*out*/, | 698 void IndexBackend::GetChildrenInternalId(std::list<int64_t>& target /*out*/, |
593 DatabaseManager& manager, | 699 DatabaseManager& manager, |
683 statement.SetReadOnly(true); | 789 statement.SetReadOnly(true); |
684 | 790 |
685 Dictionary args; | 791 Dictionary args; |
686 | 792 |
687 bool done; // Ignored | 793 bool done; // Ignored |
688 ReadChangesInternal(output, done, manager, statement, args, 1); | 794 ReadChangesInternal(output, done, manager, statement, args, 1, true); |
689 } | 795 } |
690 | 796 |
691 | 797 |
692 /* Use GetOutput().AnswerExportedResource() */ | 798 /* Use GetOutput().AnswerExportedResource() */ |
693 void IndexBackend::GetLastExportedResource(IDatabaseBackendOutput& output, | 799 void IndexBackend::GetLastExportedResource(IDatabaseBackendOutput& output, |