Skip to content

Commit a4d9c97

Browse files
author
Binon
committed
Updated the resource collection filter from radio button to check boxes
1 parent d23b44f commit a4d9c97

File tree

4 files changed

+301
-40
lines changed

4 files changed

+301
-40
lines changed

LearningHub.Nhs.WebUI/Controllers/SearchController.cs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ namespace LearningHub.Nhs.WebUI.Controllers
88
using LearningHub.Nhs.Models.Search;
99
using LearningHub.Nhs.Models.Search.SearchClick;
1010
using LearningHub.Nhs.WebUI.Filters;
11+
using LearningHub.Nhs.WebUI.Helpers;
1112
using LearningHub.Nhs.WebUI.Interfaces;
1213
using LearningHub.Nhs.WebUI.Models.Search;
1314
using Microsoft.AspNetCore.Authorization;
@@ -16,6 +17,7 @@ namespace LearningHub.Nhs.WebUI.Controllers
1617
using Microsoft.AspNetCore.Routing;
1718
using Microsoft.Extensions.Logging;
1819
using Microsoft.Extensions.Options;
20+
using Microsoft.FeatureManagement;
1921
using Settings = LearningHub.Nhs.WebUI.Configuration.Settings;
2022

2123
/// <summary>
@@ -28,6 +30,7 @@ public class SearchController : BaseController
2830
{
2931
private readonly ISearchService searchService;
3032
private readonly IFileService fileService;
33+
private readonly IFeatureManager featureManager;
3134

3235
/// <summary>
3336
/// Initializes a new instance of the <see cref="SearchController"/> class.
@@ -38,17 +41,20 @@ public class SearchController : BaseController
3841
/// <param name="searchService">The searchService.</param>
3942
/// <param name="logger">The logger.</param>
4043
/// <param name="fileService">The fileService.</param>
44+
/// <param name="featureManager"> The Feature flag manager.</param>
4145
public SearchController(
4246
IHttpClientFactory httpClientFactory,
4347
IWebHostEnvironment hostingEnvironment,
4448
IOptions<Settings> settings,
4549
ISearchService searchService,
4650
ILogger<SearchController> logger,
47-
IFileService fileService)
51+
IFileService fileService,
52+
IFeatureManager featureManager)
4853
: base(hostingEnvironment, httpClientFactory, logger, settings.Value)
4954
{
5055
this.searchService = searchService;
5156
this.fileService = fileService;
57+
this.featureManager = featureManager;
5258
}
5359

5460
/// <summary>
@@ -65,18 +71,32 @@ public async Task<IActionResult> Index(SearchRequestViewModel search, bool noSor
6571
search.SearchId ??= 0;
6672
search.GroupId = !string.IsNullOrWhiteSpace(search.GroupId) && Guid.TryParse(search.GroupId, out Guid groupId) ? groupId.ToString() : Guid.NewGuid().ToString();
6773

68-
var searchResult = await this.searchService.PerformSearch(this.User, search);
74+
// Fix: Ensure an instance of IFeatureManager is injected and used
75+
var azureSearchEnabled = Task.Run(() => this.featureManager.IsEnabledAsync(FeatureFlags.AzureSearch)).Result;
76+
SearchResultViewModel searchResult = new SearchResultViewModel();
77+
78+
if (azureSearchEnabled)
79+
{
80+
searchResult = await this.searchService.PerformSearch(this.User, search);
81+
}
82+
else
83+
{
84+
searchResult = await this.searchService.PerformSearchInFindwise(this.User, search);
85+
}
6986

7087
if (search.SearchId == 0 && searchResult.ResourceSearchResult != null)
7188
{
7289
var searchId = await this.searchService.RegisterSearchEventsAsync(
7390
search,
7491
SearchFormActionTypeEnum.BasicSearch,
7592
searchResult.ResourceSearchResult.TotalHits,
76-
searchResult.CatalogueSearchResult.TotalHits);
93+
searchResult.CatalogueSearchResult != null ? searchResult.CatalogueSearchResult.TotalHits : 0);
7794

7895
searchResult.ResourceSearchResult.SearchId = searchId;
79-
searchResult.CatalogueSearchResult.SearchId = searchId;
96+
if (searchResult.CatalogueSearchResult != null)
97+
{
98+
searchResult.CatalogueSearchResult.SearchId = searchId;
99+
}
80100
}
81101

82102
if (filterApplied)

LearningHub.Nhs.WebUI/Interfaces/ISearchService.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ public interface ISearchService
2020
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
2121
Task<SearchResultViewModel> PerformSearch(IPrincipal user, SearchRequestViewModel searchRequest);
2222

23+
/// <summary>
24+
/// Performs a search - either a combined resource and catalogue search, or just a resource search if
25+
/// searching within a catalogue.
26+
/// </summary>
27+
/// <param name="user">User.</param>
28+
/// <param name="searchRequest">The SearchRequestViewModel.</param>
29+
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
30+
Task<SearchResultViewModel> PerformSearchInFindwise(IPrincipal user, SearchRequestViewModel searchRequest);
31+
2332
/// <summary>
2433
/// Records the analytics events associated with a search.
2534
/// </summary>

LearningHub.Nhs.WebUI/Services/SearchService.cs

Lines changed: 221 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace LearningHub.Nhs.WebUI.Services
1+
namespace LearningHub.Nhs.WebUI.Services
22
{
33
using System;
44
using System.Collections.Generic;
@@ -86,6 +86,226 @@ public async Task<SearchResultViewModel> PerformSearch(IPrincipal user, SearchRe
8686
ResourceAccessLevelFilterText = searchRequest.ResourceAccessLevelId.HasValue && searchRequest.ResourceAccessLevelId != (int)ResourceAccessibilityEnum.None ? $"&resource_access_level={searchRequest.ResourceAccessLevelId.Value}" : string.Empty,
8787
};
8888

89+
SearchViewModel resourceResult = null;
90+
var resourceCollectionFilter = new List<SearchFilterModel>();
91+
92+
if (searchString != string.Empty)
93+
{
94+
var resourceResultTask = this.GetSearchResultAsync(resourceSearchRequestModel);
95+
96+
if (searchRequest.CatalogueId.HasValue)
97+
{
98+
// Search within a catalogue - resources only.
99+
await resourceResultTask;
100+
resourceResult = resourceResultTask.Result;
101+
}
102+
else
103+
{
104+
await Task.WhenAll(resourceResultTask);
105+
106+
resourceResult = resourceResultTask.Result;
107+
108+
// Did you mean suggestion when no hits found
109+
if (resourceResult?.TotalHits == 0 && (resourceResult?.Spell?.Suggestions?.Count > 0))
110+
{
111+
didYouMeanEnabled = true;
112+
113+
// pass the spell suggestion as new search text - resources
114+
if (resourceResult?.Spell?.Suggestions?.Count > 0)
115+
{
116+
resourceSearchRequestModel.SearchText = Regex.Replace(resourceResult?.Spell?.Suggestions?.FirstOrDefault().ToString(), "<.*?>", string.Empty);
117+
suggestedResource = resourceSearchRequestModel.SearchText;
118+
119+
// calling findwise endpoint with new search text - resources
120+
resourceResultTask = this.GetSearchResultAsync(resourceSearchRequestModel);
121+
}
122+
123+
await Task.WhenAll(resourceResultTask);
124+
125+
resourceResult = resourceResultTask.Result;
126+
}
127+
}
128+
129+
var searchfilters = new List<SearchFilterModel>();
130+
var resourceAccessLevelFilters = new List<SearchFilterModel>();
131+
132+
var providerfilters = new List<SearchFilterModel>();
133+
134+
if (resourceResult != null && resourceResult.Facets != null && resourceResult.Facets.Length > 0)
135+
{
136+
var filters = resourceResult.Facets.Where(x => x.Id == "resource_type").First().Filters;
137+
138+
foreach (var filteritem in filters.Select(x => x.DisplayName.ToLower()).Distinct())
139+
{
140+
var filter = filters.Where(x => x.DisplayName == filteritem).FirstOrDefault();
141+
142+
if (filter != null && UtilityHelper.FindwiseResourceTypeDict.ContainsKey(filter.DisplayName))
143+
{
144+
var resourceTypeEnum = UtilityHelper.FindwiseResourceTypeDict[filter.DisplayName];
145+
var searchfilter = new SearchFilterModel() { DisplayName = UtilityHelper.GetPrettifiedResourceTypeName(resourceTypeEnum), Count = filter.Count, Value = filteritem, Selected = searchRequest.Filters?.Contains(filter.DisplayName) ?? false };
146+
searchfilters.Add(searchfilter);
147+
}
148+
}
149+
150+
if (user.IsInRole("BasicUser"))
151+
{
152+
var accessLevelFilters = resourceResult.Facets.Where(x => x.Id == "resource_access_level").First().Filters;
153+
154+
var generalAccessValue = (int)ResourceAccessibilityEnum.GeneralAccess;
155+
var basicUserAudienceFilterItem = accessLevelFilters.Where(x => x.DisplayName == generalAccessValue.ToString()).FirstOrDefault();
156+
var basicResourceAccesslevelCount = basicUserAudienceFilterItem?.Count ?? 0;
157+
var basicUserAudienceFilter = new SearchFilterModel() { DisplayName = ResourceAccessLevelHelper.GetPrettifiedResourceAccessLevelOptionDisplayName(ResourceAccessibilityEnum.GeneralAccess), Count = basicResourceAccesslevelCount, Value = generalAccessValue.ToString(), Selected = (searchRequest.ResourceAccessLevelId ?? 0) == generalAccessValue };
158+
resourceAccessLevelFilters.Add(basicUserAudienceFilter);
159+
}
160+
161+
filters = resourceResult.Facets.Where(x => x.Id == "provider_ids").First().Filters;
162+
163+
if (filters.Length > 0)
164+
{
165+
var providers = await this.providerService.GetProviders();
166+
var provider_ids = providers.Select(n => n.Id).ToList();
167+
168+
foreach (var filteritem in filters.Select(x => x.DisplayName.ToLower()).Distinct())
169+
{
170+
var filter = filters.Where(x => x.DisplayName == filteritem).FirstOrDefault();
171+
172+
if (filter != null && provider_ids.Contains(Convert.ToInt32(filter.DisplayName)))
173+
{
174+
var provider = providers.Where(n => n.Id == Convert.ToInt32(filter.DisplayName)).FirstOrDefault();
175+
176+
var searchfilter = new SearchFilterModel() { DisplayName = provider.Name, Count = filter.Count, Value = filteritem, Selected = searchRequest.ProviderFilters?.Contains(filter.DisplayName) ?? false };
177+
providerfilters.Add(searchfilter);
178+
}
179+
}
180+
}
181+
182+
// Process resource_collection facets
183+
var collectionFacet = resourceResult.Facets.FirstOrDefault(x => x.Id == "resource_collection");
184+
if (collectionFacet != null && collectionFacet.Filters != null)
185+
{
186+
foreach (var filteritem in collectionFacet.Filters.Select(x => x.DisplayName).Distinct())
187+
{
188+
var filter = collectionFacet.Filters.Where(x => x.DisplayName == filteritem).FirstOrDefault();
189+
190+
if (filter != null && !string.IsNullOrEmpty(filter.DisplayName))
191+
{
192+
var searchfilter = new SearchFilterModel()
193+
{
194+
DisplayName = filter.DisplayName,
195+
Count = filter.Count,
196+
Value = filter.DisplayName.ToLower(),
197+
Selected = searchRequest.ResourceCollectionFilter?.Contains(filter.DisplayName, StringComparer.OrdinalIgnoreCase) ?? false,
198+
};
199+
resourceCollectionFilter.Add(searchfilter);
200+
}
201+
}
202+
203+
if (resourceCollectionFilter.Any())
204+
{
205+
// Sum of all counts
206+
var allCount = resourceCollectionFilter.Sum(x => x.Count);
207+
208+
// If none selected ➜ All is selected
209+
// If ALL child filters are selected → All is selected
210+
var allSelected =
211+
!resourceCollectionFilter.Any(x => x.Selected) ||
212+
resourceCollectionFilter.All(x => x.Selected);
213+
214+
// Create the ALL filter
215+
var allFilter = new SearchFilterModel
216+
{
217+
DisplayName = "All",
218+
Value = "all",
219+
Count = allCount,
220+
Selected = allSelected,
221+
};
222+
223+
// Insert at top
224+
resourceCollectionFilter.Insert(0, allFilter);
225+
}
226+
}
227+
}
228+
229+
resourceResult.SortItemList = searchSortItemList;
230+
resourceResult.SortItemSelected = selectedSortItem;
231+
resourceResult.SearchFilters = searchfilters;
232+
resourceResult.SearchResourceAccessLevelFilters = resourceAccessLevelFilters;
233+
resourceResult.SearchProviderFilters = providerfilters;
234+
}
235+
236+
var searchResultViewModel = new SearchResultViewModel
237+
{
238+
SearchString = searchString,
239+
GroupId = groupId,
240+
FeedbackSubmitted = searchRequest.FeedbackSubmitted ?? false,
241+
ResourceCurrentPageIndex = searchRequest.ResourcePageIndex ?? 0,
242+
CatalogueCurrentPageIndex = searchRequest.CataloguePageIndex ?? 0,
243+
ResourceSearchResult = resourceResult,
244+
245+
// SearchCollectionFilters = resourceCollectionFilters,
246+
ResourceResultPaging = new SearchResultPagingModel
247+
{
248+
CurrentPage = searchRequest.ResourcePageIndex ?? 0,
249+
PageSize = resourceSearchPageSize,
250+
TotalItems = resourceResult?.TotalHits ?? 0,
251+
},
252+
253+
CatalogueResultPaging = new SearchResultPagingModel
254+
{
255+
CurrentPage = searchRequest.CataloguePageIndex ?? 0,
256+
PageSize = catalogueSearchPageSize,
257+
},
258+
DidYouMeanEnabled = didYouMeanEnabled,
259+
SuggestedCatalogue = suggestedCatalogue,
260+
SuggestedResource = suggestedResource,
261+
};
262+
263+
searchResultViewModel.ResourceCollectionFilter = resourceCollectionFilter;
264+
265+
return searchResultViewModel;
266+
}
267+
268+
/// <summary>
269+
/// Performs a search - either a combined resource and catalogue search, or just a resource search if
270+
/// searching within a catalogue.
271+
/// </summary>
272+
/// <param name="user">user.</param>
273+
/// <param name="searchRequest">The SearchRequestViewModel.</param>
274+
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
275+
public async Task<SearchResultViewModel> PerformSearchInFindwise(IPrincipal user, SearchRequestViewModel searchRequest)
276+
{
277+
var searchSortType = 0;
278+
if (searchRequest.Sortby.HasValue && Enum.IsDefined(typeof(SearchSortTypeEnum), searchRequest.Sortby))
279+
{
280+
searchSortType = searchRequest.Sortby.Value;
281+
}
282+
283+
var searchString = searchRequest.Term?.Trim() ?? string.Empty;
284+
var searchSortItemList = SearchHelper.GetSearchSortList();
285+
var selectedSortItem = searchSortItemList.Where(x => x.SearchSortType == (SearchSortTypeEnum)searchSortType).FirstOrDefault();
286+
var groupId = Guid.Parse(searchRequest.GroupId);
287+
bool didYouMeanEnabled = false;
288+
var suggestedCatalogue = string.Empty;
289+
var suggestedResource = string.Empty;
290+
291+
var resourceSearchPageSize = this.settings.FindwiseSettings.ResourceSearchPageSize;
292+
var catalogueSearchPageSize = this.settings.FindwiseSettings.CatalogueSearchPageSize;
293+
294+
var resourceSearchRequestModel = new SearchRequestModel
295+
{
296+
SearchId = searchRequest.SearchId.Value,
297+
SearchText = searchString,
298+
FilterText = this.BuildFilterText(searchRequest.Filters, searchRequest.ResourceCollectionFilter),
299+
ProviderFilterText = searchRequest.ProviderFilters?.Any() == true ? $"&provider_ids={string.Join("&provider_ids=", searchRequest.ProviderFilters)}" : string.Empty,
300+
SortColumn = selectedSortItem.Value,
301+
SortDirection = selectedSortItem?.SortDirection,
302+
PageIndex = searchRequest.ResourcePageIndex ?? 0,
303+
PageSize = resourceSearchPageSize,
304+
GroupId = groupId,
305+
CatalogueId = searchRequest.CatalogueId,
306+
ResourceAccessLevelFilterText = searchRequest.ResourceAccessLevelId.HasValue && searchRequest.ResourceAccessLevelId != (int)ResourceAccessibilityEnum.None ? $"&resource_access_level={searchRequest.ResourceAccessLevelId.Value}" : string.Empty,
307+
};
308+
89309
var catalogueSearchRequestModel = new CatalogueSearchRequestModel()
90310
{
91311
SearchId = searchRequest.SearchId.Value,

0 commit comments

Comments
 (0)