1- namespace LearningHub . Nhs . OpenApi . Services . Helpers . Search
1+ namespace LearningHub . Nhs . OpenApi . Services . Helpers . Search
22{
33 using System ;
44 using System . Collections . Generic ;
@@ -15,7 +15,7 @@ public static Dictionary<string, List<string>> CombineAndNormaliseFilters(string
1515 {
1616 var filters = new Dictionary < string , List < string > >
1717 {
18- // { "resource_collection", new List<string> { "Resource" } }
18+ // { "resource_collection", new List<string> { "Resource" } }
1919 } ;
2020
2121 // Parse and merge additional filters from query string
@@ -26,7 +26,7 @@ public static Dictionary<string, List<string>> CombineAndNormaliseFilters(string
2626 MergeFilterDictionary ( filters , requestTypeFilters ) ;
2727 // MergeFilterDictionary(filters, providerFilters);
2828
29- NormaliseFilters ( filters ) ;
29+ // NormaliseFilters(filters);
3030
3131 return filters ;
3232 }
@@ -47,25 +47,93 @@ private static void MergeFilterDictionary(Dictionary<string, List<string>> targe
4747 /// </summary>
4848 /// <param name="filters">The filters to apply.</param>
4949 /// <returns>The filter expression string.</returns>
50- public static string BuildFilterExpression ( Dictionary < string , List < string > > ? filters )
50+ /// <summary>
51+ /// Build an OData filter that supports multi-select values.
52+ /// Pass a dictionary where key = field name, value = list of selected values.
53+ /// If `collectionFields` contains a field name, that field will be treated as a collection and use any(...).
54+ /// </summary>
55+ public static string BuildFilterExpression (
56+ Dictionary < string , List < string > > ? filters ,
57+ ISet < string > ? collectionFields = null )
5158 {
5259 if ( filters == null || ! filters . Any ( ) )
5360 return string . Empty ;
5461
55- var filterExpressions = new List < string > ( ) ;
62+ collectionFields ?? = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) ;
5663
57- foreach ( var filter in filters )
64+ // Handle spacing, NBSP, escaping quotes
65+ string Normalize ( string v )
5866 {
59- if ( filter . Value ? . Any ( ) == true )
67+ if ( v == null ) return string . Empty ;
68+
69+ // Replace NBSP
70+ v = v . Replace ( '\u00A0 ' , ' ' ) . Trim ( ) ;
71+
72+ // Collapse multiple spaces
73+ v = System . Text . RegularExpressions . Regex . Replace ( v , @"\s+" , " " ) ;
74+
75+ // Escape single quotes for OData
76+ v = v . Replace ( "'" , "''" ) ;
77+
78+ return v ;
79+ }
80+
81+ var expressions = new List < string > ( ) ;
82+
83+ foreach ( var kvp in filters )
84+ {
85+ var field = kvp . Key ;
86+ var values = kvp . Value ? . Where ( v => ! string . IsNullOrWhiteSpace ( v ) ) . ToList ( ) ;
87+
88+ if ( values == null || values . Count == 0 )
89+ continue ;
90+
91+ // Normalize all values
92+ var normalizedValues = values . Select ( Normalize ) . Distinct ( ) . ToList ( ) ;
93+
94+ // Single value → use eq
95+ if ( normalizedValues . Count == 1 )
96+ {
97+ var v = normalizedValues [ 0 ] ;
98+
99+ if ( collectionFields . Contains ( field ) )
100+ {
101+ expressions . Add ( $ "{ field } /any(t: t eq '{ v } ')") ;
102+ }
103+ else
104+ {
105+ expressions . Add ( $ "{ field } eq '{ v } '") ;
106+ }
107+
108+ continue ;
109+ }
110+
111+ // Multiple values → use OR conditions (ALWAYS works)
112+ if ( collectionFields . Contains ( field ) )
60113 {
61- var values = string . Join ( "," , filter . Value ) ;
62- filterExpressions . Add ( $ "search.in({ filter . Key } , '{ values } ')") ;
114+ // collection field (array) → OR any(...) conditions
115+ var ors = normalizedValues
116+ . Select ( v => $ "{ field } /any(t: t eq '{ v } ')") ;
117+
118+ expressions . Add ( "(" + string . Join ( " or " , ors ) + ")" ) ;
119+ }
120+ else
121+ {
122+ // single string field → OR eq conditions
123+ var ors = normalizedValues
124+ . Select ( v => $ "{ field } eq '{ v } '") ;
125+
126+ expressions . Add ( "(" + string . Join ( " or " , ors ) + ")" ) ;
63127 }
64128 }
65129
66- return filterExpressions . Any ( ) ? string . Join ( " and " , filterExpressions ) : string . Empty ;
130+ return expressions . Count > 0
131+ ? string . Join ( " and " , expressions )
132+ : string . Empty ;
67133 }
68134
135+
136+
69137 /// <summary>
70138 /// Parses filter parameters from a query string.
71139 /// </summary>
0 commit comments