Compartir a través de


Tutorial: Creación de la primera aplicación de búsqueda en Azure Cognitive Search mediante el SDK de .NET

En este tutorial se muestra cómo crear una aplicación web que consulta y devuelve resultados de un índice de búsqueda mediante Azure Cognitive Search y Visual Studio.

En este tutorial, aprenderá a:

  • Configuración de un entorno de desarrollo
  • Estructuras de datos del modelo
  • Creación de una página web para recopilar entradas de consulta y mostrar resultados
  • Definición de un método de búsqueda
  • Prueba de la aplicación

También aprenderá lo sencilla que resulta una llamada de búsqueda. Las instrucciones clave del código se encapsulan en las siguientes líneas:

var options = new SearchOptions()
{
    // The Select option specifies fields for the result set
    options.Select.Add("HotelName");
    options.Select.Add("Description");
};

var searchResult = await _searchClient.SearchAsync<Hotel>(model.searchText, options).ConfigureAwait(false);
model.resultList = searchResult.Value.GetResults().ToList();

Solo una llamada consulta el índice de búsqueda y devuelve resultados.

Buscar *grupo*

Información general

En este tutorial se utiliza el índice de ejemplo de hoteles, el cual puedes crear rápidamente en tu propio servicio de búsqueda siguiendo una guía de inicio rápido para la importación de datos. El índice contiene datos ficticios del hotel, disponibles como origen de datos integrado en cada servicio de búsqueda.

La primera lección de este tutorial crea una estructura de consulta básica y una página de búsqueda, que mejorará en las lecciones posteriores para incluir la paginación, las facetas y una experiencia de escritura anticipada.

Puede encontrar una versión finalizada del código en el siguiente proyecto:

Prerrequisitos

Instalación y ejecución del proyecto desde GitHub

Si desea avanzar a una aplicación en funcionamiento, siga los pasos que se indican a continuación para descargar y ejecutar el código terminado.

  1. Busque el ejemplo en GitHub: Creación de la primera aplicación.

  2. En la carpeta raíz, seleccione Código, seguido de Clonar o Descargar ZIP para realizar la copia local privada del proyecto.

  3. Con Visual Studio, vaya a y abra la solución para la página de búsqueda básica ("1-basic-search-page") y seleccione Iniciar sin depurar (o presione F5) para compilar y ejecutar el programa.

  4. Se trata de un índice de hoteles, por lo que escribe algunas palabras que puedes usar para buscar hoteles (por ejemplo, "wifi", "view", "bar", "parking"). Examine los resultados.

    Buscando *wifi*

Los componentes esenciales para búsquedas más sofisticadas se incluyen en esta aplicación. Si no está familiarizado con el desarrollo de búsqueda, puede volver a crear esta aplicación paso a paso para aprender el flujo de trabajo. En las secciones siguientes se muestra cómo hacerlo.

Configuración de un entorno de desarrollo

Para crear este proyecto desde cero y, por tanto, reforzar los conceptos de Azure Cognitive Search, comience con un proyecto de Visual Studio.

  1. En Visual Studio, seleccione Nuevo>proyecto y , a continuación, ASP.NET Core Web App (Model-View-Controller).

    Creación de un proyecto en la nube

  2. Asigne al proyecto un nombre como "FirstSearchApp" y establezca la ubicación. Seleccione Siguiente.

  3. Acepte los valores predeterminados de la plataforma de destino, el tipo de autenticación y HTTPS. Selecciona Crear.

  4. Instale la biblioteca cliente. En Herramientas>Administrador de paquetes NuGet>Administrar paquetes NuGet para la solución..., seleccione Examinar y busque "azure.search.documents". Instale Azure.Search.Documents (versión 11 o posterior), aceptando los contratos de licencia y las dependencias.

    Uso de NuGet para agregar bibliotecas de Azure

En este paso, establezca el punto de conexión y la clave de acceso para conectarse al servicio de búsqueda que proporciona el índice de ejemplo de hoteles.

  1. Abra appsettings.json y reemplace las líneas predeterminadas por la dirección URL del servicio de búsqueda (con el formato https://<service-name>.search.windows.net) y una clave de API de administrador o consulta del servicio de búsqueda. Dado que no es necesario crear o actualizar un índice, puede usar la clave de consulta para este tutorial.

    {
        "SearchServiceUri": "<YOUR-SEARCH-SERVICE-URI>",
        "SearchServiceQueryApiKey": "<YOUR-SEARCH-SERVICE-API-KEY>"
    }
    
  2. En el Explorador de soluciones, seleccione el archivo y, en Propiedades, cambie la opción Copiar al directorio de salida a Copiar si es más reciente.

    Copia de la configuración de la aplicación en la salida

Estructuras de datos del modelo

Los modelos (clases de C#) se usan para comunicar datos entre el cliente (la vista), el servidor (el controlador) y también la nube de Azure mediante la arquitectura de MVC (modelo, vista, controlador). Normalmente, estos modelos reflejan la estructura de los datos a los que se accede.

En este paso, modelará las estructuras de datos del índice de búsqueda, así como la cadena de búsqueda usada en las comunicaciones de vista/controlador. En el índice de hoteles, cada hotel tiene muchas habitaciones y cada hotel tiene una dirección de varias partes. Por completo, la representación completa de un hotel es una estructura de datos jerárquica y anidada. Necesitará tres clases para crear cada componente.

El conjunto de clases Hotel, Address y Room se conoce como tipos complejos, una característica importante de Azure Cognitive Search. Los tipos complejos pueden ser muchos niveles de clases y subclases, y permiten representar estructuras de datos mucho más complejas que usar tipos simples (una clase que solo contiene miembros primitivos).

  1. En el Explorador de soluciones, haga clic con el botón derecho en Modelos>agregar>nuevo elemento.

  2. Seleccione Clase y asigne un nombre al elemento Hotel.cs. Reemplace todo el contenido de Hotel.cs por el código siguiente. Tenga en cuenta que los miembros Address y Room de la clase son campos que son clases por sí mismos, por lo que también necesitarás modelos para ellos.

    using Azure.Search.Documents.Indexes;
    using Azure.Search.Documents.Indexes.Models;
    using Microsoft.Spatial;
    using System;
    using System.Text.Json.Serialization;
    
    namespace FirstAzureSearchApp.Models
    {
        public partial class Hotel
        {
            [SimpleField(IsFilterable = true, IsKey = true)]
            public string HotelId { get; set; }
    
            [SearchableField(IsSortable = true)]
            public string HotelName { get; set; }
    
            [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.EnLucene)]
            public string Description { get; set; }
    
            [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.FrLucene)]
            [JsonPropertyName("Description_fr")]
            public string DescriptionFr { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string Category { get; set; }
    
            [SearchableField(IsFilterable = true, IsFacetable = true)]
            public string[] Tags { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public bool? ParkingIncluded { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public DateTimeOffset? LastRenovationDate { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public double? Rating { get; set; }
    
            public Address Address { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true)]
            public GeographyPoint Location { get; set; }
    
            public Room[] Rooms { get; set; }
        }
    }
    
  3. Repita el mismo proceso de creación de un modelo para la clase Address y asigne un nombre al archivo Address.cs. Reemplace el contenido por lo siguiente.

    using Azure.Search.Documents.Indexes;
    
    namespace FirstAzureSearchApp.Models
    {
        public partial class Address
        {
            [SearchableField]
            public string StreetAddress { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string City { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string StateProvince { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string PostalCode { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string Country { get; set; }
        }
    }
    
  4. Y de nuevo, siga el mismo proceso para crear la clase Room y asigne un nombre al archivo Room.cs.

    using Azure.Search.Documents.Indexes;
    using Azure.Search.Documents.Indexes.Models;
    using System.Text.Json.Serialization;
    
    namespace FirstAzureSearchApp.Models
    {
        public partial class Room
        {
            [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.EnMicrosoft)]
            public string Description { get; set; }
    
            [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.FrMicrosoft)]
            [JsonPropertyName("Description_fr")]
            public string DescriptionFr { get; set; }
    
            [SearchableField(IsFilterable = true, IsFacetable = true)]
            public string Type { get; set; }
    
            [SimpleField(IsFilterable = true, IsFacetable = true)]
            public double? BaseRate { get; set; }
    
            [SearchableField(IsFilterable = true, IsFacetable = true)]
            public string BedOptions { get; set; }
    
            [SimpleField(IsFilterable = true, IsFacetable = true)]
            public int SleepsCount { get; set; }
    
            [SimpleField(IsFilterable = true, IsFacetable = true)]
            public bool? SmokingAllowed { get; set; }
    
            [SearchableField(IsFilterable = true, IsFacetable = true)]
            public string[] Tags { get; set; }
        }
    }
    
  5. El último modelo que creará en este tutorial es una clase denominada SearchData y representa la entrada del usuario (searchText) y la salida de la búsqueda (resultList). El tipo de salida es crítico, SearchResults<Hotel>, ya que este tipo coincide exactamente con los resultados de la búsqueda y es necesario trasladar esta referencia a la vista. Reemplace la plantilla predeterminada por el código siguiente.

    using Azure.Search.Documents.Models;
    
    namespace FirstAzureSearchApp.Models
    {
        public class SearchData
        {
            // The text to search for.
            public string searchText { get; set; }
    
            // The list of results.
            public SearchResults<Hotel> resultList;
        }
    }
    

Crear una página web

Las plantillas de proyecto incluyen una serie de vistas de cliente ubicadas en la carpeta Vistas . Las vistas exactas dependen de la versión de Core .NET que usa (la versión 3.1 se usa en este ejemplo). En este tutorial, modificará Index.cshtml para incluir los elementos de una página de búsqueda.

Elimine el contenido de Index.cshtml en su totalidad y vuelva a generar el archivo en los pasos siguientes.

  1. En el tutorial se usan dos imágenes pequeñas en la vista: un logotipo de Azure y un icono de lupa de búsqueda (azure-logo.png y search.png). Copie las imágenes del proyecto de GitHub en la carpeta wwwroot/images del proyecto.

  2. La primera línea de Index.cshtml debe hacer referencia al modelo usado para comunicar datos entre el cliente (la vista) y el servidor (el controlador), que es el modelo SearchData creado anteriormente. Agregue esta línea al archivo Index.cshtml.

    @model FirstAzureSearchApp.Models.SearchData
    
  3. Es una práctica estándar escribir un título para la vista, por lo que las líneas siguientes deben ser:

    @{
        ViewData["Title"] = "Home Page";
    }
    
  4. Después del título, escriba una referencia a una hoja de estilos HTML, que creará en breve.

    <head>
        <link rel="stylesheet" href="~/css/hotels.css" />
    </head>
    
  5. El cuerpo de la vista controla dos casos de uso. En primer lugar, debe proporcionar una página vacía en el primer uso, antes de que se escriba cualquier texto de búsqueda. En segundo lugar, debe controlar los resultados, además del cuadro de texto de búsqueda, para las consultas repetidas.

    Para controlar ambos casos, debe comprobar si el modelo proporcionado a la vista es NULL. Un modelo NULL indica el primer caso de uso (la ejecución inicial de la aplicación). Agregue lo siguiente al archivo Index.cshtml y lea los comentarios.

    <body>
    <h1 class="sampleTitle">
        <img src="~/images/azure-logo.png" width="80" />
        Hotels Search
    </h1>
    
    @using (Html.BeginForm("Index", "Home", FormMethod.Post))
    {
        // Display the search text box, with the search icon to the right of it.
        <div class="searchBoxForm">
            @Html.TextBoxFor(m => m.searchText, new { @class = "searchBox" }) <input class="searchBoxSubmit" type="submit" value="">
        </div>
    
        @if (Model != null)
        {
            // Show the result count.
            <p class="sampleText">
                @Model.resultList.TotalCount Results
            </p>
    
            var results = Model.resultList.GetResults().ToList();
    
            @for (var i = 0; i < results.Count; i++)
            {
                // Display the hotel name and description.
                @Html.TextAreaFor(m => results[i].Document.HotelName, new { @class = "box1" })
                @Html.TextArea($"desc{i}", results[i].Document.Description, new { @class = "box2" })
            }
        }
    }
    </body>
    
  6. Agregue la hoja de estilos. En Visual Studio, en Archivo>nuevo>archivo, seleccione Hoja de estilos (con General resaltado).

    Reemplace el código predeterminado por lo siguiente. No entraremos en este archivo con más detalle, los estilos son HTML estándar.

    textarea.box1 {
        width: 648px;
        height: 30px;
        border: none;
        background-color: azure;
        font-size: 14pt;
        color: blue;
        padding-left: 5px;
    }
    
    textarea.box2 {
        width: 648px;
        height: 100px;
        border: none;
        background-color: azure;
        font-size: 12pt;
        padding-left: 5px;
        margin-bottom: 24px;
    }
    
    .sampleTitle {
        font: 32px/normal 'Segoe UI Light',Arial,Helvetica,Sans-Serif;
        margin: 20px 0;
        font-size: 32px;
        text-align: left;
    }
    
    .sampleText {
        font: 16px/bold 'Segoe UI Light',Arial,Helvetica,Sans-Serif;
        margin: 20px 0;
        font-size: 14px;
        text-align: left;
        height: 30px;
    }
    
    .searchBoxForm {
        width: 648px;
        box-shadow: 0 0 0 1px rgba(0,0,0,.1), 0 2px 4px 0 rgba(0,0,0,.16);
        background-color: #fff;
        display: inline-block;
        border-collapse: collapse;
        border-spacing: 0;
        list-style: none;
        color: #666;
    }
    
    .searchBox {
        width: 568px;
        font-size: 16px;
        margin: 5px 0 1px 20px;
        padding: 0 10px 0 0;
        border: 0;
        max-height: 30px;
        outline: none;
        box-sizing: content-box;
        height: 35px;
        vertical-align: top;
    }
    
    .searchBoxSubmit {
        background-color: #fff;
        border-color: #fff;
        background-image: url(/images/search.png);
        background-repeat: no-repeat;
        height: 20px;
        width: 20px;
        text-indent: -99em;
        border-width: 0;
        border-style: solid;
        margin: 10px;
        outline: 0;
    }
    
  7. Guarde el archivo de hoja de estilos como hotels.css, en la carpeta wwwroot/css , junto con el archivo site.css predeterminado.

Esto completa nuestra presentación. En este momento, se han completado los modelos y las vistas. Solo queda el controlador para vincularlo todo.

Definición de métodos

En este paso, modifique el contenido del controlador principal.

  1. Abra el archivo HomeController.cs y reemplace las instrucciones using por lo siguiente.

    using Azure;
    using Azure.Search.Documents;
    using Azure.Search.Documents.Indexes;
    using FirstAzureSearchApp.Models;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Configuration;
    using System;
    using System.Diagnostics;
    using System.Linq;
    using System.Threading.Tasks;
    

Adición de métodos index

En una aplicación MVC, el método Index() es un método de acción predeterminado para cualquier controlador. Abre la página HTML del índice. El método predeterminado, que no toma parámetros, se usa en este tutorial para el caso de uso de inicio de la aplicación: representación de una página de búsqueda vacía.

En esta sección, ampliamos el método para admitir un segundo caso de uso: representar la página cuando un usuario ha escrito texto de búsqueda. Para admitir este caso, el método de índice se extiende para tomar un modelo como parámetro.

  1. Agregue el método siguiente después del método Index() predeterminado.

        [HttpPost]
        public async Task<ActionResult> Index(SearchData model)
        {
            try
            {
                // Ensure the search string is valid.
                if (model.searchText == null)
                {
                    model.searchText = "";
                }
    
                // Make the Azure Cognitive Search call.
                await RunQueryAsync(model);
            }
    
            catch
            {
                return View("Error", new ErrorViewModel { RequestId = "1" });
            }
            return View(model);
        }
    

    Observe la declaración asincrónica del método y la llamada await a RunQueryAsync. Estas palabras clave se encargan de realizar llamadas asincrónicas y, por tanto, evitan el bloqueo de subprocesos en el servidor.

    El bloque catch usa el modelo de error predeterminado que se creó.

Tenga en cuenta el control de errores y otras vistas y métodos predeterminados

En función de la versión de .NET Core que use, se crea un conjunto ligeramente diferente de vistas predeterminadas. Para .NET Core 3.1, las vistas predeterminadas son Index, Privacy y Error. Puede ver estas páginas predeterminadas al ejecutar la aplicación y examinar cómo se controlan en el controlador.

Probará la vista Error más adelante en este tutorial.

En el ejemplo de GitHub, se eliminan las vistas sin usar y sus acciones asociadas.

Adición del método RunQueryAsync

La llamada a Azure Cognitive Search se encapsula en nuestro método RunQueryAsync .

  1. En primer lugar, agregue algunas variables estáticas para configurar el servicio de Azure y una llamada para iniciarlas.

        private static SearchClient _searchClient;
        private static SearchIndexClient _indexClient;
        private static IConfigurationBuilder _builder;
        private static IConfigurationRoot _configuration;
    
        private void InitSearch()
        {
            // Create a configuration using appsettings.json
            _builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
            _configuration = _builder.Build();
    
            // Read the values from appsettings.json
            string searchServiceUri = _configuration["SearchServiceUri"];
            string queryApiKey = _configuration["SearchServiceQueryApiKey"];
    
            // Create a service and index client.
            _indexClient = new SearchIndexClient(new Uri(searchServiceUri), new AzureKeyCredential(queryApiKey));
            _searchClient = _indexClient.GetSearchClient("hotels");
        }
    
  2. Ahora, agregue el propio método RunQueryAsync .

    private async Task<ActionResult> RunQueryAsync(SearchData model)
    {
        InitSearch();
    
        var options = new SearchOptions() 
        { 
            IncludeTotalCount = true
        };
    
        // Enter Hotel property names into this list so only these values will be returned.
        // If Select is empty, all values will be returned, which can be inefficient.
        options.Select.Add("HotelName");
        options.Select.Add("Description");
    
        // For efficiency, the search call should be asynchronous, so use SearchAsync rather than Search.
        model.resultList = await _searchClient.SearchAsync<Hotel>(model.searchText, options).ConfigureAwait(false);          
    
        // Display the results.
        return View("Index", model);
    }
    

    En este método, primero asegúrese de que se inicia la configuración de Azure y, a continuación, establezca algunas opciones de búsqueda. La opción Seleccionar especifica los campos que se van a devolver en los resultados y, por tanto, coinciden con los nombres de propiedad de la clase de hotel . Si omite Select, se devuelven todos los campos no ocultos, que pueden ser ineficaces si solo está interesado en un subconjunto de todos los campos posibles.

    La llamada asincrónica a la búsqueda formula la solicitud (modelada como searchText) y la respuesta (modelada como searchResult). Si está depurando este código, la clase SearchResult es una buena candidata para establecer un punto de interrupción si necesita examinar el contenido de model.resultList. Debería encontrar que es intuitivo, proporcionando solo los datos que solicitó y no mucho más.

Prueba de la aplicación

Ahora, vamos a comprobar si la aplicación se ejecuta correctamente.

  1. Seleccione Depurar>Iniciar sin depurar o presione F5. Si la aplicación se ejecuta según lo previsto, debería obtener la vista índice inicial.

    Apertura de la aplicación

  2. Escriba una cadena de consulta como "beach" (o cualquier texto que llegue a la mente) y haga clic en el icono de búsqueda para enviar la solicitud.

    Buscar *playa*

  3. Intente escribir "cinco estrellas". Observe que esta consulta no devuelve ningún resultado. Una búsqueda más sofisticada trataría "cinco estrellas" como sinónimo de "lujo" y devolver esos resultados. La compatibilidad con sinónimos está disponible en Azure Cognitive Search, pero no se trata en esta serie de tutoriales.

  4. Intente escribir "frecuente" como texto de búsqueda. No devuelve entradas con la palabra "hotel" en ellas. Nuestra búsqueda solo busca palabras completas, aunque se devuelven algunos resultados.

  5. Pruebe otras palabras: "pool", "sunshine", "view" y lo que sea. Verá que Azure Cognitive Search funciona en su nivel más sencillo, pero sigue siendo convincente.

Probar condiciones y errores perimetrales

Es importante comprobar que nuestras características de control de errores funcionan como deberían, incluso cuando las cosas funcionan perfectamente.

  1. En el método Index , después de la llamada try { , escriba la línea Throw new Exception(). Esta excepción forzará un error al buscar texto.

  2. Ejecute la aplicación, escriba "barra" como texto de búsqueda y haga clic en el icono de búsqueda. La excepción debería resultar en la vista de error.

    Forzar un error

    Importante

    Se considera un riesgo de seguridad devolver números de error internos en páginas de error. Si la aplicación está pensada para uso general, siga los procedimientos recomendados de seguridad de qué devolver cuando se produzca un error.

  3. Quite Throw new Exception() when you're satisfied satisfied the error handling works as it should.

Conclusiones

Tenga en cuenta los siguientes aspectos de este proyecto:

  • Una llamada a Azure Cognitive Search es concisa y es fácil interpretar los resultados.
  • Las llamadas asincrónicas agregan una pequeña cantidad de complejidad al controlador, pero son un procedimiento recomendado que mejora el rendimiento.
  • Esta aplicación realizó una búsqueda de texto sencilla, definida por lo que se establece en searchOptions. Sin embargo, esta clase se puede rellenar con muchos miembros que agregan sofisticación a una búsqueda. Con un poco más de trabajo, puedes hacer que esta aplicación sea considerablemente más eficaz.

Pasos siguientes

Para mejorar la experiencia del usuario, agregue más funcionalidades, en particular la paginación (ya sea mediante números de página o desplazamiento infinito) y autocompletar y sugerencias. También puede considerar otras opciones de búsqueda (por ejemplo, búsquedas geográficas en hoteles dentro de un radio especificado de un punto determinado) y ordenar los resultados de búsqueda.

Estos pasos siguientes se abordan en los tutoriales restantes. Comencemos con la paginación.