comparison OrthancServer/Sources/ServerContext.cpp @ 5707:c8d21a09aae6 find-refactoring-clean

removed ServerContext::ILookupVisitor
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 12 Jul 2024 17:58:15 +0200
parents 708952bd869c
children 52771e1a8072
comparison
equal deleted inserted replaced
5706:1404a80dd461 5707:c8d21a09aae6
1529 return false; 1529 return false;
1530 #endif 1530 #endif
1531 } 1531 }
1532 1532
1533 1533
1534 void ServerContext::Apply(ILookupVisitor& visitor,
1535 const DatabaseLookup& lookup,
1536 ResourceType queryLevel,
1537 const std::set<std::string>& labels,
1538 LabelsConstraint labelsConstraint,
1539 size_t since,
1540 size_t limit)
1541 {
1542 const uint64_t databaseLimit = GetDatabaseLimits(queryLevel);
1543
1544 std::vector<std::string> resources, instances;
1545 const DicomTagConstraint* dicomModalitiesConstraint = NULL;
1546
1547 bool hasModalitiesInStudyLookup = (queryLevel == ResourceType_Study &&
1548 lookup.GetConstraint(dicomModalitiesConstraint, DICOM_TAG_MODALITIES_IN_STUDY) &&
1549 ((dicomModalitiesConstraint->GetConstraintType() == ConstraintType_Equal && !dicomModalitiesConstraint->GetValue().empty()) ||
1550 (dicomModalitiesConstraint->GetConstraintType() == ConstraintType_List && !dicomModalitiesConstraint->GetValues().empty())));
1551
1552 std::unique_ptr<DatabaseLookup> fastLookup(lookup.Clone());
1553
1554 if (hasModalitiesInStudyLookup)
1555 {
1556 fastLookup->RemoveConstraint(DICOM_TAG_MODALITIES_IN_STUDY);
1557 }
1558
1559 if (true)
1560 {
1561 /**
1562 * EXPERIMENTAL VERSION
1563 **/
1564
1565 ResourceFinder finder(queryLevel, false /* TODO-FIND: don't expand for now */);
1566 finder.SetDatabaseLimits(databaseLimit);
1567 finder.SetDatabaseLookup(lookup);
1568 finder.SetLabels(labels);
1569 finder.SetLabelsConstraint(labelsConstraint);
1570
1571 if (queryLevel != ResourceType_Instance)
1572 {
1573 finder.SetRetrieveOneInstanceIdentifier(true);
1574 }
1575
1576 FindResponse response;
1577 finder.Execute(response, GetIndex());
1578
1579 resources.resize(response.GetSize());
1580 instances.resize(response.GetSize());
1581
1582 for (size_t i = 0; i < response.GetSize(); i++)
1583 {
1584 const FindResponse::Resource& resource = response.GetResourceByIndex(i);
1585 resources[i] = resource.GetIdentifier();
1586
1587 if (queryLevel == ResourceType_Instance)
1588 {
1589 instances[i] = resource.GetIdentifier();
1590 }
1591 else
1592 {
1593 instances[i] = resource.GetOneInstanceIdentifier();
1594 }
1595 }
1596 }
1597 else
1598 {
1599 /**
1600 * VERSION IN ORTHANC <= 1.12.4
1601 **/
1602
1603 const size_t lookupLimit = (databaseLimit == 0 ? 0 : databaseLimit + 1);
1604 GetIndex().ApplyLookupResources(resources, &instances, *fastLookup, queryLevel, labels, labelsConstraint, lookupLimit);
1605 }
1606
1607 bool complete = (databaseLimit == 0 ||
1608 resources.size() <= databaseLimit);
1609
1610 LOG(INFO) << "Number of candidate resources after fast DB filtering on main DICOM tags: " << resources.size();
1611
1612 /**
1613 * "resources" contains the Orthanc ID of the resource at level
1614 * "queryLevel", "instances" contains one the Orthanc ID of one
1615 * sample instance from this resource.
1616 **/
1617 assert(resources.size() == instances.size());
1618
1619 size_t countResults = 0;
1620 size_t skipped = 0;
1621
1622 const bool isDicomAsJsonNeeded = visitor.IsDicomAsJsonNeeded();
1623
1624 for (size_t i = 0; i < instances.size(); i++)
1625 {
1626 // Optimization in Orthanc 1.5.1 - Don't read the full JSON from
1627 // the disk if only "main DICOM tags" are to be returned
1628
1629 boost::shared_ptr<Json::Value> dicomAsJson;
1630
1631 bool hasOnlyMainDicomTags;
1632 DicomMap dicom;
1633 DicomMap allMainDicomTagsFromDB;
1634
1635 if (!IsStorageAccessAllowedForAnswers(findStorageAccessMode_)
1636 || fastLookup->HasOnlyMainDicomTags())
1637 {
1638 // Case (1): The main DICOM tags, as stored in the database,
1639 // are sufficient to look for match
1640
1641 if (!GetIndex().GetAllMainDicomTags(allMainDicomTagsFromDB, instances[i]))
1642 {
1643 // The instance has been removed during the execution of the
1644 // lookup, ignore it
1645 continue;
1646 }
1647
1648 // New in Orthanc 1.6.0: Only keep the main DICOM tags at the
1649 // level of interest for the query
1650 switch (queryLevel)
1651 {
1652 // WARNING: Don't reorder cases below, and don't add "break"
1653 case ResourceType_Instance:
1654 dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Instance);
1655
1656 case ResourceType_Series:
1657 dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Series);
1658
1659 case ResourceType_Study:
1660 dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Study);
1661
1662 case ResourceType_Patient:
1663 dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Patient);
1664 break;
1665
1666 default:
1667 throw OrthancException(ErrorCode_InternalError);
1668 }
1669
1670 hasOnlyMainDicomTags = true;
1671 }
1672 else
1673 {
1674 // Case (2): Need to read the "DICOM-as-JSON" attachment from
1675 // the storage area
1676 dicomAsJson.reset(new Json::Value);
1677 ReadDicomAsJson(*dicomAsJson, instances[i]);
1678
1679 dicom.FromDicomAsJson(*dicomAsJson);
1680
1681 // This map contains the entire JSON, i.e. more than the main DICOM tags
1682 hasOnlyMainDicomTags = false;
1683 }
1684
1685 if (fastLookup->IsMatch(dicom))
1686 {
1687 bool isMatch = true;
1688
1689 if (hasModalitiesInStudyLookup)
1690 {
1691 std::set<DicomTag> requestedTags;
1692 requestedTags.insert(DICOM_TAG_MODALITIES_IN_STUDY);
1693 ExpandedResource resource;
1694 ComputeStudyTags(resource, *this, resources[i], requestedTags);
1695
1696 std::vector<std::string> modalities;
1697 Toolbox::TokenizeString(modalities, resource.GetMainDicomTags().GetValue(DICOM_TAG_MODALITIES_IN_STUDY).GetContent(), '\\');
1698 bool hasAtLeastOneModalityMatching = false;
1699 for (size_t m = 0; m < modalities.size(); m++)
1700 {
1701 hasAtLeastOneModalityMatching |= dicomModalitiesConstraint->IsMatch(modalities[m]);
1702 }
1703
1704 isMatch = isMatch && hasAtLeastOneModalityMatching;
1705 // copy the value of ModalitiesInStudy such that it can be reused to build the answer
1706 allMainDicomTagsFromDB.SetValue(DICOM_TAG_MODALITIES_IN_STUDY, resource.GetMainDicomTags().GetValue(DICOM_TAG_MODALITIES_IN_STUDY));
1707 }
1708
1709 if (isMatch)
1710 {
1711 if (skipped < since)
1712 {
1713 skipped++;
1714 }
1715 else if (limit != 0 &&
1716 countResults >= limit)
1717 {
1718 // Too many results, don't mark as complete
1719 complete = false;
1720 break;
1721 }
1722 else
1723 {
1724 if (IsStorageAccessAllowedForAnswers(findStorageAccessMode_) &&
1725 dicomAsJson.get() == NULL &&
1726 isDicomAsJsonNeeded)
1727 {
1728 dicomAsJson.reset(new Json::Value);
1729 ReadDicomAsJson(*dicomAsJson, instances[i]);
1730 }
1731
1732 if (hasOnlyMainDicomTags)
1733 {
1734 // This is Case (1): The variable "dicom" only contains the main DICOM tags
1735 visitor.Visit(resources[i], instances[i], allMainDicomTagsFromDB, dicomAsJson.get());
1736 }
1737 else
1738 {
1739 // Remove the non-main DICOM tags from "dicom" if Case (2)
1740 // was used, for consistency with Case (1)
1741
1742 DicomMap mainDicomTags;
1743 mainDicomTags.ExtractMainDicomTags(dicom);
1744 visitor.Visit(resources[i], instances[i], mainDicomTags, dicomAsJson.get());
1745 }
1746
1747 countResults ++;
1748 }
1749 }
1750 }
1751 }
1752
1753 if (complete)
1754 {
1755 visitor.MarkAsComplete();
1756 }
1757
1758 LOG(INFO) << "Number of matching resources: " << countResults;
1759 }
1760
1761 bool ServerContext::LookupOrReconstructMetadata(std::string& target, 1534 bool ServerContext::LookupOrReconstructMetadata(std::string& target,
1762 const std::string& publicId, 1535 const std::string& publicId,
1763 ResourceType level, 1536 ResourceType level,
1764 MetadataType metadata) 1537 MetadataType metadata)
1765 { 1538 {