comparison OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp @ 5828:7030fa489669 find-refactoring

tools/find: QueryMetadata
author Alain Mazy <am@orthanc.team>
date Mon, 07 Oct 2024 15:19:26 +0200
parents d73dfb4548c6
children 963945d780d6
comparison
equal deleted inserted replaced
5827:976872a99d39 5828:7030fa489669
39 #include "../../../OrthancFramework/Sources/MultiThreading/Semaphore.h" 39 #include "../../../OrthancFramework/Sources/MultiThreading/Semaphore.h"
40 #include "../../../OrthancFramework/Sources/SerializationToolbox.h" 40 #include "../../../OrthancFramework/Sources/SerializationToolbox.h"
41 41
42 #include "../OrthancConfiguration.h" 42 #include "../OrthancConfiguration.h"
43 #include "../Search/DatabaseLookup.h" 43 #include "../Search/DatabaseLookup.h"
44 #include "../Search/DatabaseMetadataConstraint.h"
44 #include "../ServerContext.h" 45 #include "../ServerContext.h"
45 #include "../ServerToolbox.h" 46 #include "../ServerToolbox.h"
46 #include "../SliceOrdering.h" 47 #include "../SliceOrdering.h"
47 48
48 // This "include" is mandatory for Release builds using Linux Standard Base 49 // This "include" is mandatory for Release builds using Linux Standard Base
3253 static const char* const KEY_ORDER_BY_TYPE = "Type"; // New in Orthanc 1.12.5 3254 static const char* const KEY_ORDER_BY_TYPE = "Type"; // New in Orthanc 1.12.5
3254 static const char* const KEY_ORDER_BY_DIRECTION = "Direction"; // New in Orthanc 1.12.5 3255 static const char* const KEY_ORDER_BY_DIRECTION = "Direction"; // New in Orthanc 1.12.5
3255 static const char* const KEY_PARENT_PATIENT = "ParentPatient"; // New in Orthanc 1.12.5 3256 static const char* const KEY_PARENT_PATIENT = "ParentPatient"; // New in Orthanc 1.12.5
3256 static const char* const KEY_PARENT_STUDY = "ParentStudy"; // New in Orthanc 1.12.5 3257 static const char* const KEY_PARENT_STUDY = "ParentStudy"; // New in Orthanc 1.12.5
3257 static const char* const KEY_PARENT_SERIES = "ParentSeries"; // New in Orthanc 1.12.5 3258 static const char* const KEY_PARENT_SERIES = "ParentSeries"; // New in Orthanc 1.12.5
3259 static const char* const KEY_QUERY_METADATA = "QueryMetadata"; // New in Orthanc 1.12.5
3258 3260
3259 if (call.IsDocumentation()) 3261 if (call.IsDocumentation())
3260 { 3262 {
3261 OrthancRestApi::DocumentDicomFormat(call, DicomToJsonFormat_Human); 3263 OrthancRestApi::DocumentDicomFormat(call, DicomToJsonFormat_Human);
3262 3264
3294 "Limit the reported resources to descendants of this patient (new in Orthanc 1.12.5)", true) 3296 "Limit the reported resources to descendants of this patient (new in Orthanc 1.12.5)", true)
3295 .SetRequestField(KEY_PARENT_STUDY, RestApiCallDocumentation::Type_String, 3297 .SetRequestField(KEY_PARENT_STUDY, RestApiCallDocumentation::Type_String,
3296 "Limit the reported resources to descendants of this study (new in Orthanc 1.12.5)", true) 3298 "Limit the reported resources to descendants of this study (new in Orthanc 1.12.5)", true)
3297 .SetRequestField(KEY_PARENT_SERIES, RestApiCallDocumentation::Type_String, 3299 .SetRequestField(KEY_PARENT_SERIES, RestApiCallDocumentation::Type_String,
3298 "Limit the reported resources to descendants of this series (new in Orthanc 1.12.5)", true) 3300 "Limit the reported resources to descendants of this series (new in Orthanc 1.12.5)", true)
3301 .SetRequestField(KEY_QUERY_METADATA, RestApiCallDocumentation::Type_JsonObject,
3302 "Associative array containing the filter on the values of the metadata (new in Orthanc 1.12.5)", true)
3299 .AddAnswerType(MimeType_Json, "JSON array containing either the Orthanc identifiers, or detailed information " 3303 .AddAnswerType(MimeType_Json, "JSON array containing either the Orthanc identifiers, or detailed information "
3300 "about the reported resources (if `Expand` argument is `true`)"); 3304 "about the reported resources (if `Expand` argument is `true`)");
3301 return; 3305 return;
3302 } 3306 }
3303 3307
3362 request[KEY_ORDER_BY].type() != Json::arrayValue) 3366 request[KEY_ORDER_BY].type() != Json::arrayValue)
3363 { 3367 {
3364 throw OrthancException(ErrorCode_BadRequest, 3368 throw OrthancException(ErrorCode_BadRequest,
3365 "Field \"" + std::string(KEY_ORDER_BY) + "\" must be an array"); 3369 "Field \"" + std::string(KEY_ORDER_BY) + "\" must be an array");
3366 } 3370 }
3371 else if (request.isMember(KEY_QUERY_METADATA) &&
3372 request[KEY_QUERY_METADATA].type() != Json::objectValue)
3373 {
3374 throw OrthancException(ErrorCode_BadRequest,
3375 "Field \"" + std::string(KEY_QUERY_METADATA) + "\" must be an JSON object");
3376 }
3367 else if (request.isMember(KEY_PARENT_PATIENT) && 3377 else if (request.isMember(KEY_PARENT_PATIENT) &&
3368 request[KEY_PARENT_PATIENT].type() != Json::stringValue) 3378 request[KEY_PARENT_PATIENT].type() != Json::stringValue)
3369 { 3379 {
3370 throw OrthancException(ErrorCode_BadRequest, 3380 throw OrthancException(ErrorCode_BadRequest,
3371 "Field \"" + std::string(KEY_PARENT_PATIENT) + "\" must be a string"); 3381 "Field \"" + std::string(KEY_PARENT_PATIENT) + "\" must be a string");
3434 if (request.isMember(KEY_CASE_SENSITIVE)) 3444 if (request.isMember(KEY_CASE_SENSITIVE))
3435 { 3445 {
3436 caseSensitive = request[KEY_CASE_SENSITIVE].asBool(); 3446 caseSensitive = request[KEY_CASE_SENSITIVE].asBool();
3437 } 3447 }
3438 3448
3439 DatabaseLookup query; 3449 { // DICOM Tag query
3440 3450 DatabaseLookup dicomTagLookup;
3441 Json::Value::Members members = request[KEY_QUERY].getMemberNames(); 3451
3442 for (size_t i = 0; i < members.size(); i++) 3452 Json::Value::Members members = request[KEY_QUERY].getMemberNames();
3443 { 3453 for (size_t i = 0; i < members.size(); i++)
3444 if (request[KEY_QUERY][members[i]].type() != Json::stringValue)
3445 { 3454 {
3446 throw OrthancException(ErrorCode_BadRequest, 3455 if (request[KEY_QUERY][members[i]].type() != Json::stringValue)
3447 "Tag \"" + members[i] + "\" must be associated with a string"); 3456 {
3457 throw OrthancException(ErrorCode_BadRequest,
3458 "Tag \"" + members[i] + "\" must be associated with a string");
3459 }
3460
3461 const std::string value = request[KEY_QUERY][members[i]].asString();
3462
3463 if (!value.empty())
3464 {
3465 // An empty string corresponds to an universal constraint,
3466 // so we ignore it. This mimics the behavior of class
3467 // "OrthancFindRequestHandler"
3468 dicomTagLookup.AddRestConstraint(FromDcmtkBridge::ParseTag(members[i]),
3469 value, caseSensitive, true);
3470 }
3448 } 3471 }
3449 3472
3450 const std::string value = request[KEY_QUERY][members[i]].asString(); 3473 finder.SetDatabaseLookup(dicomTagLookup);
3451 3474 }
3452 if (!value.empty()) 3475
3476 { // Metadata query
3477 Json::Value::Members members = request[KEY_QUERY_METADATA].getMemberNames();
3478 for (size_t i = 0; i < members.size(); i++)
3453 { 3479 {
3454 // An empty string corresponds to an universal constraint, 3480 if (request[KEY_QUERY_METADATA][members[i]].type() != Json::stringValue)
3455 // so we ignore it. This mimics the behavior of class 3481 {
3456 // "OrthancFindRequestHandler" 3482 throw OrthancException(ErrorCode_BadRequest,
3457 query.AddRestConstraint(FromDcmtkBridge::ParseTag(members[i]), 3483 "Tag \"" + members[i] + "\" must be associated with a string");
3458 value, caseSensitive, true); 3484 }
3485 MetadataType metadata = StringToMetadata(members[i]);
3486
3487 const std::string value = request[KEY_QUERY_METADATA][members[i]].asString();
3488
3489 if (!value.empty())
3490 {
3491 if (value.find('\\') != std::string::npos)
3492 {
3493 std::vector<std::string> items;
3494 Toolbox::TokenizeString(items, value, '\\');
3495
3496 finder.AddMetadataConstraint(new DatabaseMetadataConstraint(metadata, ConstraintType_List, items, caseSensitive));
3497 }
3498 else if (value.find('*') != std::string::npos || value.find('?') != std::string::npos)
3499 {
3500 finder.AddMetadataConstraint(new DatabaseMetadataConstraint(metadata, ConstraintType_Wildcard, value, caseSensitive));
3501 }
3502 else
3503 {
3504 finder.AddMetadataConstraint(new DatabaseMetadataConstraint(metadata, ConstraintType_Equal, value, caseSensitive));
3505 }
3506 }
3459 } 3507 }
3460 } 3508 }
3461
3462 finder.SetDatabaseLookup(query);
3463 } 3509 }
3464 3510
3465 if (request.isMember(KEY_REQUESTED_TAGS)) 3511 if (request.isMember(KEY_REQUESTED_TAGS))
3466 { 3512 {
3467 std::set<DicomTag> requestedTags; 3513 std::set<DicomTag> requestedTags;