En el artículo anterior estuvimos viendo qué es Azure Search y cómo podemos configurarlo para comenzar a utilizarlo. En este artículo veremos cómo integrarlo en una Aplicación Web por medio del SDK que nos brindan.
En esta entrega estaremos integrando Azure Search en una Aplicación Web. Veremos sus características y funcionalidades plasmadas en un WebSite. El código del proyecto podrán descargo desde GitHub desde esta dirección
https://github.com/withoutdebugger/DemoAzureSearchWODv1
Este documento está dirigido a personas que conocen muy poco o nada sobre el tema o personas que desarrollan tareas de consultoría de desarrollo o que simplemente están interesados en leer e investigar sobre la tecnología alcanzada por esta publicación.
Antes que nada, necesitaremos el SDK. El SDK posee un conjunto de librerías de cliente las cuales las utilizaremos para manejar los índices, los datasource, indexadores, subir documentos, ejecutar queries y todo lo que necesitemos.
Las librerías principales son:
- Microsoft.Azure.Search
- Microsoft.Search.Models
Estas librerías tienen definidas algunas clases principales como Index, Field y Document como también algunas operaciones como Indexes.Create o Documents.Search asociadas a SearchServiceClient y SerachIndeCliente. Están agrupadas de una forma lógica para una mejor compresión.
Una vez listo el SDK en nuestra aplicación necesitaremos los ApiKey que Azure. Esta llave nos dará acceso al servicio y a base de búsqueda. La guardaremos en nuestra configuración de connections strings.
Para obtenerlos debemos ingresar al portal de Azure. Seleccionamos nuestro servicio de búsqueda -> opción claves:
Estas claves la usaremos dentro de nuestro archivo appsettings.json para tenerlas disponibles y consultarlas en el momento de consumir el servicio.
{
"AzureSearch": {
"SearchServiceName": "{nombre del servicio}",
"SearchServiceAdminApiKey": "{apikey}",
"SearchServiceQueryApiKey": "{apikey} "
},
...
}
Nuestra aplicación es una aplicacion ASP.Net Core MVC junto a una Web APi que será consumida por medio de JQuery. No voy a entrar en detalle de la aplicación MVC pero si en el servicio de búsqueda.
Tendremos el Home controller que solamente nos devolverá la página inicial donde se encontrarán nuestros métodos en JQuery que consumirán los servicios de búsqueda.
Crearemos una Web API que se llamara Search. Será un controller llamado SearchController. Este recibirá en su constructor la clase IConfiguration para tener disponible el servicio de Azure Search.
public SearchController(IConfiguration _configuration)
{
searchServiceName = _configuration["AzureSearch:SearchServiceName"];
queryApiKey = _configuration["AzureSearch:SearchServiceQueryApiKey"];
indexClient = new SearchIndexClient(searchServiceName, "realestate-us-sample", new SearchCredentials(queryApiKey));
}
Nuestro método principal Get será el encargado de recibir las consultas desde el cliente. En esta primera versión filtra por texto, facets, tag y manejara la paginación. También tendremos algunos métodos privados que nos ayudaran a armar la consulta final.
Modelo Hotel
Este modelo es en el cual se transformará el resultado de Azure Search en nuestra aplicación.
[SerializePropertyNamesAsCamelCase]
public partial class Hotel
{
[System.ComponentModel.DataAnnotations.Key]
public string listingId { get; set; }
public string description { get; set; }
public string description_es { get; set; }
public string number { get; set; }
public string street { get; set; }
public string city { get; set; }
public string countryCode { get; set; }
public string postCode { get; set; }
public string thumbnail { get; set; }
public string status { get; set; }
public string[] tags { get; set; }
}
Get Method
El método principal recibirá los parámetros de consulta. Dentro podemos ver que tenemos los métodos privados CreateFilterFacets, CreateFilterTags, CreateParameter.
[HttpGet]
public DocumentSearchResult<Hotel> Get(string searchtext = "", string cityFacet = "", string typeFacet = "", string tags = "",
string orderby = "", string orderbydirection = "desc", int actualPage = 1)
{
//Filter
string filter = CreateFilter(cityFacet, typeFacet);
//Filter Tags
IList<ScoringParameter> filterTags = CreatedFilterTags(tags);
//Parameters
SearchParameters sp = CreateParameter(10, filter, orderBy, filterTags, actualPage);
//Search
var result = indexClient.Documents.Search<Hotel>(searchtext, sp);
return result;
}
CreateFilterFacets
Este método es el encargado de crear los filtros para los facets de City y Type. Como podemos ver en el método, el resultado un string que acompañará a la consulta. Azure Index utiliza una estructura OData formar las consultas. Por ejemplo, en el string el comando podemos ver el comando eq que significa igual. Podemos ver la lista completa de comando desde este link:
https://docs.microsoft.com/en-us/rest/api/searchservice/odata-expression-syntax-for-azure-search
private string CreateFilterFacets(string cityFacet, string typeFacet) {
string filter = "";
if (cityFacet != null)
{
filter += filter != string.Empty ? " and " : "";
filter += "city eq '" + cityFacet + "'";
}
if (typeFacet != null)
{
filter += filter != string.Empty ? " and " : "";
filter += "type eq '" + typeFacet + "'";
}
return filter;
}
CreateFilterTags
Este método es el encargado de crear una lista de Tags para filtrar. A diferencia del anterior, los tags deben filtrarse por medio de ScoringParameter que hace referencia a un nivel de acierto.
private IList<ScoringParameter> CreateFilterTags(string tags) {
IList<ScoringParameter> _parameterTags = new List<ScoringParameter>();
if (tags == null) { return _parameterTags; }
string[] _listTags = tags.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
if (_listTags.Count() > 0)
{
_parameterTags.Add(new ScoringParameter("tags", _listTags));
}
return _parameterTags;
}
CreateParameter
Antes de la ejecución final de obtención de resultados debemos armar un SearchParamters. Desde este manejaremos la configuración principal de la consulta como el modo de búsqueda, cuántos tomar por medio de top, el filtro de text, el cálculo para la página, los campos que usaremos, si incluimos el total y los facets que filtraremos. Por último, también se agregará tags correspondientes al ScoringParameters.
private SearchParameters CreateParameter(int top, string filter, IList<string> orderby, IList<ScoringParameter> filterTag, int currentPage)
{
SearchParameters searchParameters =
new SearchParameters() {
SearchMode = SearchMode.Any,
Top = 10,
Filter = filter,
Skip = currentPage - 1,
Select = new List<String>() { "listingId", "description", "description_es", "number", "street", "city", "countryCode", "postCode", "thumbnail", "status", "tags" },
// Add count
IncludeTotalResultCount = true,
//// Add facets
Facets = new List<String>() { "city", "type", "status", "tags" },
};
searchParameters.OrderBy = orderby;
searchParameters.ScoringParameters = filterTag;
return searchParameters;
}
Ejecución de resultado
Una vez que tenemos todos los objetos armados para nuestra consulta se llamará al siguiente método perteneciente al cliente de Azure Search.
var result = indexClient.Documents.Search<Hotel>(searchtext, sp);
indexClient es del tipo SearchIndexClient el cual es el encargado de realizar finalmente la consulta en nuestro servicio de Azure Search. Podemos ver que le enviaremos nuestra clase Hotel y él se encargara de hacer la transformación.
La aplicación
Finalmente, nuestra aplicación se verá visualizará de la siguiente forma luego de una búsqueda:
Podemos filtrar la búsqueda por medio de la selección en cada una de las secciones City, Type y Tags para ajustar nuestros resultados. El encargado de ajustar la búsqueda será los métodos creados en JQuery.
Conclusión
Azure Search es una excelente herramienta para realizar potentes búsquedas. También nos brinda un SSK muy potente para que podamos realizar consultas muy rápidamente y poder así integrar rápidamente en nuestras aplicaciones
Les dejos la aplicación de ejemplo en GitHub para que puedan consultarla.
https://github.com/withoutdebugger/DemoAzureSearchWODv1
También les dejo el link del demo: http://wodazuresearch.azurewebsites.net/