# HG changeset patch # User Sebastien Jodogne # Date 1680898717 -7200 # Node ID a7d95f951f8a8d4128dfc03ba2bf217a2d849270 # Parent eb2684260c1915c504c3692ffb6eacaafffbfa09 replaced "WithLabels" and "WithoutLabels", by "Labels" and "LabelsConstraint" diff -r eb2684260c19 -r a7d95f951f8a NEWS --- a/NEWS Fri Apr 07 15:44:12 2023 +0200 +++ b/NEWS Fri Apr 07 22:18:37 2023 +0200 @@ -11,7 +11,7 @@ * API version upgraded to 20 * New URIs "/.../{id}/labels/{label}" to test/set/remove labels -* "/tools/find" accepts the "WithLabels" and "WithoutLabels" arguments +* "/tools/find" accepts the "Labels" and "LabelsConstraint" arguments * "/patients/{id}", "/studies/{id}", "/series/{id}" and "/instances/{id}" contain the "Labels" field * "/system": added "UserMetadata" and "HasLabels" diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/OrthancExplorer/explorer.js --- a/OrthancServer/OrthancExplorer/explorer.js Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/OrthancExplorer/explorer.js Fri Apr 07 22:18:37 2023 +0200 @@ -566,7 +566,7 @@ } else if (input.id == 'lookup-study-labels') { // New in Orthanc 1.12.0 - lookup['WithLabels'] = input.value.split(' '); + lookup['Labels'] = input.value.split(' '); } else { console.error('Unknown lookup field: ' + input.id); diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp --- a/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp Fri Apr 07 22:18:37 2023 +0200 @@ -565,12 +565,11 @@ std::list* instancesId, const std::vector& lookup, ResourceType queryLevel, - const std::set& withLabels, - const std::set& withoutLabels, + const std::set& labels, + LabelsConstraint labelsConstraint, uint32_t limit) ORTHANC_OVERRIDE { - if (!withLabels.empty() || - !withoutLabels.empty()) + if (!labels.empty()) { throw OrthancException(ErrorCode_InternalError); // "HasLabelsSupport()" has returned "false" } diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp --- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp Fri Apr 07 22:18:37 2023 +0200 @@ -800,12 +800,11 @@ std::list* instancesId, // Can be NULL if not needed const std::vector& lookup, ResourceType queryLevel, - const std::set& withLabels, - const std::set& withoutLabels, + const std::set& labels, + LabelsConstraint labelsConstraint, uint32_t limit) ORTHANC_OVERRIDE { - if (!withLabels.empty() || - !withoutLabels.empty()) + if (!labels.empty()) { throw OrthancException(ErrorCode_InternalError); // "HasLabelsSupport()" has returned "false" } diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp --- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp Fri Apr 07 22:18:37 2023 +0200 @@ -915,13 +915,12 @@ std::list* instancesId, // Can be NULL if not needed const std::vector& lookup, ResourceType queryLevel, - const std::set& withLabels, - const std::set& withoutLabels, + const std::set& labels, + LabelsConstraint labelsConstraint, uint32_t limit) ORTHANC_OVERRIDE { if (!database_.HasLabelsSupport() && - (!withLabels.empty() || - !withoutLabels.empty())) + !labels.empty()) { throw OrthancException(ErrorCode_InternalError); } @@ -976,14 +975,27 @@ } } - for (std::set::const_iterator it = withLabels.begin(); it != withLabels.end(); ++it) + for (std::set::const_iterator it = labels.begin(); it != labels.end(); ++it) { - request.mutable_lookup_resources()->add_with_labels(*it); + request.mutable_lookup_resources()->add_labels(*it); } - - for (std::set::const_iterator it = withoutLabels.begin(); it != withoutLabels.end(); ++it) + + switch (labelsConstraint) { - request.mutable_lookup_resources()->add_without_labels(*it); + case LabelsConstraint_All: + request.mutable_lookup_resources()->set_labels_constraint(DatabasePluginMessages::LABELS_CONSTRAINT_ALL); + break; + + case LabelsConstraint_Any: + request.mutable_lookup_resources()->set_labels_constraint(DatabasePluginMessages::LABELS_CONSTRAINT_ANY); + break; + + case LabelsConstraint_None: + request.mutable_lookup_resources()->set_labels_constraint(DatabasePluginMessages::LABELS_CONSTRAINT_NONE); + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); } DatabasePluginMessages::TransactionResponse response; diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto --- a/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto Fri Apr 07 22:18:37 2023 +0200 @@ -71,6 +71,12 @@ CONSTRAINT_LIST = 4; } +enum LabelsConstraintType { + LABELS_CONSTRAINT_ALL = 0; + LABELS_CONSTRAINT_ANY = 1; + LABELS_CONSTRAINT_NONE = 2; +} + message ServerIndexChange { int64 seq = 1; int32 change_type = 2; // opaque "ChangeType" in Orthanc @@ -658,8 +664,8 @@ ResourceType query_level = 2; uint32 limit = 3; bool retrieve_instances_ids = 4; - repeated string with_labels = 5; // New in Orthanc 1.12.0 - repeated string without_labels = 6; // New in Orthanc 1.12.0 + repeated string labels = 5; // New in Orthanc 1.12.0 + LabelsConstraintType labels_constraint = 6; // New in Orthanc 1.12.0 } message Response { repeated string resources_ids = 1; diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/Sources/Database/IDatabaseWrapper.h --- a/OrthancServer/Sources/Database/IDatabaseWrapper.h Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/Sources/Database/IDatabaseWrapper.h Fri Apr 07 22:18:37 2023 +0200 @@ -26,6 +26,7 @@ #include "../../../OrthancFramework/Sources/FileStorage/FileInfo.h" #include "../../../OrthancFramework/Sources/FileStorage/IStorageArea.h" #include "../ExportedResource.h" +#include "../Search/ISqlLookupFormatter.h" #include "../ServerIndexChange.h" #include "IDatabaseListener.h" @@ -200,8 +201,8 @@ std::list* instancesId, // Can be NULL if not needed const std::vector& lookup, ResourceType queryLevel, - const std::set& withLabels, - const std::set& withoutLabels, + const std::set& labels, + LabelsConstraint labelsConstraint, uint32_t limit) = 0; // Returns "true" iff. the instance is new and has been inserted diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp --- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp Fri Apr 07 22:18:37 2023 +0200 @@ -341,14 +341,14 @@ std::list* instancesId, const std::vector& lookup, ResourceType queryLevel, - const std::set& withLabels, - const std::set& withoutLabels, + const std::set& labels, + LabelsConstraint labelsConstraint, uint32_t limit) ORTHANC_OVERRIDE { LookupFormatter formatter; std::string sql; - LookupFormatter::Apply(sql, formatter, lookup, queryLevel, withLabels, withoutLabels, limit); + LookupFormatter::Apply(sql, formatter, lookup, queryLevel, labels, labelsConstraint, limit); sql = "CREATE TEMPORARY TABLE Lookup AS " + sql; diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Fri Apr 07 22:18:37 2023 +0200 @@ -1651,9 +1651,8 @@ { // TODO - CANDIDATE FOR "TransactionType_Implicit" std::list tmp; - std::set withLabels; - std::set withoutLabels; - transaction.ApplyLookupResources(tmp, NULL, query_, level_, withLabels, withoutLabels, 0); + std::set labels; + transaction.ApplyLookupResources(tmp, NULL, query_, level_, labels, LabelsConstraint_Any, 0); CopyListToVector(result_, tmp); } }; @@ -1918,25 +1917,16 @@ } - static void CheckValidLabels(const std::set& labels) - { - for (std::set::const_iterator it = labels.begin(); it != labels.end(); ++it) - { - ServerToolbox::CheckValidLabel(*it); - } - } - - void StatelessDatabaseOperations::ApplyLookupResources(std::vector& resourcesId, std::vector* instancesId, const DatabaseLookup& lookup, ResourceType queryLevel, - const std::set& withLabels, - const std::set& withoutLabels, + const std::set& labels, + LabelsConstraint labelsConstraint, uint32_t limit) { class Operations : public ReadOnlyOperationsT6&, ResourceType, - const std::set&, const std::set&, size_t> + const std::set&, LabelsConstraint, size_t> { private: std::list resourcesList_; @@ -1970,20 +1960,22 @@ } }; - if ((!withLabels.empty() || !withoutLabels.empty()) && + if (!labels.empty() && !db_.HasLabelsSupport()) { throw OrthancException(ErrorCode_NotImplemented, "The database backend doesn't support labels"); } - CheckValidLabels(withLabels); - CheckValidLabels(withoutLabels); + for (std::set::const_iterator it = labels.begin(); it != labels.end(); ++it) + { + ServerToolbox::CheckValidLabel(*it); + } std::vector normalized; NormalizeLookup(normalized, lookup, queryLevel); Operations operations; - operations.Apply(*this, (instancesId != NULL), normalized, queryLevel, withLabels, withoutLabels, limit); + operations.Apply(*this, (instancesId != NULL), normalized, queryLevel, labels, labelsConstraint, limit); CopyListToVector(resourcesId, operations.GetResourcesList()); diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/Sources/Database/StatelessDatabaseOperations.h --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Fri Apr 07 22:18:37 2023 +0200 @@ -207,11 +207,12 @@ std::list* instancesId, // Can be NULL if not needed const std::vector& lookup, ResourceType queryLevel, - const std::set& withLabels, - const std::set& withoutLabels, + const std::set& labels, // New in Orthanc 1.12.0 + LabelsConstraint labelsConstraint, // New in Orthanc 1.12.0 uint32_t limit) { - return transaction_.ApplyLookupResources(resourcesId, instancesId, lookup, queryLevel, withLabels, withoutLabels, limit); + return transaction_.ApplyLookupResources(resourcesId, instancesId, lookup, queryLevel, + labels, labelsConstraint, limit); } void GetAllMetadata(std::map& target, @@ -676,8 +677,8 @@ std::vector* instancesId, // Can be NULL if not needed const DatabaseLookup& lookup, ResourceType queryLevel, - const std::set& withLabels, - const std::set& withoutLabels, + const std::set& labels, + LabelsConstraint labelsConstraint, uint32_t limit); bool DeleteResource(Json::Value& remainingAncestor /* out */, diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Fri Apr 07 22:18:37 2023 +0200 @@ -3072,8 +3072,8 @@ static const char* const KEY_QUERY = "Query"; static const char* const KEY_REQUESTED_TAGS = "RequestedTags"; static const char* const KEY_SINCE = "Since"; - static const char* const KEY_WITH_LABELS = "WithLabels"; // New in Orthanc 1.12.0 - static const char* const KEY_WITHOUT_LABELS = "WithoutLabels"; // New in Orthanc 1.12.0 + static const char* const KEY_LABELS = "Labels"; // New in Orthanc 1.12.0 + static const char* const KEY_LABELS_CONSTRAINT = "LabelsConstraint"; // New in Orthanc 1.12.0 if (call.IsDocumentation()) { @@ -3103,10 +3103,10 @@ "all Main Dicom Tags to keep backward compatibility with Orthanc prior to 1.11.0.", false) .SetRequestField(KEY_QUERY, RestApiCallDocumentation::Type_JsonObject, "Associative array containing the filter on the values of the DICOM tags", true) - .SetRequestField(KEY_WITH_LABELS, RestApiCallDocumentation::Type_JsonListOfStrings, - "List of strings specifying which labels must be present in the resources (new in Orthanc 1.12.0)", true) - .SetRequestField(KEY_WITHOUT_LABELS, RestApiCallDocumentation::Type_JsonListOfStrings, - "List of strings specifying which labels must not be present in the resources (new in Orthanc 1.12.0)", true) + .SetRequestField(KEY_LABELS, RestApiCallDocumentation::Type_JsonListOfStrings, + "List of strings specifying which labels to look for in the resources (new in Orthanc 1.12.0)", true) + .SetRequestField(KEY_LABELS_CONSTRAINT, RestApiCallDocumentation::Type_String, + "Constraint on the labels, can be `All`, `Any`, or `None` (defaults to `All`, new in Orthanc 1.12.0)", true) .AddAnswerType(MimeType_Json, "JSON array containing either the Orthanc identifiers, or detailed information " "about the reported resources (if `Expand` argument is `true`)"); return; @@ -3157,17 +3157,17 @@ throw OrthancException(ErrorCode_BadRequest, "Field \"" + std::string(KEY_REQUESTED_TAGS) + "\" must be an array"); } - else if (request.isMember(KEY_WITH_LABELS) && - request[KEY_WITH_LABELS].type() != Json::arrayValue) + else if (request.isMember(KEY_LABELS) && + request[KEY_LABELS].type() != Json::arrayValue) { throw OrthancException(ErrorCode_BadRequest, - "Field \"" + std::string(KEY_WITH_LABELS) + "\" must be an array of strings"); + "Field \"" + std::string(KEY_LABELS) + "\" must be an array of strings"); } - else if (request.isMember(KEY_WITHOUT_LABELS) && - request[KEY_WITHOUT_LABELS].type() != Json::arrayValue) + else if (request.isMember(KEY_LABELS_CONSTRAINT) && + request[KEY_LABELS_CONSTRAINT].type() != Json::stringValue) { throw OrthancException(ErrorCode_BadRequest, - "Field \"" + std::string(KEY_WITHOUT_LABELS) + "\" must be an array of strings"); + "Field \"" + std::string(KEY_LABELS_CONSTRAINT) + "\" must be an array of strings"); } else { @@ -3241,33 +3241,41 @@ } } - if (request.isMember(KEY_WITH_LABELS)) // New in Orthanc 1.12.0 + if (request.isMember(KEY_LABELS)) // New in Orthanc 1.12.0 { - for (Json::Value::ArrayIndex i = 0; i < request[KEY_WITH_LABELS].size(); i++) + for (Json::Value::ArrayIndex i = 0; i < request[KEY_LABELS].size(); i++) { - if (request[KEY_WITH_LABELS][i].type() != Json::stringValue) + if (request[KEY_LABELS][i].type() != Json::stringValue) { - throw OrthancException(ErrorCode_BadRequest, "Field \""+ std::string(KEY_WITH_LABELS) + "\" must contain strings"); + throw OrthancException(ErrorCode_BadRequest, "Field \"" + std::string(KEY_LABELS) + "\" must contain strings"); } else { - query.AddWithLabel(request[KEY_WITH_LABELS][i].asString()); + query.AddLabel(request[KEY_LABELS][i].asString()); } } } + + query.SetLabelsConstraint(LabelsConstraint_All); - if (request.isMember(KEY_WITHOUT_LABELS)) // New in Orthanc 1.12.0 + if (request.isMember(KEY_LABELS_CONSTRAINT)) { - for (Json::Value::ArrayIndex i = 0; i < request[KEY_WITHOUT_LABELS].size(); i++) + const std::string& s = request[KEY_LABELS_CONSTRAINT].asString(); + if (s == "All") { - if (request[KEY_WITHOUT_LABELS][i].type() != Json::stringValue) - { - throw OrthancException(ErrorCode_BadRequest, "Field \""+ std::string(KEY_WITHOUT_LABELS) + "\" must contain strings"); - } - else - { - query.AddWithoutLabel(request[KEY_WITHOUT_LABELS][i].asString()); - } + query.SetLabelsConstraint(LabelsConstraint_All); + } + else if (s == "Any") + { + query.SetLabelsConstraint(LabelsConstraint_Any); + } + else if (s == "None") + { + query.SetLabelsConstraint(LabelsConstraint_None); + } + else + { + throw OrthancException(ErrorCode_BadRequest, "Field \"" + std::string(KEY_LABELS_CONSTRAINT) + "\" must be \"All\", \"Any\", or \"None\""); } } diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/Sources/Search/DatabaseLookup.cpp --- a/OrthancServer/Sources/Search/DatabaseLookup.cpp Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/Sources/Search/DatabaseLookup.cpp Fri Apr 07 22:18:37 2023 +0200 @@ -370,22 +370,12 @@ } - void DatabaseLookup::AddWithLabel(const std::string& label) + void DatabaseLookup::AddLabel(const std::string& label) { if (!label.empty()) { ServerToolbox::CheckValidLabel(label); - withLabels_.insert(label); - } - } - - - void DatabaseLookup::AddWithoutLabel(const std::string& label) - { - if (!label.empty()) - { - ServerToolbox::CheckValidLabel(label); - withoutLabels_.insert(label); + labels_.insert(label); } } } diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/Sources/Search/DatabaseLookup.h --- a/OrthancServer/Sources/Search/DatabaseLookup.h Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/Sources/Search/DatabaseLookup.h Fri Apr 07 22:18:37 2023 +0200 @@ -22,6 +22,7 @@ #pragma once +#include "../Search/ISqlLookupFormatter.h" #include "DicomTagConstraint.h" class DcmItem; @@ -32,8 +33,8 @@ { private: std::vector constraints_; - std::set withLabels_; - std::set withoutLabels_; + std::set labels_; + LabelsConstraint labelsConstraint_; void AddDicomConstraintInternal(const DicomTag& tag, ValueRepresentation vr, @@ -44,7 +45,8 @@ void AddConstraintInternal(DicomTagConstraint* constraint); // Takes ownership public: - DatabaseLookup() + DatabaseLookup() : + labelsConstraint_(LabelsConstraint_All) { } @@ -95,18 +97,21 @@ void RemoveConstraint(const DicomTag& tag); - void AddWithLabel(const std::string& label); - - void AddWithoutLabel(const std::string& label); + void AddLabel(const std::string& label); - const std::set& GetWithLabels() const + void SetLabelsConstraint(LabelsConstraint constraint) { - return withLabels_; + labelsConstraint_ = constraint; } - const std::set& GetWithoutLabels() const + const std::set& GetLabels() const { - return withoutLabels_; + return labels_; + } + + LabelsConstraint GetLabelsConstraint() const + { + return labelsConstraint_; } }; } diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/Sources/Search/ISqlLookupFormatter.cpp --- a/OrthancServer/Sources/Search/ISqlLookupFormatter.cpp Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/Sources/Search/ISqlLookupFormatter.cpp Fri Apr 07 22:18:37 2023 +0200 @@ -307,8 +307,8 @@ ISqlLookupFormatter& formatter, const std::vector& lookup, ResourceType queryLevel, - const std::set& withLabels, - const std::set& withoutLabels, + const std::set& labels, + LabelsConstraint labelsConstraint, size_t limit) { assert(ResourceType_Patient < ResourceType_Study && @@ -384,20 +384,7 @@ std::list where; - if (!withLabels.empty()) - { - std::list labels; - for (std::set::const_iterator it = withLabels.begin(); it != withLabels.end(); ++it) - { - labels.push_back(formatter.GenerateParameter(*it)); - } - - where.push_back(boost::lexical_cast(withLabels.size()) + - " = (SELECT COUNT(1) FROM Labels WHERE internalId = " + FormatLevel(queryLevel) + - ".internalId AND label IN (" + Join(labels, "", ", ") + "))"); - } - - if (!withoutLabels.empty()) + if (!labels.empty()) { /** * "In SQL Server, NOT EXISTS and NOT IN predicates are the best @@ -405,14 +392,34 @@ * question are NOT NULL." * https://explainextended.com/2009/09/15/not-in-vs-not-exists-vs-left-join-is-null-sql-server/ **/ - std::list labels; - for (std::set::const_iterator it = withoutLabels.begin(); it != withoutLabels.end(); ++it) + + std::list formattedLabels; + for (std::set::const_iterator it = labels.begin(); it != labels.end(); ++it) { - labels.push_back(formatter.GenerateParameter(*it)); + formattedLabels.push_back(formatter.GenerateParameter(*it)); } - where.push_back("NOT EXISTS (SELECT 1 FROM Labels WHERE internalId = " + FormatLevel(queryLevel) + - ".internalId AND label IN (" + Join(labels, "", ", ") + "))"); + std::string condition; + switch (labelsConstraint) + { + case LabelsConstraint_Any: + condition = "> 0"; + break; + + case LabelsConstraint_All: + condition = "= " + boost::lexical_cast(labels.size()); + break; + + case LabelsConstraint_None: + condition = "= 0"; + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + where.push_back("(SELECT COUNT(1) FROM Labels WHERE internalId = " + FormatLevel(queryLevel) + + ".internalId AND label IN (" + Join(formattedLabels, "", ", ") + ")) " + condition); } where.push_back(FormatLevel(queryLevel) + ".resourceType = " + diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/Sources/Search/ISqlLookupFormatter.h --- a/OrthancServer/Sources/Search/ISqlLookupFormatter.h Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/Sources/Search/ISqlLookupFormatter.h Fri Apr 07 22:18:37 2023 +0200 @@ -35,6 +35,13 @@ { class DatabaseConstraint; + enum LabelsConstraint + { + LabelsConstraint_All, + LabelsConstraint_Any, + LabelsConstraint_None + }; + // This class is also used by the "orthanc-databases" project class ISqlLookupFormatter : public boost::noncopyable { @@ -60,8 +67,8 @@ ISqlLookupFormatter& formatter, const std::vector& lookup, ResourceType queryLevel, - const std::set& withLabels, // New in Orthanc 1.12.0 - const std::set& withoutLabels, // New in Orthanc 1.12.0 + const std::set& labels, // New in Orthanc 1.12.0 + LabelsConstraint labelsConstraint, // New in Orthanc 1.12.0 size_t limit); }; } diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/Sources/ServerContext.cpp --- a/OrthancServer/Sources/ServerContext.cpp Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/Sources/ServerContext.cpp Fri Apr 07 22:18:37 2023 +0200 @@ -1450,7 +1450,7 @@ { const size_t lookupLimit = (databaseLimit == 0 ? 0 : databaseLimit + 1); GetIndex().ApplyLookupResources(resources, &instances, *fastLookup, queryLevel, - lookup.GetWithLabels(), lookup.GetWithoutLabels(), lookupLimit); + lookup.GetLabels(), lookup.GetLabelsConstraint(), lookupLimit); } bool complete = (databaseLimit == 0 || diff -r eb2684260c19 -r a7d95f951f8a OrthancServer/UnitTestsSources/ServerIndexTests.cpp --- a/OrthancServer/UnitTestsSources/ServerIndexTests.cpp Fri Apr 07 15:44:12 2023 +0200 +++ b/OrthancServer/UnitTestsSources/ServerIndexTests.cpp Fri Apr 07 22:18:37 2023 +0200 @@ -169,7 +169,7 @@ lookup.push_back(c.ConvertToDatabaseConstraint(level, DicomTagType_Identifier)); std::set noLabel; - transaction_->ApplyLookupResources(result, NULL, lookup, level, noLabel, noLabel, 0 /* no limit */); + transaction_->ApplyLookupResources(result, NULL, lookup, level, noLabel, LabelsConstraint_All, 0 /* no limit */); } void DoLookupIdentifier2(std::list& result, @@ -190,7 +190,7 @@ lookup.push_back(c2.ConvertToDatabaseConstraint(level, DicomTagType_Identifier)); std::set noLabel; - transaction_->ApplyLookupResources(result, NULL, lookup, level, noLabel, noLabel, 0 /* no limit */); + transaction_->ApplyLookupResources(result, NULL, lookup, level, noLabel, LabelsConstraint_All, 0 /* no limit */); } }; }