Skip to content

Commit 43a763d

Browse files
Refine search results and equivalent variable names display
1 parent 9c98b16 commit 43a763d

File tree

1 file changed

+51
-27
lines changed

1 file changed

+51
-27
lines changed

frontend/src/pages/cohorts.tsx

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -44,32 +44,45 @@ const SearchResultsDisplay = React.memo(({cohortsData, searchTerms, searchMode,
4444
}) => {
4545
const results = useMemo(() => {
4646
if (searchTerms.length === 0) return {
47-
matchedCohorts: [] as string[],
47+
matchedCohorts: [] as {cohortId: string; sections: string[]}[],
4848
variablesByCohort: {} as Record<string, string[]>,
4949
totalVariables: 0
5050
};
5151

52-
const searchableCohortFields = [
53-
'cohort_id', 'institution', 'study_type', 'study_objective', 'morbidity',
54-
'study_participants', 'study_population', 'administrator', 'population_location',
55-
'primary_outcome_spec', 'secondary_outcome_spec'
56-
];
52+
// Map field names to human-readable section names (lowercase)
53+
const fieldToSection: Record<string, string> = {
54+
'cohort_id': 'cohort name',
55+
'institution': 'institution',
56+
'study_type': 'study type',
57+
'study_objective': 'study objective',
58+
'morbidity': 'morbidity',
59+
'study_participants': 'study participants',
60+
'study_population': 'study population',
61+
'administrator': 'administrator',
62+
'population_location': 'population location',
63+
'primary_outcome_spec': 'primary outcome specification',
64+
'secondary_outcome_spec': 'secondary outcome specification',
65+
};
66+
const searchableCohortFields = Object.keys(fieldToSection);
5767
const searchableVarFields = ['var_name', 'var_label', 'concept_name', 'mapped_label', 'omop_domain', 'concept_code', 'omop_id'];
5868
const searchableCatFields = ['value', 'label', 'mapped_label'];
5969

60-
const matchedCohorts: string[] = [];
70+
const matchedCohorts: {cohortId: string; sections: string[]}[] = [];
6171
const variablesByCohort: Record<string, string[]> = {};
6272
let totalVariables = 0;
6373

6474
Object.entries(cohortsData).forEach(([cohortId, cohortData]) => {
6575
// Check cohort metadata match (for 'cohorts' or 'all' scope)
6676
if (searchScope === 'cohorts' || searchScope === 'all') {
6777
const cohortWithId = { ...cohortData, cohort_id: cohortId };
68-
const cohortMatches = searchableCohortFields.some(field =>
69-
matchesSearchTerms((cohortWithId as any)[field], searchTerms, searchMode)
70-
);
71-
if (cohortMatches && !matchedCohorts.includes(cohortId)) {
72-
matchedCohorts.push(cohortId);
78+
const matchedSections: string[] = [];
79+
searchableCohortFields.forEach(field => {
80+
if (matchesSearchTerms((cohortWithId as any)[field], searchTerms, searchMode)) {
81+
matchedSections.push(fieldToSection[field]);
82+
}
83+
});
84+
if (matchedSections.length > 0 && !matchedCohorts.some(c => c.cohortId === cohortId)) {
85+
matchedCohorts.push({ cohortId, sections: matchedSections });
7386
}
7487
}
7588

@@ -104,6 +117,13 @@ const SearchResultsDisplay = React.memo(({cohortsData, searchTerms, searchMode,
104117

105118
const cohortsWithVarMatches = Object.keys(results.variablesByCohort).length;
106119

120+
// Format cohort metadata results: "CohortA (study objective, morbidity), CohortB (institution)"
121+
const formatCohortResults = () => {
122+
return results.matchedCohorts
123+
.map(({cohortId, sections}) => `${cohortId} (${sections.join(', ')})`)
124+
.join(', ');
125+
};
126+
107127
// Format variable results: "var1, var2 (CohortA); var3 (CohortB)"
108128
const formatVariableResults = () => {
109129
return Object.entries(results.variablesByCohort)
@@ -119,7 +139,7 @@ const SearchResultsDisplay = React.memo(({cohortsData, searchTerms, searchMode,
119139
</span>
120140
{results.matchedCohorts.length > 0 && (
121141
<div className="mt-1 text-xs text-gray-600 dark:text-gray-400">
122-
<strong>Cohorts:</strong> {results.matchedCohorts.join(', ')}
142+
<strong>Studies metadata:</strong> {formatCohortResults()}
123143
</div>
124144
)}
125145
</div>
@@ -150,7 +170,7 @@ const SearchResultsDisplay = React.memo(({cohortsData, searchTerms, searchMode,
150170
{(results.matchedCohorts.length > 0 || results.totalVariables > 0) && (
151171
<div className="mt-1 text-xs text-gray-600 dark:text-gray-400 max-h-24 overflow-y-auto">
152172
{results.matchedCohorts.length > 0 && (
153-
<div><strong>Cohorts:</strong> {results.matchedCohorts.join(', ')}</div>
173+
<div><strong>Studies metadata:</strong> {formatCohortResults()}</div>
154174
)}
155175
{results.totalVariables > 0 && (
156176
<div><strong>Variables:</strong> {formatVariableResults()}</div>
@@ -182,9 +202,9 @@ const EquivalentVariableNames = React.memo(({cohortsData, searchTerms, searchMod
182202

183203
Object.entries(cohortsData).forEach(([_cohortId, cohortData]) => {
184204
Object.entries(cohortData.variables || {}).forEach(([varName, varData]) => {
185-
const nameMatches = matchesSearchTerms(varName, searchTerms, searchMode);
205+
const nameMatches = matchesSearchTerms(varName, searchTerms, 'and');
186206
if (nameMatches && varData.concept_code) {
187-
const code = varData.concept_code.trim();
207+
const code = varData.concept_code.trim().toUpperCase();
188208
if (code) {
189209
if (!matchedConceptCodes.has(code)) {
190210
matchedConceptCodes.set(code, new Set());
@@ -207,7 +227,7 @@ const EquivalentVariableNames = React.memo(({cohortsData, searchTerms, searchMod
207227
Object.entries(cohortsData).forEach(([cohortId, cohortData]) => {
208228
const namesInCohort: string[] = [];
209229
Object.entries(cohortData.variables || {}).forEach(([varName, varData]) => {
210-
if (varData.concept_code && varData.concept_code.trim() === conceptCode) {
230+
if (varData.concept_code && varData.concept_code.trim().toUpperCase() === conceptCode) {
211231
namesInCohort.push(varName);
212232
if (!conceptName && varData.concept_name) {
213233
conceptName = varData.concept_name;
@@ -227,15 +247,20 @@ const EquivalentVariableNames = React.memo(({cohortsData, searchTerms, searchMod
227247
return a.isMatched ? 1 : -1;
228248
});
229249

230-
// Only show if there's at least one name beyond the matched ones
231-
const hasEquivalent = cohortEntries.some(e =>
232-
e.names.some(n => !matchedVarNames.has(n))
233-
);
234-
if (hasEquivalent) {
250+
// Show if there are multiple distinct variable names for this concept code
251+
const allNames = new Set(cohortEntries.flatMap(e => e.names));
252+
if (allNames.size > 1 || cohortEntries.length > 1) {
235253
result.push({ conceptCode, conceptName, namesByCohort: cohortEntries });
236254
}
237255
});
238256

257+
// Sort groups by total number of variable names (largest first)
258+
result.sort((a, b) => {
259+
const countA = a.namesByCohort.reduce((sum, e) => sum + e.names.length, 0);
260+
const countB = b.namesByCohort.reduce((sum, e) => sum + e.names.length, 0);
261+
return countB - countA;
262+
});
263+
239264
return result.length > 0 ? result : null;
240265
}, [cohortsData, searchTerms, searchMode, searchScope]);
241266

@@ -252,17 +277,16 @@ const EquivalentVariableNames = React.memo(({cohortsData, searchTerms, searchMod
252277
{expanded && (
253278
<div className="mt-2 space-y-2">
254279
{equivalentNames.map(({conceptCode, conceptName, namesByCohort}) => (
255-
<div key={conceptCode} className="p-2 bg-base-100 rounded-lg border border-base-300 text-sm">
280+
<div key={conceptCode} className="p-2 bg-base-100 rounded-lg border border-base-300 text-base">
256281
<div className="font-semibold text-gray-700 dark:text-gray-300 mb-1">
257282
Standard code: <span className="text-primary">{conceptCode}</span>
258283
{conceptName && <span className="ml-1 text-gray-500">({conceptName})</span>}
259284
</div>
260285
<div className="space-y-1">
261-
{namesByCohort.map(({cohortId, names, isMatched}) => (
262-
<div key={cohortId} className={isMatched ? 'text-gray-400 dark:text-gray-500' : ''}>
286+
{namesByCohort.map(({cohortId, names}) => (
287+
<div key={cohortId}>
263288
<span className="font-medium">{cohortId}:</span>{' '}
264289
{names.join(', ')}
265-
{isMatched && <span className="ml-1 text-xs italic">(matched)</span>}
266290
</div>
267291
))}
268292
</div>
@@ -763,7 +787,7 @@ export default function CohortsList() {
763787

764788
{/* Search Results Display */}
765789
{searchInput.trim() && (
766-
<div className="mt-2 p-2 bg-base-200 rounded-lg text-base">
790+
<div className="mt-2 p-2 bg-base-200 rounded-lg text-lg">
767791
<div className="flex items-start gap-3">
768792
<span className="text-gray-600 dark:text-gray-400 mt-0.5">🔍</span>
769793
<div className="flex-1">

0 commit comments

Comments
 (0)