Búsqueda de datos con Azure Search y Xamarin.Forms

Azure Search es un servicio en la nube que proporciona funcionalidades de indexación y consulta de los datos cargados. Esto elimina los requisitos de infraestructura y las complejidades del algoritmo de búsqueda tradicionalmente asociadas a la implementación de la funcionalidad de búsqueda en una aplicación. En este artículo se muestra cómo usar la biblioteca de Microsoft Azure Search para integrar Azure Search en una aplicación de Xamarin.Forms.

Información general

Los datos se almacenan en Azure Search como índices y documentos. Un índice es un almacén de datos en el que el servicio Azure Search puede buscar y que es conceptualmente similar a una tabla de base de datos. Un documento es una sola unidad de datos que se pueden buscar en un índice y es conceptualmente similar a una fila de base de datos. Al cargar documentos y enviar consultas de búsqueda a Azure Search, las solicitudes se realizan a un índice específico en el servicio de búsqueda.

Cada solicitud realizada a Azure Search debe incluir el nombre del servicio y una clave de API. Hay dos tipos de clave de API:

  • Las claves de administrador conceden derechos completos a todas las operaciones. Esto incluye la administración del servicio, la creación y eliminación de índices, y los orígenes de datos.
  • Las claves de consulta conceden acceso de solo lectura a índices y documentos, y las deben usar las aplicaciones que emiten solicitudes de búsqueda.

La solicitud más común a Azure Search es la ejecución de una consulta. Hay dos tipos de consulta que se pueden enviar:

Las consultas de búsqueda y las consultas de filtro se pueden usar por separado o en conjunto. Cuando se usan en conjunto, la consulta de filtro se aplica primero a todo el índice y, a continuación, se realiza la consulta de búsqueda en los resultados de la consulta de filtro.

Azure Search también admite la recuperación de sugerencias en función de la entrada de búsqueda. Para obtener más información, consulte Consultas de sugerencias.

Nota:

Si no tiene una suscripción a Azure, cree una cuenta gratuita antes de empezar.

Configuración

El proceso para integrar Azure Search en una aplicación Xamarin.Forms es el siguiente:

  1. Cree un servicio Azure Search. Para obtener más información, consulte Creación de un servicio Azure Search mediante Azure Portal.
  2. Quite Silverlight como marco de destino de la Biblioteca de clases portable (PCL) de la solución Xamarin.Forms. Esto se puede lograr cambiando el perfil de PCL a cualquier perfil que admita el desarrollo multiplataforma, pero no admite Silverlight, como el perfil 151 o el perfil 92.
  3. Agregue el paquete NuGet de la Biblioteca de Microsoft Azure Search al proyecto PCL de la solución Xamarin.Forms.

Después de realizar estos pasos, la API de biblioteca de Microsoft Search se puede usar para administrar índices de búsqueda y orígenes de datos, cargar y administrar documentos, y ejecutar consultas.

Creación de un índice de Azure Search

Se debe definir un esquema de índice que se asigne a la estructura de los datos que se van a buscar. Esto se puede lograr en Azure Portal o mediante programación mediante la clase SearchServiceClient. Esta clase administra las conexiones a Azure Search y se puede usar para crear un índice. En el siguiente ejemplo de código se muestra cómo crear una instancia de esta clase:

var searchClient =
  new SearchServiceClient(Constants.SearchServiceName, new SearchCredentials(Constants.AdminApiKey));

La sobrecarga del constructor SearchServiceClient toma un nombre del servicio de búsqueda y un objeto SearchCredentials como argumentos, con el objeto SearchCredentials que encapsula la clave de administrador para el servicio Azure Search. La clave de administrador es necesaria para crear un índice.

Nota:

Se debe usar una sola instancia SearchServiceClient en una aplicación para evitar abrir demasiadas conexiones a Azure Search.

El objeto Index define un índice, como se muestra en el ejemplo de código siguiente:

static void CreateSearchIndex()
{
  var index = new Index()
  {
    Name = Constants.Index,
    Fields = new[]
    {
      new Field("id", DataType.String) { IsKey = true, IsRetrievable = true },
      new Field("name", DataType.String) { IsRetrievable = true, IsFilterable = true, IsSortable = true, IsSearchable = true },
      new Field("location", DataType.String) { IsRetrievable = true, IsFilterable = true, IsSortable = true, IsSearchable = true },
      new Field("details", DataType.String) { IsRetrievable = true, IsFilterable = true, IsSearchable = true },
      new Field("imageUrl", DataType.String) { IsRetrievable = true }
    },
    Suggesters = new[]
    {
      new Suggester("nameSuggester", SuggesterSearchMode.AnalyzingInfixMatching, new[] { "name" })
    }
  };

  searchClient.Indexes.Create(index);
}

La propiedad Index.Name debe establecerse en el nombre del índice y la propiedad Index.Fields debe establecerse en una matriz de objetos Field. Cada instancia Field especifica un nombre, un tipo y todas las propiedades, que especifican cómo se usa el campo. Estas propiedades incluyen:

  • IsKey: indica si el campo es la clave del índice. Solo se debe designar un campo en el índice, de tipo DataType.String, como campo clave.
  • IsFacetable: indica si es posible realizar la navegación por facetas en este campo. El valor predeterminado es false.
  • IsFilterable: indica si el campo se puede usar en las consultas de filtro. El valor predeterminado es false.
  • IsRetrievable: indica si el campo se puede recuperar en los resultados de la búsqueda. El valor predeterminado es true.
  • IsSearchable: indica si el campo está incluido en las búsquedas de texto completo. El valor predeterminado es false.
  • IsSortable: indica si el campo se puede usar en expresiones OrderBy. El valor predeterminado es false.

Nota:

Cambiar un índice después de implementarlo implica volver a generar y volver a cargar los datos.

Un objeto Index puede especificar de manera opcional una propiedad Suggesters, que define los campos del índice que se usarán para admitir consultas de sugerencias de búsqueda o de autocompletar. La propiedad Suggesters debe establecerse en una matriz de objetos Suggester que definen los campos que se usan para generar los resultados de la sugerencia de búsqueda.

Después de crear el objeto Index, el índice se crea llamando a Indexes.Create en la instancia SearchServiceClient.

Nota:

Al crear un índice a partir de una aplicación que debe mantenerse con capacidad de respuesta, use el método Indexes.CreateAsync.

Para obtener más información, consulte Creación de un índice de Azure Search mediante el SDK de .NET.

Eliminación del índice de Azure Search

Un índice se puede eliminar mediante una llamada a Indexes.Delete en la instancia SearchServiceClient:

searchClient.Indexes.Delete(Constants.Index);

Carga de datos en el índice de Azure Search

Después de definir el índice, los datos se pueden cargar en él mediante uno de estos dos modelos:

  • Modelo de extracción: los datos se ingieren periódicamente desde Azure Cosmos DB, Azure SQL Database, Azure Blob Storage o SQL Server hospedados en una máquina virtual de Azure.
  • Modelo de inserción: los datos se envían mediante programación al índice. Este es el modelo adoptado en este artículo.

Se debe crear una instancia SearchIndexClient para importar datos en el índice. Esto se puede lograr llamando al método SearchServiceClient.Indexes.GetClient, como se muestra en el ejemplo de código siguiente:

static void UploadDataToSearchIndex()
{
  var indexClient = searchClient.Indexes.GetClient(Constants.Index);

  var monkeyList = MonkeyData.Monkeys.Select(m => new
  {
    id = Guid.NewGuid().ToString(),
    name = m.Name,
    location = m.Location,
    details = m.Details,
    imageUrl = m.ImageUrl
  });

  var batch = IndexBatch.New(monkeyList.Select(IndexAction.Upload));
  try
  {
    indexClient.Documents.Index(batch);
  }
  catch (IndexBatchException ex)
  {
    // Sometimes when the Search service is under load, indexing will fail for some
    // documents in the batch. Compensating actions like delaying and retrying should be taken.
    // Here, the failed document keys are logged.
    Console.WriteLine("Failed to index some documents: {0}",
      string.Join(", ", ex.IndexingResults.Where(r => !r.Succeeded).Select(r => r.Key)));
  }
}

Los datos que se van a importar en el índice se empaquetan como un objeto IndexBatch, que encapsula una colección de objetos IndexAction. Cada instancia IndexAction contiene un documento y una propiedad que indica a Azure Search qué acción realizar en el documento. En el ejemplo de código anterior, se especifica la acción IndexAction.Upload, lo que hace que el documento se inserte en el índice si es nuevo o se reemplaza si ya existe. A continuación, el objeto IndexBatch se envía al índice llamando al método Documents.Index en el objeto SearchIndexClient. Para obtener información sobre otras acciones de indexación, consulte Decidir qué acción de indexación se va a usar.

Nota:

Solo se pueden incluir 1000 documentos en una sola solicitud de indexación.

Tenga en cuenta que en el ejemplo de código anterior, la colección monkeyList se crea como un objeto anónimo a partir de una colección de objetos Monkey. Esto crea datos para el campo id y resuelve la asignación de nombres de propiedad Monkey de mayúsculas y minúsculas Pascal a nombres de campo de índice de búsqueda de mayúsculas y minúsculas camel. Como alternativa, esta asignación también se puede realizar agregando el atributo [SerializePropertyNamesAsCamelCase] a la clase Monkey.

Para obtener más información, consulte Carga de datos en Azure Search mediante el SDK de .NET.

Consulta del índice de Azure Search

Se debe crear una instancia SearchIndexClient para consultar un índice. Cuando una aplicación ejecuta consultas, es aconsejable seguir el principio de privilegios mínimos y crear un elemento SearchIndexClient directamente, pasando la clave de consulta como argumento. Esto garantiza que los usuarios tengan acceso de solo lectura a los índices y documentos. Este enfoque se muestra en el ejemplo de código siguiente:

SearchIndexClient indexClient =
  new SearchIndexClient(Constants.SearchServiceName, Constants.Index, new SearchCredentials(Constants.QueryApiKey));

La sobrecarga del constructor SearchIndexClient toma un nombre de servicio de búsqueda, un nombre de índice y un objeto SearchCredentials como argumentos, con el objeto SearchCredentials que encapsula la clave de consulta para el servicio Azure Search.

Consultas de búsqueda

El índice se puede consultar llamando al método Documents.SearchAsync en la instancia SearchIndexClient, como se muestra en el ejemplo de código siguiente:

async Task AzureSearch(string text)
{
  Monkeys.Clear();

  var searchResults = await indexClient.Documents.SearchAsync<Monkey>(text);
  foreach (SearchResult<Monkey> result in searchResults.Results)
  {
    Monkeys.Add(new Monkey
    {
      Name = result.Document.Name,
      Location = result.Document.Location,
      Details = result.Document.Details,
      ImageUrl = result.Document.ImageUrl
    });
  }
}

El método SearchAsync toma un argumento de texto de búsqueda y un objeto SearchParameters opcional que se puede usar para refinar aún más la consulta. Se especifica una consulta de búsqueda como argumento de texto de búsqueda, mientras que se puede especificar una consulta de filtro estableciendo la propiedad Filter del argumento SearchParameters. En el ejemplo de código siguiente se muestran ambos tipos de consulta:

var parameters = new SearchParameters
{
  Filter = "location ne 'China' and location ne 'Vietnam'"
};
var searchResults = await indexClient.Documents.SearchAsync<Monkey>(text, parameters);

Esta consulta de filtro se aplica a todo el índice y quita los documentos de los resultados en los que el campo location no es igual a China y no es igual a Vietnam. Después del filtrado, la consulta de búsqueda se realiza en los resultados de la consulta de filtro.

Nota:

Para filtrar sin buscar, pase * como argumento de texto de búsqueda.

El método SearchAsync devuelve un objeto DocumentSearchResult que contiene los resultados de la consulta. Este objeto se enumera, donde cada objeto Document se crea como un objeto Monkey y se agrega a MonkeysObservableCollection para la visualización. En las capturas de pantalla siguientes se muestran los resultados de la consulta de búsqueda devueltos desde Azure Search:

Resultado de la búsqueda

Para obtener más información sobre la búsqueda y el filtrado, consulte Consulta del índice de Azure Search mediante el SDK de .NET.

Consultas de sugerencias

Azure Search permite solicitar sugerencias en función de una consulta de búsqueda mediante una llamada al método Documents.SuggestAsync en la instancia SearchIndexClient. Esto se muestra en el ejemplo de código siguiente:

async Task AzureSuggestions(string text)
{
  Suggestions.Clear();

  var parameters = new SuggestParameters()
  {
    UseFuzzyMatching = true,
    HighlightPreTag = "[",
    HighlightPostTag = "]",
    MinimumCoverage = 100,
    Top = 10
  };

  var suggestionResults =
    await indexClient.Documents.SuggestAsync<Monkey>(text, "nameSuggester", parameters);

  foreach (var result in suggestionResults.Results)
  {
    Suggestions.Add(new Monkey
    {
      Name = result.Text,
      Location = result.Document.Location,
      Details = result.Document.Details,
      ImageUrl = result.Document.ImageUrl
    });
  }
}

El método SuggestAsync toma un argumento de texto de búsqueda, el nombre del proveedor de sugerencias que se va a usar (que se define en el índice) y un objeto SuggestParameters opcional que se puede usar para refinar aún más la consulta. La instancia SuggestParameters establece las siguientes propiedades:

  • UseFuzzyMatching: cuando se establece en true, Azure Search encontrará sugerencias aunque haya un carácter sustituido o que falte en el texto de búsqueda.
  • HighlightPreTag: la etiqueta que se antepone a los aciertos de la sugerencia.
  • HighlightPostTag: la etiqueta que se anexa a los aciertos de la sugerencia.
  • MinimumCoverage: representa el porcentaje del índice que debe estar cubierto por una consulta de sugerencia para que la consulta se notifique como correcta. El valor predeterminado es 80.
  • Top: número de sugerencias que se van a recuperar. Debe ser un entero entre 1 y 100, con un valor predeterminado de 5.

El efecto general es que los 10 primeros resultados del índice se devolverán con un resaltado de aciertos y los resultados incluirán documentos que incluyan términos de búsqueda con un deletreado similar.

El método SuggestAsync devuelve un objeto DocumentSuggestResult que contiene los resultados de la consulta. Este objeto se enumera, donde cada objeto Document se crea como un objeto Monkey y se agrega a MonkeysObservableCollection para la visualización. En las capturas de pantalla siguientes se muestran los resultados de la sugerencia devueltos desde Azure Search:

Resultados de sugerencias

Tenga en cuenta que en la aplicación de ejemplo, el método SuggestAsync solo se invoca cuando el usuario termina de escribir un término de búsqueda. Sin embargo, también se puede usar para admitir consultas de búsqueda de autocompletar ejecutando en cada evento keypress.

Resumen

En este artículo se muestra cómo usar la biblioteca de Microsoft Azure Search para integrar Azure Search en una aplicación Xamarin.Forms. Azure Search es un servicio en la nube que proporciona funcionalidades de indexación y consulta para los datos cargados. Esto elimina los requisitos de infraestructura y las complejidades del algoritmo de búsqueda tradicionalmente asociadas a la implementación de la funcionalidad de búsqueda en una aplicación.