@@ -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