Inicio rápido: Búsqueda de texto completo con los SDK de Azure

Aprenda a usar la biblioteca cliente Azure.Search.Documents en un SDK de Azure para crear, cargar y consultar un índice de búsqueda mediante datos de ejemplo para la búsqueda de texto completo. La búsqueda de texto completo usa Apache Lucene para la indexación y las consultas, y un algoritmo de clasificación BM25 para los resultados de puntuación.

Este inicio rápido incluye pasos para los siguientes SDK:

Requisitos previos

  • Una cuenta de Azure con una suscripción activa. También puede crear una cuenta de forma gratuita.

  • Un servicio Azure AI Search. Cree un servicio si no tiene uno. Puede usar el nivel Gratis para este inicio rápido.

  • Una clave de API y un punto de conexión de servicio para el servicio. Inicie sesión en Azure Portal y encuentre su servicio de búsqueda.

    En la sección Información general, copie la dirección URL y guárdela en un editor de texto para un paso posterior. Un punto de conexión de ejemplo podría ser similar a https://mydemo.search.windows.net.

    En la sección Configuración>Claves, copie y guarde una clave de administrador a fin de obtener derechos completos para crear y eliminar objetos. Hay dos claves intercambiables una principal y otra secundaria. Elija una de las dos.

    Recorte de pantalla que muestra el punto de conexión HTTP y las ubicaciones de las claves de API principal y secundaria.

Creación, carga y consulta de un índice

Elija un lenguaje de programación para el paso siguiente. Las bibliotecas cliente Azure.Search.Documents están disponibles en los SDK de Azure para .NET, Python, Java y JavaScript o Typescript.

Cree una aplicación de consola mediante la biblioteca cliente Azure.Search.Documents para crear, cargar y consultar un índice de búsqueda.

Como alternativa, puede descargar el código fuente para empezar con un proyecto terminado o seguir los pasos de este artículo para crear su propio proyecto.

Configurar el entorno

  1. Inicie Visual Studio y cree un nuevo proyecto para una aplicación de consola.

  2. En Herramientas>Administrador de paquetes NuGet seleccione Administrar paquetes NuGet para la solución.... .

  3. Seleccione Examinar.

  4. Busque el paquete de Azure.Search.Documents y seleccione la versión 11.0 o una posterior.

  5. Seleccione Instalar para agregar el ensamblado al proyecto y la solución.

Creación de un cliente de búsqueda

  1. En Program.cs, cambie el espacio de nombres a AzureSearch.SDK.Quickstart.v11 y, a continuación, agregue las siguientes directivas using.

    using Azure;
    using Azure.Search.Documents;
    using Azure.Search.Documents.Indexes;
    using Azure.Search.Documents.Indexes.Models;
    using Azure.Search.Documents.Models;
    
  2. Copie el siguiente código para crear dos clientes. SearchIndexClient crea el índice y SearchClient carga y consulta uno existente. Ambos necesitan el punto de conexión de servicio y una clave de API de administración para la autenticación con derechos de creación y eliminación.

    Dado que el código compila el URI automáticamente, especifique solo el nombre del servicio de búsqueda en la propiedad serviceName.

     static void Main(string[] args)
     {
         string serviceName = "<your-search-service-name>";
         string apiKey = "<your-search-service-admin-api-key>";
         string indexName = "hotels-quickstart";
    
         // Create a SearchIndexClient to send create/delete index commands
         Uri serviceEndpoint = new Uri($"https://{serviceName}.search.windows.net/");
         AzureKeyCredential credential = new AzureKeyCredential(apiKey);
         SearchIndexClient adminClient = new SearchIndexClient(serviceEndpoint, credential);
    
         // Create a SearchClient to load and query documents
         SearchClient srchclient = new SearchClient(serviceEndpoint, indexName, credential);
         . . . 
     }
    

Creación de un índice

En este inicio rápido se crea un índice de hoteles que se cargará con datos de hotel y en el que se ejecutarán consultas. En este paso, defina los campos en el índice. Cada definición de campo incluye un nombre, un tipo de datos y atributos que determinan cómo se usa el campo.

En este ejemplo, se usan métodos sincrónicos de la biblioteca Azure.Search.Documents para simplificar y mejorar la legibilidad. Sin embargo, para los escenarios de producción, debe usar métodos asincrónicos para mantener la aplicación escalable y receptiva. Por ejemplo, usaría CreateIndexAsync en lugar de CreateIndex.

  1. Agregue una definición de clase vacía al proyecto: Hotel.cs

  2. Copie el código siguiente en Hotel.cs para definir la estructura de un documento de hotel. Los atributos del campo determinan cómo se utilizan en una aplicación. Por ejemplo, el atributo IsFilterable se tiene que asignar a cada campo que admita una expresión de filtro.

    using System;
    using System.Text.Json.Serialization;
    using Azure.Search.Documents.Indexes;
    using Azure.Search.Documents.Indexes.Models;
    
    namespace AzureSearch.Quickstart
    {
        public partial class Hotel
        {
            [SimpleField(IsKey = true, IsFilterable = 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; }
    
            [SearchableField]
            public Address Address { get; set; }
        }
    }
    

    En la biblioteca clienteAzure.Search.Documents puede usar SearchableField y SimpleField para optimizar las definiciones de campo. Ambos son derivados de un SearchField y pueden simplificar el código:

    • SimpleField puede ser cualquier tipo de datos, no permite búsquedas (se omite en las consultas de búsqueda de texto completo) y se puede recuperar (no está oculto). Otros atributos están desactivados de forma predeterminada, pero se pueden habilitar. Puede usar un SimpleField para los identificadores de documento o los campos que se usan solo en filtros, facetas o perfiles de puntuación. En caso de hacerlo, asegúrese de aplicar los atributos necesarios para el escenario, como IsKey = true para un identificador de documento. Para más información, consulte SimpleFieldAttribute.cs en el código fuente.

    • SearchableField debe ser una cadena y siempre permite búsquedas y se puede recuperar. Otros atributos están desactivados de forma predeterminada, pero se pueden habilitar. Ya que este tipo de campo se puede buscar, admite sinónimos y el complemento completo de las propiedades del analizador. Para más información, consulte SearchableFieldAttribute.cs en el código fuente.

    Tanto si usa la API básica de SearchField, como uno de los modelos auxiliares, debe habilitar explícitamente los atributos de filtro, faceta y ordenación. Por ejemplo, IsFilterable, IsSortabley IsFacetable se tienen que atribuir explícitamente, como se muestra en el ejemplo anterior.

  3. Agregue una segunda definición de clase vacía al proyecto: Address.cs. Pegue el código siguiente en la clase.

    using Azure.Search.Documents.Indexes;
    
     namespace AzureSearch.Quickstart
     {
         public partial class Address
         {
             [SearchableField(IsFilterable = true)]
             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. Cree dos clases más: Hotel.Methods.cs y Address.Methods.cs para las invalidaciones de ToString(). Estas clases se usan para representar los resultados de búsqueda en la salida de la consola. El contenido de estas clases no se proporciona en este artículo, pero puede copiar el código de los archivos de GitHub.

  5. En Program.cs, cree un objeto SearchIndex y llame al método CreateIndex para expresar el índice en el servicio de búsqueda. El índice también incluye SearchSuggester, que habilita la función autocompletar en los campos especificados.

     // Create hotels-quickstart index
     private static void CreateIndex(string indexName, SearchIndexClient adminClient)
     {
         FieldBuilder fieldBuilder = new FieldBuilder();
         var searchFields = fieldBuilder.Build(typeof(Hotel));
    
         var definition = new SearchIndex(indexName, searchFields);
    
         var suggester = new SearchSuggester("sg", new[] { "HotelName", "Category", "Address/City", "Address/StateProvince" });
         definition.Suggesters.Add(suggester);
    
         adminClient.CreateOrUpdateIndex(definition);
     }
    

Carga de documentos

Azure AI Search busca en el contenido almacenado en el servicio. En este paso, el usuario cargará los documentos JSON que se ajustan al índice de hotel que acaba de crear.

En Azure AI Search, los documentos de búsqueda son estructuras de datos que se pueden usar como entradas para la indexación y como salidas de las consultas. Tal como se obtienen del origen de datos externo, las entradas de documento pueden ser las filas de una base de datos, los blobs en Blob Storage o documentos JSON en el disco. En este ejemplo vamos a atajar e insertaremos los documentos JSON para cuatro hoteles en el propio código.

Al cargar documentos, tiene que utilizar un objeto IndexDocumentsBatch. Un objeto IndexDocumentsBatch contiene una colección de Acciones, y cada una de ellas contiene un documento y una propiedad que indican a Azure AI Search la acción que debe realizar (carga, combinación, eliminación y mergeOrUpload).

  1. En Program.cs, cree una matriz de documentos y las acciones de índice y pase la matriz a IndexDocumentsBatch. Los siguientes documentos se ajustan al índice hotels-quickstart, tal como se define mediante la clase hotel.

    // Upload documents in a single Upload request.
    private static void UploadDocuments(SearchClient searchClient)
    {
        IndexDocumentsBatch<Hotel> batch = IndexDocumentsBatch.Create(
            IndexDocumentsAction.Upload(
                new Hotel()
                {
                    HotelId = "1",
                    HotelName = "Stay-Kay City Hotel",
                    Description = "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.",
                    DescriptionFr = "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.",
                    Category = "Boutique",
                    Tags = new[] { "pool", "air conditioning", "concierge" },
                    ParkingIncluded = false,
                    LastRenovationDate = new DateTimeOffset(1970, 1, 18, 0, 0, 0, TimeSpan.Zero),
                    Rating = 3.6,
                    Address = new Address()
                    {
                        StreetAddress = "677 5th Ave",
                        City = "New York",
                        StateProvince = "NY",
                        PostalCode = "10022",
                        Country = "USA"
                    }
                }),
            IndexDocumentsAction.Upload(
                new Hotel()
                {
                    HotelId = "2",
                    HotelName = "Old Century Hotel",
                    Description = "The hotel is situated in a  nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts.",
                    DescriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
                    Category = "Boutique",
                    Tags = new[] { "pool", "free wifi", "concierge" },
                    ParkingIncluded = false,
                    LastRenovationDate = new DateTimeOffset(1979, 2, 18, 0, 0, 0, TimeSpan.Zero),
                    Rating = 3.60,
                    Address = new Address()
                    {
                        StreetAddress = "140 University Town Center Dr",
                        City = "Sarasota",
                        StateProvince = "FL",
                        PostalCode = "34243",
                        Country = "USA"
                    }
                }),
            IndexDocumentsAction.Upload(
                new Hotel()
                {
                    HotelId = "3",
                    HotelName = "Gastronomic Landscape Hotel",
                    Description = "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.",
                    DescriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
                    Category = "Resort and Spa",
                    Tags = new[] { "air conditioning", "bar", "continental breakfast" },
                    ParkingIncluded = true,
                    LastRenovationDate = new DateTimeOffset(2015, 9, 20, 0, 0, 0, TimeSpan.Zero),
                    Rating = 4.80,
                    Address = new Address()
                    {
                        StreetAddress = "3393 Peachtree Rd",
                        City = "Atlanta",
                        StateProvince = "GA",
                        PostalCode = "30326",
                        Country = "USA"
                    }
                }),
            IndexDocumentsAction.Upload(
                new Hotel()
                {
                    HotelId = "4",
                    HotelName = "Sublime Palace Hotel",
                    Description = "Sublime Palace Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Palace is part of a lovingly restored 1800 palace.",
                    DescriptionFr = "Le Sublime Palace Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Palace fait partie d'un Palace 1800 restauré avec amour.",
                    Category = "Boutique",
                    Tags = new[] { "concierge", "view", "24-hour front desk service" },
                    ParkingIncluded = true,
                    LastRenovationDate = new DateTimeOffset(1960, 2, 06, 0, 0, 0, TimeSpan.Zero),
                    Rating = 4.60,
                    Address = new Address()
                    {
                        StreetAddress = "7400 San Pedro Ave",
                        City = "San Antonio",
                        StateProvince = "TX",
                        PostalCode = "78216",
                        Country = "USA"
                    }
                })
            );
    
        try
        {
            IndexDocumentsResult result = searchClient.IndexDocuments(batch);
        }
        catch (Exception)
        {
            // If for some reason any documents are dropped during indexing, you can compensate by delaying and
            // retrying. This simple demo just logs the failed document keys and continues.
            Console.WriteLine("Failed to index some of the documents: {0}");
        }
    }
    

    Una vez inicializado el objeto IndexDocumentsBatch, puede enviarlo al índice mediante una llamada a IndexDocuments en el objeto SearchClient.

  2. Agregue las siguientes líneas a Main(). La carga de documentos se realiza mediante SearchClient, pero la operación también requiere derechos de administrador en el servicio, lo cual se suele asociar a SearchIndexClient. Una manera de configurar esta operación es obtener SearchClient mediante SearchIndexClient (adminClient en este ejemplo).

     SearchClient ingesterClient = adminClient.GetSearchClient(indexName);
    
     // Load documents
     Console.WriteLine("{0}", "Uploading documents...\n");
     UploadDocuments(ingesterClient);
    
  3. Dado que se trata de una aplicación de consola que ejecuta todos los comandos secuencialmente, agregue un tiempo de espera de 2 segundos entre la indexación y las consultas.

    // Wait 2 seconds for indexing to complete before starting queries (for demo and console-app purposes only)
    Console.WriteLine("Waiting for indexing...\n");
    System.Threading.Thread.Sleep(2000);
    

    El retraso de 2 segundos compensa por la indexación, que es asincrónica, por lo que todos los documentos se pueden indexar antes de que se ejecutan las consultas. La codificación en un retraso solo suele ser necesaria en las pruebas, demostraciones y aplicaciones de ejemplo.

Búsqueda de un índice

Puede obtener resultados de consulta tan pronto como se indexe el primer documento, pero las pruebas reales del índice deben esperar hasta que todos los documentos estén indexados.

Esta sección agrega dos funcionalidades: la lógica de consulta y los resultados. En el caso de las consultas, use el método Search. Este método admite texto de búsqueda (la cadena de consulta) y otras opciones.

La clase SearchResults representa los resultados.

  1. En Program.cs, cree un método WriteDocuments, que imprime los resultados de la búsqueda en la consola.

    // Write search results to console
    private static void WriteDocuments(SearchResults<Hotel> searchResults)
    {
        foreach (SearchResult<Hotel> result in searchResults.GetResults())
        {
            Console.WriteLine(result.Document);
        }
    
        Console.WriteLine();
    }
    
    private static void WriteDocuments(AutocompleteResults autoResults)
    {
        foreach (AutocompleteItem result in autoResults.Results)
        {
            Console.WriteLine(result.Text);
        }
    
        Console.WriteLine();
    }
    
  2. Cree un método RunQueries para ejecutar consultas y devolver los resultados. Los resultados son objetos de Hotel. Este ejemplo muestra la firma del método y la primera consulta. Esta consulta muestra el parámetro Select, que permite crear el resultado con los campos seleccionados del documento.

    // Run queries, use WriteDocuments to print output
    private static void RunQueries(SearchClient srchclient)
    {
        SearchOptions options;
        SearchResults<Hotel> response;
    
        // Query 1
        Console.WriteLine("Query #1: Search on empty term '*' to return all documents, showing a subset of fields...\n");
    
        options = new SearchOptions()
        {
            IncludeTotalCount = true,
            Filter = "",
            OrderBy = { "" }
        };
    
        options.Select.Add("HotelId");
        options.Select.Add("HotelName");
        options.Select.Add("Address/City");
    
        response = srchclient.Search<Hotel>("*", options);
        WriteDocuments(response);
    
  3. En la segunda consulta, busque un término, agregue un filtro que seleccione los documentos cuya clasificación sea mayor que 4 y, a continuación, ordene por clasificación en orden descendente. Un filtro es una expresión booleana que se evalúa en campos IsFilterable en un índice. Las consultas de filtro incluyen o excluyen valores. Como tal, no hay puntuaciones de relevancia asociadas a una consulta de filtro.

    // Query 2
    Console.WriteLine("Query #2: Search on 'hotels', filter on 'Rating gt 4', sort by Rating in descending order...\n");
    
    options = new SearchOptions()
    {
        Filter = "Rating gt 4",
        OrderBy = { "Rating desc" }
    };
    
    options.Select.Add("HotelId");
    options.Select.Add("HotelName");
    options.Select.Add("Rating");
    
    response = srchclient.Search<Hotel>("hotels", options);
    WriteDocuments(response);
    
  4. La tercera consulta muestra searchFields, que se usa para delimitar una operación de búsqueda de texto completo a campos específicos.

    // Query 3
    Console.WriteLine("Query #3: Limit search to specific fields (pool in Tags field)...\n");
    
    options = new SearchOptions()
    {
        SearchFields = { "Tags" }
    };
    
    options.Select.Add("HotelId");
    options.Select.Add("HotelName");
    options.Select.Add("Tags");
    
    response = srchclient.Search<Hotel>("pool", options);
    WriteDocuments(response);
    
  5. La cuarta consulta muestra facets, que se puede usar para estructurar una estructura de navegación por facetas.

     // Query 4
     Console.WriteLine("Query #4: Facet on 'Category'...\n");
    
     options = new SearchOptions()
     {
         Filter = ""
     };
    
     options.Facets.Add("Category");
    
     options.Select.Add("HotelId");
     options.Select.Add("HotelName");
     options.Select.Add("Category");
    
     response = srchclient.Search<Hotel>("*", options);
     WriteDocuments(response);
    
  6. En la quinta consulta se devuelve un documento específico. Una búsqueda de documentos es una respuesta típica al evento OnClick en un conjunto de resultados.

     // Query 5
     Console.WriteLine("Query #5: Look up a specific document...\n");
    
     Response<Hotel> lookupResponse;
     lookupResponse = srchclient.GetDocument<Hotel>("3");
    
     Console.WriteLine(lookupResponse.Value.HotelId);
    
  7. La última consulta muestra la sintaxis de autocompletar; se simula una entrada de usuario parcial sa que se resuelve en dos posibles coincidencias en los sourceFields asociados al que se ha definido en el índice.

     // Query 6
     Console.WriteLine("Query #6: Call Autocomplete on HotelName that starts with 'sa'...\n");
    
     var autoresponse = srchclient.Autocomplete("sa", "sg");
     WriteDocuments(autoresponse);
    
  8. Agregue RunQueries a Main().

    // Call the RunQueries method to invoke a series of queries
    Console.WriteLine("Starting queries...\n");
    RunQueries(srchclient);
    
    // End the program
    Console.WriteLine("{0}", "Complete. Press any key to end this program...\n");
    Console.ReadKey();
    

Las consultas anteriores muestran varias maneras de buscar términos coincidentes en una consulta: búsqueda de texto completo, filtros y autocompletar.

Las búsquedas de texto completo y los filtros se realizan mediante el método SearchClient.Search. Se puede pasar una consulta de búsqueda en la cadena searchText, mientras que una expresión de filtro se puede pasar en la propiedad Filter de la clase SearchOptions. Para filtrar sin buscar, solo tiene que pasar "*" al parámetro searchText del método Search. Para buscar sin filtrar, deje la propiedad Filter sin establecer, o no pase ninguna instancia de SearchOptions.

Ejecución del programa

Presione F5 para recompilar la aplicación y ejecutar el programa en su totalidad.

La salida incluye mensajes de Console.WriteLIne, con la incorporación de la información de la consulta y los resultados.

Limpieza de recursos

Cuando trabaje con su propia suscripción, es una buena idea al final de un proyecto identificar si todavía se necesitan los recursos que ha creado. Los recursos que se dejan en ejecución pueden costarle mucho dinero. Puede eliminar los recursos de forma individual o eliminar el grupo de recursos para eliminar todo el conjunto de recursos.

Puede encontrar y administrar recursos en el portal, mediante el vínculo Todos los recursos o Grupos de recursos en el panel de navegación izquierdo.

Si está usando un servicio gratuito, recuerde que está limitado a tres índices, indexadores y orígenes de datos. Puede eliminar elementos individuales en el portal para mantenerse por debajo del límite.

Paso siguiente

En este inicio rápido, ha realizado un conjunto de tareas para crear un índice, cargar documentos en él y ejecutar consultas. En diferentes fases, hemos atajado para simplificar el código y mejorar la legibilidad y la comprensión. Ahora que ya conoce los conceptos básicos, pruebe el tutorial que llama a las API de Azure AI Search en una aplicación web.