Mercurial > hg > orthanc
diff OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp @ 5758:ca06dde85358 large-queries
merged find-refactoring -> large-queries
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Thu, 05 Sep 2024 18:52:27 +0200 |
parents | 5463c3ae3235 89d559e67b03 |
children | de33982a0bf4 |
line wrap: on
line diff
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Thu Sep 05 18:21:56 2024 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Thu Sep 05 18:52:27 2024 +0200 @@ -304,113 +304,6 @@ } - class StatelessDatabaseOperations::MainDicomTagsRegistry : public boost::noncopyable - { - private: - class TagInfo - { - private: - ResourceType level_; - DicomTagType type_; - - public: - TagInfo() - { - } - - TagInfo(ResourceType level, - DicomTagType type) : - level_(level), - type_(type) - { - } - - ResourceType GetLevel() const - { - return level_; - } - - DicomTagType GetType() const - { - return type_; - } - }; - - typedef std::map<DicomTag, TagInfo> Registry; - - - Registry registry_; - - void LoadTags(ResourceType level) - { - { - const DicomTag* tags = NULL; - size_t size; - - ServerToolbox::LoadIdentifiers(tags, size, level); - - for (size_t i = 0; i < size; i++) - { - if (registry_.find(tags[i]) == registry_.end()) - { - registry_[tags[i]] = TagInfo(level, DicomTagType_Identifier); - } - else - { - // These patient-level tags are copied in the study level - assert(level == ResourceType_Study && - (tags[i] == DICOM_TAG_PATIENT_ID || - tags[i] == DICOM_TAG_PATIENT_NAME || - tags[i] == DICOM_TAG_PATIENT_BIRTH_DATE)); - } - } - } - - { - std::set<DicomTag> tags; - DicomMap::GetMainDicomTags(tags, level); - - for (std::set<DicomTag>::const_iterator - tag = tags.begin(); tag != tags.end(); ++tag) - { - if (registry_.find(*tag) == registry_.end()) - { - registry_[*tag] = TagInfo(level, DicomTagType_Main); - } - } - } - } - - public: - MainDicomTagsRegistry() - { - LoadTags(ResourceType_Patient); - LoadTags(ResourceType_Study); - LoadTags(ResourceType_Series); - LoadTags(ResourceType_Instance); - } - - void LookupTag(ResourceType& level, - DicomTagType& type, - const DicomTag& tag) const - { - Registry::const_iterator it = registry_.find(tag); - - if (it == registry_.end()) - { - // Default values - level = ResourceType_Instance; - type = DicomTagType_Generic; - } - else - { - level = it->second.GetLevel(); - type = it->second.GetType(); - } - } - }; - - void StatelessDatabaseOperations::ReadWriteTransaction::LogChange(int64_t internalId, ChangeType changeType, ResourceType resourceType, @@ -475,38 +368,6 @@ } - void StatelessDatabaseOperations::NormalizeLookup(std::vector<DatabaseConstraint>& target, - const DatabaseLookup& source, - ResourceType queryLevel) const - { - assert(mainDicomTagsRegistry_.get() != NULL); - - target.clear(); - target.reserve(source.GetConstraintsCount()); - - for (size_t i = 0; i < source.GetConstraintsCount(); i++) - { - ResourceType level; - DicomTagType type; - - mainDicomTagsRegistry_->LookupTag(level, type, source.GetConstraint(i).GetTag()); - - if (type == DicomTagType_Identifier || - type == DicomTagType_Main) - { - // Use the fact that patient-level tags are copied at the study level - if (level == ResourceType_Patient && - queryLevel != ResourceType_Patient) - { - level = ResourceType_Study; - } - - target.push_back(source.GetConstraint(i).ConvertToDatabaseConstraint(level, type)); - } - } - } - - class StatelessDatabaseOperations::Transaction : public boost::noncopyable { private: @@ -910,7 +771,7 @@ Toolbox::GetMissingsFromSet(target.missingRequestedTags_, requestedTags, savedMainDicomTags); while ((target.missingRequestedTags_.size() > 0) - && currentLevel != ResourceType_Patient) + && currentLevel != ResourceType_Patient) { currentLevel = GetParentResourceType(currentLevel); @@ -1727,20 +1588,21 @@ DicomTagConstraint c(tag, ConstraintType_Equal, value, true, true); - std::vector<DatabaseConstraint> query; - query.push_back(c.ConvertToDatabaseConstraint(level, DicomTagType_Identifier)); + DatabaseConstraints query; + bool isIdentical; // unused + query.AddConstraint(c.ConvertToDatabaseConstraint(isIdentical, level, DicomTagType_Identifier)); class Operations : public IReadOnlyOperations { private: - std::vector<std::string>& result_; - const std::vector<DatabaseConstraint>& query_; - ResourceType level_; + std::vector<std::string>& result_; + const DatabaseConstraints& query_; + ResourceType level_; public: Operations(std::vector<std::string>& result, - const std::vector<DatabaseConstraint>& query, + const DatabaseConstraints& query, ResourceType level) : result_(result), query_(query), @@ -2026,7 +1888,7 @@ LabelsConstraint labelsConstraint, uint32_t limit) { - class Operations : public ReadOnlyOperationsT6<bool, const std::vector<DatabaseConstraint>&, ResourceType, + class Operations : public ReadOnlyOperationsT6<bool, const DatabaseConstraints&, ResourceType, const std::set<std::string>&, LabelsConstraint, size_t> { private: @@ -2072,8 +1934,10 @@ ServerToolbox::CheckValidLabel(*it); } - std::vector<DatabaseConstraint> normalized; - NormalizeLookup(normalized, lookup, queryLevel); + DatabaseConstraints normalized; + + assert(mainDicomTagsRegistry_.get() != NULL); + mainDicomTagsRegistry_->NormalizeLookup(normalized, lookup, queryLevel); Operations operations; operations.Apply(*this, (instancesId != NULL), normalized, queryLevel, labels, labelsConstraint, limit); @@ -2554,7 +2418,7 @@ catch (boost::bad_lexical_cast&) { LOG(ERROR) << "Cannot read the global sequence " - << boost::lexical_cast<std::string>(sequence_) << ", resetting it"; + << boost::lexical_cast<std::string>(sequence_) << ", resetting it"; oldValue = 0; } @@ -2853,8 +2717,8 @@ public: explicit Operations(const ParsedDicomFile& dicom, bool limitToThisLevelDicomTags, ResourceType limitToLevel) - : limitToThisLevelDicomTags_(limitToThisLevelDicomTags), - limitToLevel_(limitToLevel) + : limitToThisLevelDicomTags_(limitToThisLevelDicomTags), + limitToLevel_(limitToLevel) { OrthancConfiguration::DefaultExtractDicomSummary(summary_, dicom); hasher_.reset(new DicomInstanceHasher(summary_)); @@ -2979,7 +2843,7 @@ } bool StatelessDatabaseOperations::ReadWriteTransaction::HasReachedMaxPatientCount(unsigned int maximumPatientCount, - const std::string& patientId) + const std::string& patientId) { if (maximumPatientCount != 0) { @@ -3081,7 +2945,7 @@ }; if (maximumStorageMode == MaxStorageMode_Recycle - && (maximumStorageSize != 0 || maximumPatientCount != 0)) + && (maximumStorageSize != 0 || maximumPatientCount != 0)) { Operations operations(maximumStorageSize, maximumPatientCount); Apply(operations); @@ -3145,9 +3009,9 @@ } static void SetMainDicomSequenceMetadata(ResourcesContent& content, - int64_t resource, - const DicomMap& dicomSummary, - ResourceType level) + int64_t resource, + const DicomMap& dicomSummary, + ResourceType level) { std::string serialized; GetMainDicomSequenceMetadataContent(serialized, dicomSummary, level); @@ -3340,7 +3204,7 @@ // Ensure there is enough room in the storage for the new instance uint64_t instanceSize = 0; for (Attachments::const_iterator it = attachments_.begin(); - it != attachments_.end(); ++it) + it != attachments_.end(); ++it) { instanceSize += it->GetCompressedSize(); } @@ -3369,7 +3233,7 @@ // Attach the files to the newly created instance for (Attachments::const_iterator it = attachments_.begin(); - it != attachments_.end(); ++it) + it != attachments_.end(); ++it) { if (isReconstruct_) { @@ -3851,4 +3715,75 @@ boost::shared_lock<boost::shared_mutex> lock(mutex_); return db_.GetDatabaseCapabilities().HasExtendedChanges(); } + + void StatelessDatabaseOperations::ExecuteFind(FindResponse& response, + const FindRequest& request) + { + class IntegratedFind : public ReadOnlyOperationsT3<FindResponse&, const FindRequest&, + const IDatabaseWrapper::Capabilities&> + { + public: + virtual void ApplyTuple(ReadOnlyTransaction& transaction, + const Tuple& tuple) ORTHANC_OVERRIDE + { + transaction.ExecuteFind(tuple.get<0>(), tuple.get<1>(), tuple.get<2>()); + } + }; + + class FindStage : public ReadOnlyOperationsT3<std::list<std::string>&, const IDatabaseWrapper::Capabilities&, const FindRequest& > + { + public: + virtual void ApplyTuple(ReadOnlyTransaction& transaction, + const Tuple& tuple) ORTHANC_OVERRIDE + { + transaction.ExecuteFind(tuple.get<0>(), tuple.get<1>(), tuple.get<2>()); + } + }; + + class ExpandStage : public ReadOnlyOperationsT4<FindResponse&, const IDatabaseWrapper::Capabilities&, const FindRequest&, const std::string&> + { + public: + virtual void ApplyTuple(ReadOnlyTransaction& transaction, + const Tuple& tuple) ORTHANC_OVERRIDE + { + transaction.ExecuteExpand(tuple.get<0>(), tuple.get<1>(), tuple.get<2>(), tuple.get<3>()); + } + }; + + IDatabaseWrapper::Capabilities capabilities = db_.GetDatabaseCapabilities(); + + if (db_.HasIntegratedFind()) + { + /** + * In this flavor, the "find" and the "expand" phases are + * executed in one single transaction. + **/ + IntegratedFind operations; + operations.Apply(*this, response, request, capabilities); + } + else + { + /** + * In this flavor, the "find" and the "expand" phases for each + * found resource are executed in distinct transactions. This is + * the compatibility mode equivalent to Orthanc <= 1.12.3. + **/ + std::list<std::string> identifiers; + + FindStage find; + find.Apply(*this, identifiers, capabilities, request); + + ExpandStage expand; + + for (std::list<std::string>::const_iterator it = identifiers.begin(); it != identifiers.end(); ++it) + { + /** + * Not that the resource might have been deleted (as we are in + * another transaction). The database engine must ignore such + * error cases. + **/ + expand.Apply(*this, response, capabilities, request, *it); + } + } + } }