public static List <Filter> GetFilters(List <KeyValuePair <string, string> > parameters) { List <Filter> filters = new List <Filter>(); foreach (KeyValuePair <string, string> kvpair in parameters.Where(p => p.Key == "filter")) { string filterName = kvpair.Value.EmGetTextBeforeDelimiter(':'); FilterMapInfo fmInfo = Providers.filterMapInfoList.Single(f => f.FilterName == filterName); int index = filters.FindIndex(f => f.AzureIndexFieldName == fmInfo.AzureIndexFieldName); if (index < 0) { //We don't already have this one in the filter list. Add it and its values. filters.Add(new Filter { AzureIndexFieldName = fmInfo.AzureIndexFieldName, Values = new List <string>() { kvpair.Value.Substring(filterName.Length + 1) } }); continue; } //This filter name is already in the filters list. Just add the values. filters[index].Values.Add(kvpair.Value.Substring(filterName.Length + 1)); } return(filters); }
public static List <Filter> GetFilters(List <NameValue> parameters) { List <Filter> filters = new List <Filter>(); foreach (NameValue nv in parameters.Where(p => p.Name == "filter")) { string filterName = nv.Value.EmGetTextBeforeDelimiter(':'); FilterMapInfo fmInfo = Providers.filterMapInfoList.Single(f => f.FilterName == filterName); int index = filters.FindIndex(f => f.AzureIndexFieldName == fmInfo.AzureIndexFieldName); if (index < 0) { //We don't already have this one in the filter list. Add it and its values. if (filterName == "condition_primarycare") { //There can only be one of these filters. filters.Add(new Filter { AzureIndexFieldName = fmInfo.AzureIndexFieldName, Values = new List <string>() { "true" } }); continue; } filters.Add(new Filter { AzureIndexFieldName = fmInfo.AzureIndexFieldName, Values = new List <string>() { nv.Value.Substring(filterName.Length + 1) } }); continue; } //This filter name is already in the filters list. Just add the values. filters[index].Values.Add(nv.Value.Substring(filterName.Length + 1)); } return(filters); }
public static bool AreParametersAcceptable(List <NameValue> queryParams, out List <string> failureReasons) { //The keys and filter name parts of the value of the query parms (what is there before the colon) are all lower case at this point. failureReasons = new List <string>(); if (queryParams.Count == 0) { failureReasons.Add("No parameters provided."); } List <NameValue> entries; //Ensure you only received recognizable parameters. List <string> parameterNames = new List <string>() { "skip", "take", "seed", "universal", "filter", "latitude", "longitude", "radius", "includefacets", "includetotalcount" }; //TODO still need to deal with lat/lon/radius everywhere. foreach (NameValue parm in queryParams) { if (parameterNames.Contains(parm.Name) == false) { failureReasons.Add($"'{parm.Name}' is not a known parameter."); } } //Validate the 'skip' parameter. entries = queryParams .Where(q => q.Name == "skip") .ToList(); if (entries.Count == 0) { failureReasons.Add("'skip' parameter is required."); } if (entries.Count > 1) { failureReasons.Add("'skip' parameter provided more than once."); } if (entries.Count == 1) { int skip; if (int.TryParse(entries[0].Value, out skip) == false) { failureReasons.Add($"The 'skip' parameter value must be a number. Received {entries[0].Value}."); } //Seed must be provided on any subsequent pages. It should in some cases be required on the first page too when the user navigates back to the first page. But that we don't know here. entries = queryParams .Where(q => q.Name == "seed") .ToList(); if (entries.Count > 1) { failureReasons.Add("There can only be one 'seed' parameter."); } if (skip > 0 && entries.Count == 0) { failureReasons.Add("'seed' required on subsequent page calls."); } } //Validate the 'take' parameter. entries = queryParams .Where(q => q.Name == "take") .ToList(); if (entries.Count == 0) { failureReasons.Add("'take' parameter is required."); } if (entries.Count > 1) { failureReasons.Add("'take' parameter provided more than once."); } if (entries.Count == 1) { int take; if (int.TryParse(entries[0].Value, out take) == false) { failureReasons.Add($"The 'take' parameter value must be a number. Received {entries[0].Value}."); } else { if (take <= 0) { failureReasons.Add("'take' has to be greater than 0."); } if (take > 100) { failureReasons.Add("'take' cannot exceed 100."); } } } //Validate the 'includeFacets' parameter. entries = queryParams .Where(q => q.Name == "includefacets") .ToList(); if (entries.Count > 1) { failureReasons.Add("'includeFacets' parameter provided more than once."); } if (entries.Count == 1) { bool include; if (bool.TryParse(entries[0].Value, out include) == false) { failureReasons.Add($"The 'includeFacets' parameter value must have a boolean value. Received {entries[0].Value}."); } } //Validate the 'includeTotalCount' parameter. entries = queryParams .Where(q => q.Name == "includetotalcount") .ToList(); if (entries.Count > 1) { failureReasons.Add("'includeTotalCount' parameter provided more than once."); } if (entries.Count == 1) { bool include; if (bool.TryParse(entries[0].Value, out include) == false) { failureReasons.Add($"The 'includeTotalCount' parameter value must have a boolean value. Received {entries[0].Value}."); } } //Validate 'filter' parameters List <NameValue> filters = queryParams .Where(q => q.Name == "filter") .ToList(); //We can have "&filter=agesSeen:babies&filter=agesSeen:youth". This is an AND for agesSeen. All good so far. //But we cannot have more than one suggestion type filter. So "&filter=condition:cancer&filter=condition:blood" is not allowed. //This is because these type filters come from the suggestions. //Try and get the filter names first. List <string> filterNamesRequested = filters .Select(f => f.Value.EmGetTextBeforeDelimiter(':')) .ToList(); List <string> formattedFilterNamesRequested = filterNamesRequested .Where(f => f != null && f.Length != 0) .ToList(); if (formattedFilterNamesRequested.Count != filterNamesRequested.Count) { failureReasons.Add("Received one or more filters without a facet name, colon or value."); } //List<string> suggestions = formattedFilterNamesRequested // .Intersect(Providers.filterMapInfoList.Where(fmi => fmi.IsSuggestion == true).Select(fmi => fmi.FilterName)) // .ToList(); //if (suggestions.Count > 1) //{ // foreach (string dupSuggestion in suggestions) // { // failureReasons.Add($"Received more than one suggestion parameter, namely {dupSuggestion}."); // } //} //Check formatting of filter parameters. foreach (NameValue filter in filters) { string filterName = filter.Value.EmGetTextBeforeDelimiter(':'); if (filterName == null) { failureReasons.Add($"Invalid filter value, {filter.Value}. Missing the colon."); } else { if (filterName.Length == 0) { failureReasons.Add($"Invalid filter value, {filter.Value}. Search value not provided."); } } } //Check for valid filter names. foreach (string filterNameRequested in formattedFilterNamesRequested) { if (Providers.filterMapInfoList.Count(f => f.FilterName == filterNameRequested) == 0) { failureReasons.Add($"'{filterNameRequested}' is an invalid filter name."); } } //Need to have at least one filter or one universal search parameter. List <NameValue> universalSearches = queryParams .Where(q => q.Name == "universal") .ToList(); if (universalSearches.Count > 1) { failureReasons.Add("Can only specify one 'universal' search parameter."); } if (filters.Count == 0 && universalSearches.Count == 0) { failureReasons.Add("Must specify at least one universal search or one filter parameter."); } //Do not allow multiple entries for the boolean types. entries = queryParams.Where(q => q.Name == "filter").ToList(); foreach (NameValue fil in entries) { string filterName = fil.Value.EmGetTextBeforeDelimiter(':'); if (string.IsNullOrWhiteSpace(filterName)) { continue; } FilterMapInfo fmi = Providers.filterMapInfoList.SingleOrDefault(f => f.FilterName == filterName); if (fmi == null) { continue; } if (fmi.AzureIndexFieldType != AzureIndexFieldTypes.boolean) { continue; } //Booleans can only have true/false values. string val = fil.Value.Substring(filterName.Length + 1); if (!(val.EmCompareIgnoreCase("true") || val.EmCompareIgnoreCase("false"))) { if (filterName != "condition_primarycare") { //The search value will later in GetFilters() be converted to a boolean true value. failureReasons.Add($"'{fmi.FilterName}' has an invalid value, namely '{val}'."); } } //There can only be one of these parameter entries for this type of filter. int cnt = 0; foreach (NameValue k in entries) { if (k.Value.EmGetTextBeforeDelimiter(':') == filterName) { cnt++; } } if (cnt > 1) { failureReasons.Add($"Can only specify one '{filterName}' filter parameter."); } } return(failureReasons.Count == 0); }
public static SearchParameters BuildAzureSearchParameters(int skip, int take, string universal, List <Filter> filters, bool includeFacets, bool includeTotalCount) { List <string> facets = new List <string>() { "agesSeen", "acceptedInsurances", "acceptNewPatients", "isMale", "providerType", "languages", "networkAffiliations" }; List <string> searchFields = null; string search = "*"; SearchMode searchMode = SearchMode.All; QueryType queryType = QueryType.Simple; if (universal != null) { searchFields = universalSearchFields; search = universal; //wild cards? } string filter = null; if (filters.Count > 0) { queryType = QueryType.Full; filter = string.Empty; foreach (Filter f in filters) { //Find filter entry in the filter map info list. FilterMapInfo fmInfo = filterMapInfoList.Single(m => m.AzureIndexFieldName == f.AzureIndexFieldName); foreach (string val in f.Values) { switch (fmInfo.AzureIndexFieldType) { case AzureIndexFieldTypes.collection: filter += $"({fmInfo.AzureIndexFieldName}/any(i: i eq '{val}')) and "; break; case AzureIndexFieldTypes.boolean: filter += $"({f.AzureIndexFieldName} eq {val}) and "; break; case AzureIndexFieldTypes.text: filter += $"({f.AzureIndexFieldName} eq '{val}') and "; break; //TODO When adding lat/lon/radius, will need to add more types. The radius searches goes off elsewhere first anyhow. } } } filter = filter.Substring(0, filter.Length - 5); //Chop off the last 'and'. } SearchParameters searchParameters = new SearchParameters { Facets = (includeFacets ? facets.ToArray() : null), Filter = filter, IncludeTotalResultCount = includeTotalCount, OrderBy = new List <string>() { "searchRank", "randomNumber" }, //What about relevance/score? QueryType = queryType, SearchFields = searchFields, SearchMode = searchMode, Select = new[] { "id", "searchRank" }, Skip = skip, Top = take }; return(searchParameters); }