Skip to content

Commit

Permalink
ENH: Improve terminology selector speed
Browse files Browse the repository at this point in the history
When the type selector was populated, all the type IDs were retrieved first, and then the type objects were looked up based on these IDs.
This lookup is very slow if there are tens of thousands of types in the terminology.

Improved performance by returning not just all the type IDs but also all the corresponding type objects, therefore avoiding the costly lookup for each type.
  • Loading branch information
lassoan committed Apr 26, 2024
1 parent 29f6b91 commit 2bd24cd
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1541,7 +1541,8 @@ bool vtkSlicerTerminologiesModuleLogic::GetTypesInTerminologyCategory(std::strin

//---------------------------------------------------------------------------
bool vtkSlicerTerminologiesModuleLogic::FindTypesInTerminologyCategory(
std::string terminologyName, CodeIdentifier categoryId, std::vector<CodeIdentifier>& types, std::string search)
std::string terminologyName, CodeIdentifier categoryId, std::vector<CodeIdentifier>& types, std::string search,
std::vector<vtkSmartPointer<vtkSlicerTerminologyType>> *typeObjects/*=nullptr*/)
{
types.clear();

Expand Down Expand Up @@ -1576,6 +1577,12 @@ bool vtkSlicerTerminologiesModuleLogic::FindTypesInTerminologyCategory(
{
CodeIdentifier typeId(typeCodingSchemeDesignator.GetString(), typeCodeValue.GetString(), typeNameStr);
types.push_back(typeId);
if (typeObjects)
{
vtkSmartPointer<vtkSlicerTerminologyType> typeObject = vtkSmartPointer<vtkSlicerTerminologyType>::New();
this->Internal->PopulateTerminologyTypeFromJson(type, typeObject);
typeObjects->push_back(typeObject);
}
}
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,18 @@ class VTK_SLICER_TERMINOLOGIES_LOGIC_EXPORT vtkSlicerTerminologiesModuleLogic :
bool GetNthCategoryInTerminology(std::string terminologyName, int categoryIndex, vtkSlicerTerminologyCategory* category);

/// Get terminology types from a terminology category as collection of \sa vtkSlicerTerminologyType container objects
/// \param typeCollection Output argument containing all the \sa vtkSlicerTerminologyType objects created
/// \param types Output argument containing all the \sa vtkSlicerTerminologyType objects created
/// from the types found in the given terminology category
/// \return Success flag
bool GetTypesInTerminologyCategory(std::string terminologyName, CodeIdentifier categoryId, std::vector<CodeIdentifier>& types);
/// Get all type names (codeMeaning) in a terminology category
/// \param typeCollection Output argument containing all the \sa vtkSlicerTerminologyType objects created
/// Get terminology types from a terminology category as collection of \sa vtkSlicerTerminologyType container objects
/// \param types Output argument containing all the \sa type IDs in the category.
/// from the types found in the given terminology category
/// \param typeObjects Output argument containing all the \sa type objects in the category.. This is useful if type objects
/// need to be retrieved for a large number of types, because it avoids the need to do a costly search in the json tree.
/// \return Success flag
bool FindTypesInTerminologyCategory(std::string terminologyName, CodeIdentifier categoryId, std::vector<CodeIdentifier>& types, std::string search);
bool FindTypesInTerminologyCategory(std::string terminologyName, CodeIdentifier categoryId, std::vector<CodeIdentifier>& types, std::string search,
std::vector<vtkSmartPointer<vtkSlicerTerminologyType>>* typeObjects=nullptr);
/// Get a type with given name from a terminology category
/// \param type Output argument containing the details of the found type if any (if return value is true)
/// \return Success flag
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1227,47 +1227,49 @@ void qSlicerTerminologyNavigatorWidget::populateTypeTable()
}

// Get types in selected categories containing the search string. If no search string then add every type
std::vector<vtkSlicerTerminologiesModuleLogic::CodeIdentifier> types;
QMap<int, int> typeIndexToCategoryIndexMap;
int typeIndex = 0;
int categoryIndex = 0;
struct TypeInfo
{
vtkSlicerTerminologiesModuleLogic::CodeIdentifier id;
vtkSlicerTerminologyCategory* category;
QColor color;
};
std::vector<TypeInfo> types;
std::string searchTerm(d->SearchBox_Type->text().toUtf8().constData());
std::vector<vtkSlicerTerminologiesModuleLogic::CodeIdentifier>::iterator idIt;
std::vector<vtkSmartPointer<vtkSlicerTerminologyType>>::iterator typeObjIt;
std::set<std::string> existingTypesSchemeValue;
foreach (vtkSlicerTerminologyCategory* category, selectedCategories)
{
std::vector<vtkSlicerTerminologiesModuleLogic::CodeIdentifier> typesInCategory;
std::vector<vtkSmartPointer<vtkSlicerTerminologyType>> typesObjectsInCategory;
vtkSlicerTerminologiesModuleLogic::CodeIdentifier categoryId = vtkSlicerTerminologiesModuleLogic::CodeIdentifierFromTerminologyCategory(category);

logic->FindTypesInTerminologyCategory(
d->CurrentTerminologyName.toUtf8().constData(),
vtkSlicerTerminologiesModuleLogic::CodeIdentifierFromTerminologyCategory(category),
typesInCategory, searchTerm );
typesInCategory, searchTerm, &typesObjectsInCategory);

for (idIt=typesInCategory.begin(); idIt!=typesInCategory.end(); ++idIt)
std::vector<vtkSlicerTerminologiesModuleLogic::CodeIdentifier>::iterator idIt;
for (idIt=typesInCategory.begin(), typeObjIt=typesObjectsInCategory.begin(); idIt!=typesInCategory.end(); ++idIt, ++typeObjIt)
{
// Determine if type already exists in list
bool duplicate = false;
std::vector<vtkSlicerTerminologiesModuleLogic::CodeIdentifier>::iterator typeIt;
for (typeIt=types.begin(); typeIt!=types.end(); ++typeIt)
std::string typesSchemeValue = idIt->CodeValue + idIt->CodingSchemeDesignator;
if (existingTypesSchemeValue.find(typesSchemeValue) != existingTypesSchemeValue.end())
{
if ( !idIt->CodeValue.compare(typeIt->CodeValue)
&& !idIt->CodingSchemeDesignator.compare(typeIt->CodingSchemeDesignator) )
{
duplicate = true;
break;
}
// duplicate
continue;
}
if (!duplicate)
{
// Add type
types.push_back(*idIt);

// Store type-category relationship
typeIndexToCategoryIndexMap[typeIndex] = categoryIndex;
// Add type
TypeInfo typeInfo;
typeInfo.id = *idIt;
typeInfo.category = category;
typeInfo.color = d->recommendedColorForType(d->CurrentTerminologyName.toUtf8().constData(), category, *typeObjIt);
types.push_back(typeInfo);

typeIndex++;
}
// Store type-category relationship
existingTypesSchemeValue.insert(typesSchemeValue);
}

++categoryIndex;
}

QTableWidgetItem* selectedItem = nullptr;
Expand All @@ -1288,34 +1290,30 @@ void qSlicerTerminologyNavigatorWidget::populateTypeTable()
}

// Add type items to table
typeIndex = 0;
int typeIndex = 0;
vtkNew<vtkSlicerTerminologyType> typeObject;
for (idIt=types.begin(); idIt!=types.end(); ++idIt, ++typeIndex)
std::vector<TypeInfo>::iterator typeIt;
for (typeIt=types.begin(); typeIt!=types.end(); ++typeIt, ++typeIndex)
{
vtkSlicerTerminologiesModuleLogic::CodeIdentifier addedTypeId = (*idIt);
const vtkSlicerTerminologiesModuleLogic::CodeIdentifier &addedTypeId = typeIt->id;
QString addedTypeName(addedTypeId.CodeMeaning.c_str());
QTableWidgetItem* addedTypeItem = new QTableWidgetItem(addedTypeName);
addedTypeItem->setData(CodingSchemeDesignatorRole, QString(addedTypeId.CodingSchemeDesignator.c_str()));
addedTypeItem->setData(CodeValueRole, QString(addedTypeId.CodeValue.c_str()));
// Reference containing category so that it can be set when type is selected
vtkSlicerTerminologyCategory* category = selectedCategories[typeIndexToCategoryIndexMap[typeIndex]];
vtkSlicerTerminologyCategory* category = typeIt->category;
addedTypeItem->setData(CategoryCodingSchemeDesignatorRole, QString(category->GetCodingSchemeDesignator()));
addedTypeItem->setData(CategoryCodeValueRole, QString(category->GetCodeValue()));
addedTypeItem->setData(CategoryCodeMeaningRole, QString(category->GetCodeMeaning()));
QString tooltip = QString("Category: %1 (anatomy:%2)").arg(category->GetCodeMeaning()).arg(
(category->GetShowAnatomy() ? "available" : "N/A") );
addedTypeItem->setToolTip(tooltip);
// Set color preview (Note: does not show anything if colors are only stored in the type modifiers)
vtkSlicerTerminologiesModuleLogic::CodeIdentifier categoryId = vtkSlicerTerminologiesModuleLogic::CodeIdentifierFromTerminologyCategory(category);
if (logic->GetTypeInTerminologyCategory(
d->CurrentTerminologyName.toUtf8().constData(), categoryId, addedTypeId, typeObject))

if (typeIt->color.isValid())
{
QColor color = d->recommendedColorForType(d->CurrentTerminologyName.toUtf8().constData(), category, typeObject);
if (color.isValid())
{
addedTypeItem->setData(Qt::DecorationRole, color);
}
addedTypeItem->setData(Qt::DecorationRole, typeIt->color);
}

// Insert type item
d->tableWidget_Type->setItem(typeIndex + noneTypeExists, 0, addedTypeItem);

Expand Down

0 comments on commit 2bd24cd

Please sign in to comment.