I needed to implement a search that would be called via an Azure Function and require the passing of latitude and longitude to facilitate searching within a specific distance. So I started to build my Azure function using the SDK and it ended up looking like what is described in this post.
So I’ve been working on a demo project using Azure Search, and if you’ve followed this blog for a while, you know. I do a lot of work that requires Azure Government. Well, recently, I needed to implement a search that would be called via an Azure Function and require the passing of latitude and longitude to facilitate the searching within a specific distance. So I started to build my Azure function using the SDK. And what I ended up with looked a lot like this...
Key Data Elements
First, to be able to interact with my search service, I need to install the following nuget package:
Microsoft.Azure.Search
And upon doing so, I found pretty good documentation here for building the search client. So I built out a GeoSearchProvider
class that looked like the following:
NOTE: I use a custom class called IConfigurationProvider
which encapsulates my configuration store, in most cases its KeyVault
, but it can be a variety of other options.
public class GeoSearchProvider : IGeoSearchProvider
{
IConfigurationProvider _configurationProvider;
public GeoSearchProvider(IConfigurationProvider configurationProvider)
{
_configurationProvider = configurationProvider;
}
public async Task<DocumentSearchResult<SearchResultModel>>
RunSearch(string text, string latitude, string longitude,
string kmdistance, Microsoft.Extensions.Logging.ILogger log)
{
if (String.IsNullOrEmpty(kmdistance))
{
kmdistance = await _configurationProvider.GetSetting("SearchDefaultDistance");
}
var serviceName = await _configurationProvider.GetSetting("SearchServiceName");
var serviceApiKey = await _configurationProvider.GetSetting("SearchServiceApiKey");
var indexName = await _configurationProvider.GetSetting("SearchServiceIndex");
SearchIndexClient indexClient = new SearchIndexClient
(serviceName, indexName, new SearchCredentials(serviceApiKey));
var parameters = new SearchParameters()
{
Select = new[] { "...{list of fields}..." },
Filter = string.Format("geo.distance(location,
geography'POINT({0} {1})') le {2}", latitude, longitude, kmdistance)
};
var logmessage = await _configurationProvider.GetSetting("SearchLogMessage");
try
{
var results = await indexClient.Documents.SearchAsync<SearchResultModel>
(text, parameters);
log.LogInformation(string.Format(logmessage, text, latitude,
longitude, kmdistance, results.Count.ToString()));
return results;
}
catch (Exception ex)
{
log.LogError(ex.Message);
log.LogError(ex.StackTrace);
throw ex;
}
}
}
The above code seems pretty straight forward and will run just fine to get back my search results. I even built in logic so that if I don’t give it a distance, it will take a default from the configuration store, pretty slick.
And I pretty quickly ran into a problem, and that error was “Host Not found”.
And I racked my brain on this for a while before I discovered the cause. By default, the Azure Search SDK talks to Commercial. Not Azure Government, and after picking through the documentation, I found this. There is a property called DnsSuffix
, which allows you to put in the suffix used for finding the search service. By default, it is “search.windows.net
”. I changed my code to the following:
public class GeoSearchProvider : IGeoSearchProvider
{
IConfigurationProvider _configurationProvider;
public GeoSearchProvider(IConfigurationProvider configurationProvider)
{
_configurationProvider = configurationProvider;
}
public async Task<DocumentSearchResult<SearchResultModel>>
RunSearch(string text, string latitude, string longitude, string kmdistance,
Microsoft.Extensions.Logging.ILogger log)
{
if (String.IsNullOrEmpty(kmdistance))
{
kmdistance = await _configurationProvider.GetSetting("SearchDefaultDistance");
}
var serviceName = await _configurationProvider.GetSetting("SearchServiceName");
var serviceApiKey = await _configurationProvider.GetSetting("SearchServiceApiKey");
var indexName = await _configurationProvider.GetSetting("SearchServiceIndex");
var dnsSuffix = await _configurationProvider.GetSetting("SearchSearchDnsSuffix");
SearchIndexClient indexClient = new SearchIndexClient
(serviceName, indexName, new SearchCredentials(serviceApiKey));
indexClient.SearchDnsSuffix = dnsSuffix;
var parameters = new SearchParameters()
{
Select = new[] { "...{list of fields}..." },
Filter = string.Format("geo.distance
(location, geography'POINT({0} {1})') le {2}", latitude, longitude, kmdistance)
};
var logmessage = await _configurationProvider.GetSetting("SearchLogMessage");
try
{
var results = await indexClient.Documents.SearchAsync<SearchResultModel>
(text, parameters);
log.LogInformation(string.Format(logmessage, text, latitude,
longitude, kmdistance, results.Count.ToString()));
return results;
}
catch (Exception ex)
{
log.LogError(ex.Message);
log.LogError(ex.StackTrace);
throw ex;
}
}
}
And set the “SearchSearchDnsSuffix
” to “search.azure.us
” for government, and it all immediately worked.