Compartir a través de


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

Aprenda a usar la biblioteca cliente Azure.Search.Documents 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.

Esta guía de inicio rápido crea y consulta un pequeño índice de inicio rápido de hoteles que contiene datos sobre cuatro hoteles.

Sugerencia

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.

Requisitos previos

Requisitos previos de Microsoft Entra ID

Para la autenticación sin clave recomendada con Microsoft Entra ID, debe hacer lo siguiente:

  • Instale la CLI de Azure utilizada para la autenticación sin clave con Microsoft Entra ID.
  • Asigne los roles Search Service Contributor y Search Index Data Contributor a su cuenta de usuario. Puede asignar roles en Azure Portal en Control de acceso (IAM)>Agregar asignación de roles. Para más información, consulte Conexión a la Búsqueda de Azure AI usando los roles.

Recuperación de información de recursos

Debe recuperar la siguiente información para autenticar la aplicación con el servicio de Búsqueda de Azure AI:

Nombre de la variable Valor
SEARCH_API_ENDPOINT Este valor se puede encontrar en Azure Portal. Seleccione el servicio de búsqueda y, a continuación, en el menú de la izquierda, seleccione Información general. El valor de URL en Esenciales es el punto de conexión que necesita. Un punto de conexión de ejemplo podría ser similar a https://mydemo.search.windows.net.

Obtenga más información sobre la autenticación sin claves y la configuración de variables de entorno.

Configurar

  1. Cree una nueva carpeta full-text-quickstart para que contenga la aplicación y abra Visual Studio Code en esa carpeta con el siguiente comando:

    mkdir full-text-quickstart && cd full-text-quickstart
    
  2. Cree una nueva aplicación de consola con el siguiente comando:

    dotnet new console
    
  3. Instale la biblioteca cliente de Búsqueda de Azure AI (Azure.Search.Documents) para .NET con:

    dotnet add package Azure.Search.Documents
    
  4. Para la autenticación sin clave recomendada con Microsoft Entra ID, instale el paquete de Azure.Identity con:

    dotnet add package Azure.Identity
    
  5. Para la autenticación sin clave recomendada con Microsoft Entra ID, inicie sesión en Azure con el siguiente comando:

    az login
    

Creación, carga y consulta de un índice de búsqueda

En la sección de configuración anterior, creó una nueva aplicación de consola e instaló la biblioteca cliente de Búsqueda de Azure AI.

En esta sección, añadirá código para crear un índice de búsqueda, cargarlo con documentos y ejecutar consultas. Ejecute el programa para ver los resultados en la consola. Para una explicación detallada del código, consulte la sección explicación del código.

En este inicio rápido, el código de ejemplo usa Microsoft Entra ID para la autenticación sin clave recomendada. Si prefiere usar una clave de API, puede reemplazar el objeto DefaultAzureCredential por un objeto AzureKeyCredential.

Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/");
DefaultAzureCredential credential = new();
  1. En Program.cs, pegue el siguiente código. Edite las variables serviceName y apiKey con el nombre de su servicio de búsqueda y la clave de API de administrador.

    using System;
    using Azure;
    using Azure.Identity;
    using Azure.Search.Documents;
    using Azure.Search.Documents.Indexes;
    using Azure.Search.Documents.Indexes.Models;
    using Azure.Search.Documents.Models;
    
    namespace AzureSearch.Quickstart
    
    {
        class Program
        {
            static void Main(string[] args)
            {    
                // Your search service endpoint
                Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/");
    
                // Use the recommended keyless credential instead of the AzureKeyCredential credential.
                DefaultAzureCredential credential = new();
                //AzureKeyCredential credential = new AzureKeyCredential("Your search service admin key");
    
                // Create a SearchIndexClient to send create/delete index commands
                SearchIndexClient searchIndexClient = new SearchIndexClient(serviceEndpoint, credential);
    
                // Create a SearchClient to load and query documents
                string indexName = "hotels-quickstart";
                SearchClient searchClient = new SearchClient(serviceEndpoint, indexName, credential);
    
                // Delete index if it exists
                Console.WriteLine("{0}", "Deleting index...\n");
                DeleteIndexIfExists(indexName, searchIndexClient);
    
                // Create index
                Console.WriteLine("{0}", "Creating index...\n");
                CreateIndex(indexName, searchIndexClient);
    
                SearchClient ingesterClient = searchIndexClient.GetSearchClient(indexName);
    
                // Load documents
                Console.WriteLine("{0}", "Uploading documents...\n");
                UploadDocuments(ingesterClient);
    
                // Wait 2 secondsfor indexing to complete before starting queries (for demo and console-app purposes only)
                Console.WriteLine("Waiting for indexing...\n");
                System.Threading.Thread.Sleep(2000);
    
                // Call the RunQueries method to invoke a series of queries
                Console.WriteLine("Starting queries...\n");
                RunQueries(searchClient);
    
                // End the program
                Console.WriteLine("{0}", "Complete. Press any key to end this program...\n");
                Console.ReadKey();
            }
    
            // Delete the hotels-quickstart index to reuse its name
            private static void DeleteIndexIfExists(string indexName, SearchIndexClient searchIndexClient)
            {
                searchIndexClient.GetIndexNames();
                {
                    searchIndexClient.DeleteIndex(indexName);
                }
            }
            // Create hotels-quickstart index
            private static void CreateIndex(string indexName, SearchIndexClient searchIndexClient)
            {
                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);
    
                searchIndexClient.CreateOrUpdateIndex(definition);
            }
    
            // 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 = "Secret Point Motel",
                            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 = "Twin Dome Motel",
                            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 = "Triple 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 Cliff Hotel",
                            Description = "Sublime Cliff 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 Cliff is part of a lovingly restored 1800 palace.",
                            DescriptionFr = "Le sublime Cliff 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 Cliff 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}");
                }
            }
    
            // Run queries, use WriteDocuments to print output
            private static void RunQueries(SearchClient searchClient)
            {
                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("Rating");
    
                response = searchClient.Search<Hotel>("*", options);
                WriteDocuments(response);
    
                // 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 = searchClient.Search<Hotel>("hotels", options);
                WriteDocuments(response);
    
                // 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 = searchClient.Search<Hotel>("pool", options);
                WriteDocuments(response);
    
                // Query 4 - Use Facets to return a faceted navigation structure for a given query
                // Filters are typically used with facets to narrow results on OnClick events
                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 = searchClient.Search<Hotel>("*", options);
                WriteDocuments(response);
    
                // Query 5
                Console.WriteLine("Query #5: Look up a specific document...\n");
    
                Response<Hotel> lookupResponse;
                lookupResponse = searchClient.GetDocument<Hotel>("3");
    
                Console.WriteLine(lookupResponse.Value.HotelId);
    
    
                // Query 6
                Console.WriteLine("Query #6: Call Autocomplete on HotelName...\n");
    
                var autoresponse = searchClient.Autocomplete("sa", "sg");
                WriteDocuments(autoresponse);
    
            }
    
            // 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. En la misma carpeta, cree un nuevo archivo llamado Hotel.cs y pegue el siguiente código. Este código define la estructura de un documento de hotel.

    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; }
        }
    }
    
  3. Cree un nuevo archivo con el nombre Hotel.cs y pegue el siguiente código para definir la estructura de un documento de hoteles. 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; }
        }
    }
    
  4. Cree un nuevo archivo con el nombre Address.cs y pegue el siguiente código para definir la estructura de un documento de direcciones.

    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; }
        }
    }
    
  5. Cree un nuevo archivo con el nombre Hotel.Methods.cs y pegue el siguiente código para definir una invalidación de ToString() para la clase Hotel.

    using System;
    using System.Text;
    
    namespace AzureSearch.Quickstart
    {
        public partial class Hotel
        {
            public override string ToString()
            {
                var builder = new StringBuilder();
    
                if (!String.IsNullOrEmpty(HotelId))
                {
                    builder.AppendFormat("HotelId: {0}\n", HotelId);
                }
    
                if (!String.IsNullOrEmpty(HotelName))
                {
                    builder.AppendFormat("Name: {0}\n", HotelName);
                }
    
                if (!String.IsNullOrEmpty(Description))
                {
                    builder.AppendFormat("Description: {0}\n", Description);
                }
    
                if (!String.IsNullOrEmpty(DescriptionFr))
                {
                    builder.AppendFormat("Description (French): {0}\n", DescriptionFr);
                }
    
                if (!String.IsNullOrEmpty(Category))
                {
                    builder.AppendFormat("Category: {0}\n", Category);
                }
    
                if (Tags != null && Tags.Length > 0)
                {
                    builder.AppendFormat("Tags: [ {0} ]\n", String.Join(", ", Tags));
                }
    
                if (ParkingIncluded.HasValue)
                {
                    builder.AppendFormat("Parking included: {0}\n", ParkingIncluded.Value ? "yes" : "no");
                }
    
                if (LastRenovationDate.HasValue)
                {
                    builder.AppendFormat("Last renovated on: {0}\n", LastRenovationDate);
                }
    
                if (Rating.HasValue)
                {
                    builder.AppendFormat("Rating: {0}\n", Rating);
                }
    
                if (Address != null && !Address.IsEmpty)
                {
                    builder.AppendFormat("Address: \n{0}\n", Address.ToString());
                }
    
                return builder.ToString();
            }
        }
    }
    
  6. Cree un nuevo archivo con el nombre Address.Methods.cs y pegue el siguiente código para definir una invalidación de ToString() para la clase Address.

    using System;
    using System.Text;
    using System.Text.Json.Serialization;
    
    namespace AzureSearch.Quickstart
    {
        public partial class Address
        {
            public override string ToString()
            {
                var builder = new StringBuilder();
    
                if (!IsEmpty)
                {
                    builder.AppendFormat("{0}\n{1}, {2} {3}\n{4}", StreetAddress, City, StateProvince, PostalCode, Country);
                }
    
                return builder.ToString();
            }
    
            [JsonIgnore]
            public bool IsEmpty => String.IsNullOrEmpty(StreetAddress) &&
                                   String.IsNullOrEmpty(City) &&
                                   String.IsNullOrEmpty(StateProvince) &&
                                   String.IsNullOrEmpty(PostalCode) &&
                                   String.IsNullOrEmpty(Country);
        }
    }
    
  7. Compile y ejecute la aplicación con el siguiente comando:

    dotnet run
    

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

Explicación del código

En las secciones anteriores, creó una nueva aplicación de consola e instaló la biblioteca cliente de Búsqueda de Azure AI. Agregó código para crear un índice de búsqueda, cargarlo con documentos y ejecutar consultas. Ejecutó el programa para ver los resultados en la consola.

En esta sección, se explica el código que agregó a la aplicación de consola.

Creación de un cliente de búsqueda

En Program.cs, creó dos clientes:

Ambos clientes necesitan el punto de conexión del servicio de búsqueda y las credenciales descritas anteriormente en la sección información de recursos.

En este inicio rápido, el código de ejemplo usa Microsoft Entra ID para la autenticación sin clave recomendada. Si prefiere usar una clave de API, puede reemplazar el objeto DefaultAzureCredential por un objeto AzureKeyCredential.

Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/");
DefaultAzureCredential credential = new();
static void Main(string[] args)
{
    // Your search service endpoint
    Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/");

    // Use the recommended keyless credential instead of the AzureKeyCredential credential.
    DefaultAzureCredential credential = new();
    //AzureKeyCredential credential = new AzureKeyCredential("Your search service admin key");

    // Create a SearchIndexClient to send create/delete index commands
    SearchIndexClient searchIndexClient = new SearchIndexClient(serviceEndpoint, credential);

    // Create a SearchClient to load and query documents
    string indexName = "hotels-quickstart";
    SearchClient searchClient = new SearchClient(serviceEndpoint, indexName, credential);
    
    // REDACTED FOR BREVITY . . . 
}

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, se definen 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.

Definición de las estructuras

Ha creado dos clases de asistente, Hotel.cs y Address.cs, para definir la estructura de un documento de hoteles y su dirección. La clase Hotel incluye campos para el id. del hotel, nombre, descripción, categoría, etiquetas, aparcamiento, fecha de renovación, calificación y dirección. La clase Address incluye campos para la dirección, la ciudad, el estado o provincia, el código postal y el país o región.

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.

Creación del índice de búsqueda

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 searchIndexClient)
{
    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);

    searchIndexClient.CreateOrUpdateIndex(definition);
}

Carga de documentos

Azure AI Search busca en el contenido almacenado en el servicio. En este paso, cargará documentos JSON que se ajusten al índice de hoteles que creó.

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).

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"
                }
            }),
        // REDACTED FOR BREVITY
}

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

Puede cargar documentos usando SearchClient en Main(), pero la operación también requiere derechos de administrador en el servicio, que normalmente está asociado con SearchIndexClient. Una manera de configurar esta operación es obtener SearchClient mediante SearchIndexClient (searchIndexClient en este ejemplo).

SearchClient ingesterClient = searchIndexClient.GetSearchClient(indexName);

// Load documents
Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(ingesterClient);

Dado que tenemos una aplicación de consola que ejecuta todos los comandos secuencialmente, agregamos 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.

En Program.cs, el método WriteDocuments 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();
}

Ejemplo de consulta 1

El método RunQueries ejecuta consultas y devuelve 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 searchClient)
{
    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 = searchClient.Search<Hotel>("*", options);
    WriteDocuments(response);
    // REDACTED FOR BREVITY
}

Ejemplo de consulta 2

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 = searchClient.Search<Hotel>("hotels", options);
WriteDocuments(response);

Ejemplo de consulta 3

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 = searchClient.Search<Hotel>("pool", options);
WriteDocuments(response);

Ejemplo de consulta 4

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 = searchClient.Search<Hotel>("*", options);
WriteDocuments(response);

Ejemplo de consulta 5

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 = searchClient.GetDocument<Hotel>("3");

Console.WriteLine(lookupResponse.Value.HotelId);

Ejemplo de consulta 6

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 = searchClient.Autocomplete("sa", "sg");
WriteDocuments(autoresponse);

Resumen de consultas

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.

Aprenda a usar la biblioteca cliente Azure.Search.Documents 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.

Esta guía de inicio rápido crea y consulta un pequeño índice de inicio rápido de hoteles que contiene datos sobre cuatro hoteles.

Sugerencia

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.

Requisitos previos

Requisitos previos de Microsoft Entra ID

Para la autenticación sin clave recomendada con Microsoft Entra ID, debe hacer lo siguiente:

  • Instale la CLI de Azure utilizada para la autenticación sin clave con Microsoft Entra ID.
  • Asigne los roles Search Service Contributor y Search Index Data Contributor a su cuenta de usuario. Puede asignar roles en Azure Portal en Control de acceso (IAM)>Agregar asignación de roles. Para más información, consulte Conexión a la Búsqueda de Azure AI usando los roles.

Recuperación de información de recursos

Debe recuperar la siguiente información para autenticar la aplicación con el servicio de Búsqueda de Azure AI:

Nombre de la variable Valor
SEARCH_API_ENDPOINT Este valor se puede encontrar en Azure Portal. Seleccione el servicio de búsqueda y, a continuación, en el menú de la izquierda, seleccione Información general. El valor de URL en Esenciales es el punto de conexión que necesita. Un punto de conexión de ejemplo podría ser similar a https://mydemo.search.windows.net.

Obtenga más información sobre la autenticación sin claves y la configuración de variables de entorno.

Configuración

El ejemplo de esta guía de inicio rápido funciona con el entorno de ejecución de Java. Instale un kit de desarrollo de Java como Azul Zulu OpenJDK. La compilación de Microsoft de OpenJDK o su JDK preferido también deberían funcionar.

  1. Instalación de Apache Maven. A continuación, ejecute mvn -v para confirmar que la instalación se ha realizado correctamente.

  2. Cree un nuevo archivo pom.xml en la raíz del proyecto y copie el siguiente código en él:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>azure.search.sample</groupId>
        <artifactId>azuresearchquickstart</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <build>
            <sourceDirectory>src</sourceDirectory>
            <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                <source>1.8</source>
                <target>1.8</target>
                </configuration>
            </plugin>
            </plugins>
        </build>
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>com.azure</groupId>
                <artifactId>azure-search-documents</artifactId>
                <version>11.7.3</version>
            </dependency>
            <dependency>
                <groupId>com.azure</groupId>
                <artifactId>azure-core</artifactId>
                <version>1.53.0</version>
            </dependency>
            <dependency>
                <groupId>com.azure</groupId>
                <artifactId>azure-identity</artifactId>
                <version>1.15.1</version>
            </dependency>
        </dependencies>
    </project>
    
  3. Instale las dependencias, incluida la biblioteca cliente de Búsqueda de Azure AI (Azure.Search.Documents) para Java y la biblioteca cliente de Azure Identity para Java con:

    mvn clean dependency:copy-dependencies
    
  4. Para la autenticación sin clave recomendada con Microsoft Entra ID, inicie sesión en Azure con el siguiente comando:

    az login
    

Creación, carga y consulta de un índice de búsqueda

En la sección de configuración anterior, instaló la biblioteca cliente de Búsqueda de Azure AI y otras dependencias.

En esta sección, añadirá código para crear un índice de búsqueda, cargarlo con documentos y ejecutar consultas. Ejecute el programa para ver los resultados en la consola. Para una explicación detallada del código, consulte la sección explicación del código.

En este inicio rápido, el código de ejemplo usa Microsoft Entra ID para la autenticación sin clave recomendada. Si prefiere usar una clave de API, puede reemplazar el objeto DefaultAzureCredential por un objeto AzureKeyCredential.

String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
  1. Cree un nuevo archivo con el nombre App.java y pegue el siguiente código en App.java:

    import java.util.Arrays;
    import java.util.ArrayList;
    import java.time.OffsetDateTime;
    import java.time.ZoneOffset;
    import java.time.LocalDateTime;
    import java.time.LocalDate;
    import java.time.LocalTime;
    import com.azure.core.util.Configuration;
    import com.azure.core.util.Context;
    import com.azure.identity.DefaultAzureCredential;
    import com.azure.identity.DefaultAzureCredentialBuilder;
    import com.azure.search.documents.SearchClient;
    import com.azure.search.documents.SearchClientBuilder;
    import com.azure.search.documents.indexes.SearchIndexClient;
    import com.azure.search.documents.indexes.SearchIndexClientBuilder;
    import com.azure.search.documents.indexes.models.IndexDocumentsBatch;
    import com.azure.search.documents.models.SearchOptions;
    import com.azure.search.documents.indexes.models.SearchIndex;
    import com.azure.search.documents.indexes.models.SearchSuggester;
    import com.azure.search.documents.util.AutocompletePagedIterable;
    import com.azure.search.documents.util.SearchPagedIterable;
    
    public class App {
    
        public static void main(String[] args) {
            // Your search service endpoint
            "https://<Put your search service NAME here>.search.windows.net/";
    
            // Use the recommended keyless credential instead of the AzureKeyCredential credential.
            DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
            //AzureKeyCredential credential = new AzureKeyCredential("<Your search service admin key>");
    
            // Create a SearchIndexClient to send create/delete index commands
            SearchIndexClient searchIndexClient = new SearchIndexClientBuilder()
                .endpoint(searchServiceEndpoint)
                .credential(credential)
                .buildClient();
    
            // Create a SearchClient to load and query documents
            String indexName = "hotels-quickstart-java";
            SearchClient searchClient = new SearchClientBuilder()
                .endpoint(searchServiceEndpoint)
                .credential(credential)
                .indexName(indexName)
                .buildClient();
    
            // Create Search Index for Hotel model
            searchIndexClient.createOrUpdateIndex(
                new SearchIndex(indexName, SearchIndexClient.buildSearchFields(Hotel.class, null))
                .setSuggesters(new SearchSuggester("sg", Arrays.asList("HotelName"))));
    
            // Upload sample hotel documents to the Search Index
            uploadDocuments(searchClient);
    
            // Wait 2 seconds for indexing to complete before starting queries (for demo and console-app purposes only)
            System.out.println("Waiting for indexing...\n");
            try
            {
                Thread.sleep(2000);
            }
            catch (InterruptedException e)
            {
            }
    
            // Call the RunQueries method to invoke a series of queries
            System.out.println("Starting queries...\n");
            RunQueries(searchClient);
    
            // End the program
            System.out.println("Complete.\n");
        }
    
        // Upload documents in a single Upload request.
        private static void uploadDocuments(SearchClient searchClient)
        {
            var hotelList = new ArrayList<Hotel>();
    
            var hotel = new Hotel();
            hotel.hotelId = "1";
            hotel.hotelName = "Stay-Kay City Hotel";
            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.";
            hotel.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.";
            hotel.category = "Boutique";
            hotel.tags = new String[] { "pool", "air conditioning", "concierge" };
            hotel.parkingIncluded = false;
            hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(1970, 1, 18), LocalTime.of(0, 0)), ZoneOffset.UTC);
            hotel.rating = 3.6;
            hotel.address = new Address();
            hotel.address.streetAddress = "677 5th Ave";
            hotel.address.city = "New York";
            hotel.address.stateProvince = "NY";
            hotel.address.postalCode = "10022";
            hotel.address.country = "USA";
            hotelList.add(hotel);
    
            hotel = new Hotel();
            hotel.hotelId = "2";
            hotel.hotelName = "Old Century Hotel";
            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.";
            hotel.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.";
            hotel.category = "Boutique";
            hotel.tags = new String[] { "pool", "free wifi", "concierge" };
            hotel.parkingIncluded = false;
            hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(1979, 2, 18), LocalTime.of(0, 0)), ZoneOffset.UTC);
            hotel.rating = 3.60;
            hotel.address = new Address();
            hotel.address.streetAddress = "140 University Town Center Dr";
            hotel.address.city = "Sarasota";
            hotel.address.stateProvince = "FL";
            hotel.address.postalCode = "34243";
            hotel.address.country = "USA";
            hotelList.add(hotel);
    
            hotel = new Hotel();
            hotel.hotelId = "3";
            hotel.hotelName = "Gastronomic Landscape Hotel";
            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.";
            hotel.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.";
            hotel.category = "Resort and Spa";
            hotel.tags = new String[] { "air conditioning", "bar", "continental breakfast" };
            hotel.parkingIncluded = true;
            hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(2015, 9, 20), LocalTime.of(0, 0)), ZoneOffset.UTC);
            hotel.rating = 4.80;
            hotel.address = new Address();
            hotel.address.streetAddress = "3393 Peachtree Rd";
            hotel.address.city = "Atlanta";
            hotel.address.stateProvince = "GA";
            hotel.address.postalCode = "30326";
            hotel.address.country = "USA";
            hotelList.add(hotel);
    
            hotel = new Hotel();
            hotel.hotelId = "4";
            hotel.hotelName = "Sublime Palace Hotel";
            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.";
            hotel.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.";
            hotel.category = "Boutique";
            hotel.tags = new String[] { "concierge", "view", "24-hour front desk service" };
            hotel.parkingIncluded = true;
            hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(1960, 2, 06), LocalTime.of(0, 0)), ZoneOffset.UTC);
            hotel.rating = 4.60;
            hotel.address = new Address();
            hotel.address.streetAddress = "7400 San Pedro Ave";
            hotel.address.city = "San Antonio";
            hotel.address.stateProvince = "TX";
            hotel.address.postalCode = "78216";
            hotel.address.country = "USA";
            hotelList.add(hotel);
    
            var batch = new IndexDocumentsBatch<Hotel>();
            batch.addMergeOrUploadActions(hotelList);
            try
            {
                searchClient.indexDocuments(batch);
            }
            catch (Exception e)
            {
                e.printStackTrace();
                // If for some reason any documents are dropped during indexing, you can compensate by delaying and
                // retrying. This simple demo just logs failure and continues
                System.err.println("Failed to index some of the documents");
            }
        }
    
        // Write search results to console
        private static void WriteSearchResults(SearchPagedIterable searchResults)
        {
            searchResults.iterator().forEachRemaining(result ->
            {
                Hotel hotel = result.getDocument(Hotel.class);
                System.out.println(hotel);
            });
    
            System.out.println();
        }
    
        // Write autocomplete results to console
        private static void WriteAutocompleteResults(AutocompletePagedIterable autocompleteResults)
        {
            autocompleteResults.iterator().forEachRemaining(result ->
            {
                String text = result.getText();
                System.out.println(text);
            });
    
            System.out.println();
        }
    
        // Run queries, use WriteDocuments to print output
        private static void RunQueries(SearchClient searchClient)
        {
            // Query 1
            System.out.println("Query #1: Search on empty term '*' to return all documents, showing a subset of fields...\n");
    
            SearchOptions options = new SearchOptions();
            options.setIncludeTotalCount(true);
            options.setFilter("");
            options.setOrderBy("");
            options.setSelect("HotelId", "HotelName", "Address/City");
    
            WriteSearchResults(searchClient.search("*", options, Context.NONE));
    
            // Query 2
            System.out.println("Query #2: Search on 'hotels', filter on 'Rating gt 4', sort by Rating in descending order...\n");
    
            options = new SearchOptions();
            options.setFilter("Rating gt 4");
            options.setOrderBy("Rating desc");
            options.setSelect("HotelId", "HotelName", "Rating");
    
            WriteSearchResults(searchClient.search("hotels", options, Context.NONE));
    
            // Query 3
            System.out.println("Query #3: Limit search to specific fields (pool in Tags field)...\n");
    
            options = new SearchOptions();
            options.setSearchFields("Tags");
    
            options.setSelect("HotelId", "HotelName", "Tags");
    
            WriteSearchResults(searchClient.search("pool", options, Context.NONE));
    
            // Query 4
            System.out.println("Query #4: Facet on 'Category'...\n");
    
            options = new SearchOptions();
            options.setFilter("");
            options.setFacets("Category");
            options.setSelect("HotelId", "HotelName", "Category");
    
            WriteSearchResults(searchClient.search("*", options, Context.NONE));
    
            // Query 5
            System.out.println("Query #5: Look up a specific document...\n");
    
            Hotel lookupResponse = searchClient.getDocument("3", Hotel.class);
            System.out.println(lookupResponse.hotelId);
            System.out.println();
    
             // Query 6
            System.out.println("Query #6: Call Autocomplete on HotelName that starts with 's'...\n");
    
            WriteAutocompleteResults(searchClient.autocomplete("s", "sg"));
        }
    }
    
  2. Cree un nuevo archivo con el nombre Hotel.java y pegue el siguiente código en Hotel.java:

    import com.azure.search.documents.indexes.SearchableField;
    import com.azure.search.documents.indexes.SimpleField;
    import com.fasterxml.jackson.annotation.JsonInclude;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.annotation.JsonInclude.Include;
    
    import java.time.OffsetDateTime;
    
    /**
     * Model class representing a hotel.
     */
    @JsonInclude(Include.NON_NULL)
    public class Hotel {
        /**
         * Hotel ID
         */
        @JsonProperty("HotelId")
        @SimpleField(isKey = true)
        public String hotelId;
    
        /**
         * Hotel name
         */
        @JsonProperty("HotelName")
        @SearchableField(isSortable = true)
        public String hotelName;
    
        /**
         * Description
         */
        @JsonProperty("Description")
        @SearchableField(analyzerName = "en.microsoft")
        public String description;
    
        /**
         * French description
         */
        @JsonProperty("DescriptionFr")
        @SearchableField(analyzerName = "fr.lucene")
        public String descriptionFr;
    
        /**
         * Category
         */
        @JsonProperty("Category")
        @SearchableField(isFilterable = true, isSortable = true, isFacetable = true)
        public String category;
    
        /**
         * Tags
         */
        @JsonProperty("Tags")
        @SearchableField(isFilterable = true, isFacetable = true)
        public String[] tags;
    
        /**
         * Whether parking is included
         */
        @JsonProperty("ParkingIncluded")
        @SimpleField(isFilterable = true, isSortable = true, isFacetable = true)
        public Boolean parkingIncluded;
    
        /**
         * Last renovation time
         */
        @JsonProperty("LastRenovationDate")
        @SimpleField(isFilterable = true, isSortable = true, isFacetable = true)
        public OffsetDateTime lastRenovationDate;
    
        /**
         * Rating
         */
        @JsonProperty("Rating")
        @SimpleField(isFilterable = true, isSortable = true, isFacetable = true)
        public Double rating;
    
        /**
         * Address
         */
        @JsonProperty("Address")
        public Address address;
    
        @Override
        public String toString()
        {
            try
            {
                return new ObjectMapper().writeValueAsString(this);
            }
            catch (JsonProcessingException e)
            {
                e.printStackTrace();
                return "";
            }
        }
    }
    
  3. Cree un nuevo archivo con el nombre Address.java y pegue el siguiente código en Address.java:

    import com.azure.search.documents.indexes.SearchableField;
    import com.fasterxml.jackson.annotation.JsonInclude;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.annotation.JsonInclude.Include;
    
    /**
     * Model class representing an address.
     */
    @JsonInclude(Include.NON_NULL)
    public class Address {
        /**
         * Street address
         */
        @JsonProperty("StreetAddress")
        @SearchableField
        public String streetAddress;
    
        /**
         * City
         */
        @JsonProperty("City")
        @SearchableField(isFilterable = true, isSortable = true, isFacetable = true)
        public String city;
    
        /**
         * State or province
         */
        @JsonProperty("StateProvince")
        @SearchableField(isFilterable = true, isSortable = true, isFacetable = true)
        public String stateProvince;
    
        /**
         * Postal code
         */
        @JsonProperty("PostalCode")
        @SearchableField(isFilterable = true, isSortable = true, isFacetable = true)
        public String postalCode;
    
        /**
         * Country
         */
        @JsonProperty("Country")
        @SearchableField(isFilterable = true, isSortable = true, isFacetable = true)
        public String country;
    }
    
  4. Ejecute la nueva aplicación de consola:

    javac Address.java App.java Hotel.java -cp ".;target\dependency\*"
    java -cp ".;target\dependency\*" App
    

Explicación del código

En las secciones anteriores, creó una nueva aplicación de consola e instaló la biblioteca cliente de Búsqueda de Azure AI. Agregó código para crear un índice de búsqueda, cargarlo con documentos y ejecutar consultas. Ejecutó el programa para ver los resultados en la consola.

En esta sección, se explica el código que agregó a la aplicación de consola.

Creación de un cliente de búsqueda

En App.java creó dos clientes:

  • SearchIndexClient crea el índice.
  • SearchClient carga y consulta un índice existente.

Ambos clientes necesitan el punto de conexión del servicio de búsqueda y las credenciales descritas anteriormente en la sección información de recursos.

En este inicio rápido, el código de ejemplo usa Microsoft Entra ID para la autenticación sin clave recomendada. Si prefiere usar una clave de API, puede reemplazar el objeto DefaultAzureCredential por un objeto AzureKeyCredential.

String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
public static void main(String[] args) {
    // Your search service endpoint
    String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";

    // Use the recommended keyless credential instead of the AzureKeyCredential credential.
    DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
    //AzureKeyCredential credential = new AzureKeyCredential("Your search service admin key");

    // Create a SearchIndexClient to send create/delete index commands
    SearchIndexClient searchIndexClient = new SearchIndexClientBuilder()
        .endpoint(searchServiceEndpoint)
        .credential(credential)
        .buildClient();
    
    // Create a SearchClient to load and query documents
    String indexName = "hotels-quickstart-java";
    SearchClient searchClient = new SearchClientBuilder()
        .endpoint(searchServiceEndpoint)
        .credential(credential)
        .indexName(indexName)
        .buildClient();

    // Create Search Index for Hotel model
    searchIndexClient.createOrUpdateIndex(
        new SearchIndex(indexName, SearchIndexClient.buildSearchFields(Hotel.class, null))
        .setSuggesters(new SearchSuggester("sg", Arrays.asList("HotelName"))));

    // REDACTED FOR BREVITY . . . 
}

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, se definen 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.

Definición de las estructuras

Ha creado dos clases de asistente, Hotel.java y Address.java, para definir la estructura de un documento de hoteles y su dirección. La clase Hotel incluye campos para el id. del hotel, nombre, descripción, categoría, etiquetas, aparcamiento, fecha de renovación, calificación y dirección. La clase Address incluye campos para la dirección, la ciudad, el estado o provincia, el código postal y el país o región.

En la biblioteca cliente Azure.Search.Documents puede usar SearchableField y SimpleField para optimizar las definiciones de campo.

  • 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.
  • 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.

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, isSortable y isFacetable deben atribuirse explícitamente, como se muestra en el ejemplo anterior.

Creación del índice de búsqueda

En App.java, cree un objeto SearchIndex en el método main y, a continuación, llame al método createOrUpdateIndex para crear un índice en su servicio de búsqueda. El índice también incluye SearchSuggester, que habilita la función autocompletar en los campos especificados.

// Create Search Index for Hotel model
searchIndexClient.createOrUpdateIndex(
    new SearchIndex(indexName, SearchIndexClient.buildSearchFields(Hotel.class, null))
    .setSuggesters(new SearchSuggester("sg", Arrays.asList("HotelName"))));

Carga de documentos

Azure AI Search busca en el contenido almacenado en el servicio. En este paso, cargará documentos JSON que se ajusten al índice de hoteles que creó.

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 IndexActions, 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).

En App.java, cree documentos y acciones de índice y, a continuación, páselos a IndexDocumentsBatch. Los siguientes documentos se ajustan al índice hotels-quickstart, tal como se define mediante la clase hotel.

private static void uploadDocuments(SearchClient searchClient)
{
    var hotelList = new ArrayList<Hotel>();

    var hotel = new Hotel();
    hotel.hotelId = "1";
    hotel.hotelName = "Stay-Kay City Hotel";
    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.";
    hotel.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.";
    hotel.category = "Boutique";
    hotel.tags = new String[] { "pool", "air conditioning", "concierge" };
    hotel.parkingIncluded = false;
    hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(1970, 1, 18), LocalTime.of(0, 0)), ZoneOffset.UTC);
    hotel.rating = 3.6;
    hotel.address = new Address();
    hotel.address.streetAddress = "677 5th Ave";
    hotel.address.city = "New York";
    hotel.address.stateProvince = "NY";
    hotel.address.postalCode = "10022";
    hotel.address.country = "USA";
    hotelList.add(hotel);
    
    // REDACTED FOR BREVITY

    var batch = new IndexDocumentsBatch<Hotel>();
    batch.addMergeOrUploadActions(hotelList);
    try
    {
        searchClient.indexDocuments(batch);
    }
    catch (Exception e)
    {
        e.printStackTrace();
        // If for some reason any documents are dropped during indexing, you can compensate by delaying and
        // retrying. This simple demo just logs failure and continues
        System.err.println("Failed to index some of the documents");
    }
}

Una vez que haya inicializado el objeto IndexDocumentsBatch, puede enviarlo al índice, para lo que debe realizar una llamada a indexDocuments en el objeto SearchClient.

Puede cargar documentos usando SearchClient en main(), pero la operación también requiere derechos de administrador en el servicio, que normalmente está asociado con SearchIndexClient. Una manera de configurar esta operación es obtener SearchClient mediante SearchIndexClient (searchIndexClient en este ejemplo).

uploadDocuments(searchClient);

Dado que tenemos una aplicación de consola que ejecuta todos los comandos secuencialmente, agregamos 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)
System.out.println("Waiting for indexing...\n");
try
{
    Thread.sleep(2000);
}
catch (InterruptedException e)
{
}

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.

En App.java, el método WriteDocuments imprime los resultados de la búsqueda en la consola.

// Write search results to console
private static void WriteSearchResults(SearchPagedIterable searchResults)
{
    searchResults.iterator().forEachRemaining(result ->
    {
        Hotel hotel = result.getDocument(Hotel.class);
        System.out.println(hotel);
    });

    System.out.println();
}

// Write autocomplete results to console
private static void WriteAutocompleteResults(AutocompletePagedIterable autocompleteResults)
{
    autocompleteResults.iterator().forEachRemaining(result ->
    {
        String text = result.getText();
        System.out.println(text);
    });

    System.out.println();
}

Ejemplo de consulta 1

El método RunQueries ejecuta consultas y devuelve 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 searchClient)
{
    // Query 1
    System.out.println("Query #1: Search on empty term '*' to return all documents, showing a subset of fields...\n");

    SearchOptions options = new SearchOptions();
    options.setIncludeTotalCount(true);
    options.setFilter("");
    options.setOrderBy("");
    options.setSelect("HotelId", "HotelName", "Address/City");

    WriteSearchResults(searchClient.search("*", options, Context.NONE));
}

Ejemplo de consulta 2

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
System.out.println("Query #2: Search on 'hotels', filter on 'Rating gt 4', sort by Rating in descending order...\n");

options = new SearchOptions();
options.setFilter("Rating gt 4");
options.setOrderBy("Rating desc");
options.setSelect("HotelId", "HotelName", "Rating");

WriteSearchResults(searchClient.search("hotels", options, Context.NONE));

Ejemplo de consulta 3

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
System.out.println("Query #3: Limit search to specific fields (pool in Tags field)...\n");

options = new SearchOptions();
options.setSearchFields("Tags");

options.setSelect("HotelId", "HotelName", "Tags");

WriteSearchResults(searchClient.search("pool", options, Context.NONE));

Ejemplo de consulta 4

La cuarta consulta muestra facets, que se puede usar para estructurar una estructura de navegación por facetas.

// Query 4
System.out.println("Query #4: Facet on 'Category'...\n");

options = new SearchOptions();
options.setFilter("");
options.setFacets("Category");
options.setSelect("HotelId", "HotelName", "Category");

WriteSearchResults(searchClient.search("*", options, Context.NONE));

Ejemplo de consulta 5

En la quinta consulta se devuelve un documento específico.

// Query 5
System.out.println("Query #5: Look up a specific document...\n");

Hotel lookupResponse = searchClient.getDocument("3", Hotel.class);
System.out.println(lookupResponse.hotelId);
System.out.println();

Ejemplo de consulta 6

La última consulta muestra la sintaxis de autocompletar; se simula una entrada de usuario parcial s que se resuelve en dos posibles coincidencias en los sourceFields asociados al proveedor de sugerencias que se ha definido en el índice.

// Query 6
System.out.println("Query #6: Call Autocomplete on HotelName that starts with 's'...\n");

WriteAutocompleteResults(searchClient.autocomplete("s", "sg"));

Resumen de consultas

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.

Aprenda a usar la biblioteca cliente Azure.Search.Documents 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.

Esta guía de inicio rápido crea y consulta un pequeño índice de inicio rápido de hoteles que contiene datos sobre cuatro hoteles.

Sugerencia

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.

Requisitos previos

Requisitos previos de Microsoft Entra ID

Para la autenticación sin clave recomendada con Microsoft Entra ID, debe hacer lo siguiente:

  • Instale la CLI de Azure utilizada para la autenticación sin clave con Microsoft Entra ID.
  • Asigne los roles Search Service Contributor y Search Index Data Contributor a su cuenta de usuario. Puede asignar roles en Azure Portal en Control de acceso (IAM)>Agregar asignación de roles. Para más información, consulte Conexión a la Búsqueda de Azure AI usando los roles.

Recuperación de información de recursos

Debe recuperar la siguiente información para autenticar la aplicación con el servicio de Búsqueda de Azure AI:

Nombre de la variable Valor
SEARCH_API_ENDPOINT Este valor se puede encontrar en Azure Portal. Seleccione el servicio de búsqueda y, a continuación, en el menú de la izquierda, seleccione Información general. El valor de URL en Esenciales es el punto de conexión que necesita. Un punto de conexión de ejemplo podría ser similar a https://mydemo.search.windows.net.

Obtenga más información sobre la autenticación sin claves y la configuración de variables de entorno.

Configurar

  1. Cree una nueva carpeta full-text-quickstart para que contenga la aplicación y abra Visual Studio Code en esa carpeta con el siguiente comando:

    mkdir full-text-quickstart && cd full-text-quickstart
    
  2. Cree el package.json con el comando siguiente:

    npm init -y
    
  3. Instale la biblioteca cliente de Búsqueda de Azure AI (Azure.Search.Documents) para JavaScript con:

    npm install @azure/search-documents
    
  4. Para la autenticación sin contraseña recomendada, instale la biblioteca cliente de Azure Identity con:

    npm install @azure/identity
    

Creación, carga y consulta de un índice de búsqueda

En la sección de configuración anterior, instaló la biblioteca cliente de Búsqueda de Azure AI y otras dependencias.

En esta sección, añadirá código para crear un índice de búsqueda, cargarlo con documentos y ejecutar consultas. Ejecute el programa para ver los resultados en la consola. Para una explicación detallada del código, consulte la sección explicación del código.

En este inicio rápido, el código de ejemplo usa Microsoft Entra ID para la autenticación sin clave recomendada. Si prefiere usar una clave de API, puede reemplazar el objeto DefaultAzureCredential por un objeto AzureKeyCredential.

String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
  1. Cree un nuevo archivo con el nombre index.js y pegue el siguiente código en index.js:

    // Import from the @azure/search-documents library
    import { SearchIndexClient, odata } from "@azure/search-documents";
    // Import from the Azure Identity library
    import { DefaultAzureCredential } from "@azure/identity";
    // Importing the hotels sample data
    import hotelData from './hotels.json' assert { type: "json" };
    // Load the .env file if it exists
    import * as dotenv from "dotenv";
    dotenv.config();
    // Defining the index definition
    const indexDefinition = {
        "name": "hotels-quickstart",
        "fields": [
            {
                "name": "HotelId",
                "type": "Edm.String",
                "key": true,
                "filterable": true
            },
            {
                "name": "HotelName",
                "type": "Edm.String",
                "searchable": true,
                "filterable": false,
                "sortable": true,
                "facetable": false
            },
            {
                "name": "Description",
                "type": "Edm.String",
                "searchable": true,
                "filterable": false,
                "sortable": false,
                "facetable": false,
                "analyzerName": "en.lucene"
            },
            {
                "name": "Description_fr",
                "type": "Edm.String",
                "searchable": true,
                "filterable": false,
                "sortable": false,
                "facetable": false,
                "analyzerName": "fr.lucene"
            },
            {
                "name": "Category",
                "type": "Edm.String",
                "searchable": true,
                "filterable": true,
                "sortable": true,
                "facetable": true
            },
            {
                "name": "Tags",
                "type": "Collection(Edm.String)",
                "searchable": true,
                "filterable": true,
                "sortable": false,
                "facetable": true
            },
            {
                "name": "ParkingIncluded",
                "type": "Edm.Boolean",
                "filterable": true,
                "sortable": true,
                "facetable": true
            },
            {
                "name": "LastRenovationDate",
                "type": "Edm.DateTimeOffset",
                "filterable": true,
                "sortable": true,
                "facetable": true
            },
            {
                "name": "Rating",
                "type": "Edm.Double",
                "filterable": true,
                "sortable": true,
                "facetable": true
            },
            {
                "name": "Address",
                "type": "Edm.ComplexType",
                "fields": [
                    {
                        "name": "StreetAddress",
                        "type": "Edm.String",
                        "filterable": false,
                        "sortable": false,
                        "facetable": false,
                        "searchable": true
                    },
                    {
                        "name": "City",
                        "type": "Edm.String",
                        "searchable": true,
                        "filterable": true,
                        "sortable": true,
                        "facetable": true
                    },
                    {
                        "name": "StateProvince",
                        "type": "Edm.String",
                        "searchable": true,
                        "filterable": true,
                        "sortable": true,
                        "facetable": true
                    },
                    {
                        "name": "PostalCode",
                        "type": "Edm.String",
                        "searchable": true,
                        "filterable": true,
                        "sortable": true,
                        "facetable": true
                    },
                    {
                        "name": "Country",
                        "type": "Edm.String",
                        "searchable": true,
                        "filterable": true,
                        "sortable": true,
                        "facetable": true
                    }
                ]
            }
        ],
        "suggesters": [
            {
                "name": "sg",
                "searchMode": "analyzingInfixMatching",
                "sourceFields": [
                    "HotelName"
                ]
            }
        ]
    };
    async function main() {
        // Your search service endpoint
        const searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
        // Use the recommended keyless credential instead of the AzureKeyCredential credential.
        const credential = new DefaultAzureCredential();
        //const credential = new AzureKeyCredential(Your search service admin key);
        // Create a SearchIndexClient to send create/delete index commands
        const searchIndexClient = new SearchIndexClient(searchServiceEndpoint, credential);
        // Creating a search client to upload documents and issue queries
        const indexName = "hotels-quickstart";
        const searchClient = searchIndexClient.getSearchClient(indexName);
        console.log('Checking if index exists...');
        await deleteIndexIfExists(searchIndexClient, indexName);
        console.log('Creating index...');
        let index = await searchIndexClient.createIndex(indexDefinition);
        console.log(`Index named ${index.name} has been created.`);
        console.log('Uploading documents...');
        let indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotelData['value']);
        console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)} `);
        // waiting one second for indexing to complete (for demo purposes only)
        sleep(1000);
        console.log('Querying the index...');
        console.log();
        await sendQueries(searchClient);
    }
    async function deleteIndexIfExists(searchIndexClient, indexName) {
        try {
            await searchIndexClient.deleteIndex(indexName);
            console.log('Deleting index...');
        }
        catch {
            console.log('Index does not exist yet.');
        }
    }
    async function sendQueries(searchClient) {
        // Query 1
        console.log('Query #1 - search everything:');
        let searchOptions = {
            includeTotalCount: true,
            select: ["HotelId", "HotelName", "Rating"]
        };
        let searchResults = await searchClient.search("*", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log(`Result count: ${searchResults.count}`);
        console.log();
        // Query 2
        console.log('Query #2 - search with filter, orderBy, and select:');
        let state = 'FL';
        searchOptions = {
            filter: odata `Address/StateProvince eq ${state}`,
            orderBy: ["Rating desc"],
            select: ["HotelId", "HotelName", "Rating"]
        };
        searchResults = await searchClient.search("wifi", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
        // Query 3
        console.log('Query #3 - limit searchFields:');
        searchOptions = {
            select: ["HotelId", "HotelName", "Rating"],
            searchFields: ["HotelName"]
        };
        searchResults = await searchClient.search("sublime cliff", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
        // Query 4
        console.log('Query #4 - limit searchFields and use facets:');
        searchOptions = {
            facets: ["Category"],
            select: ["HotelId", "HotelName", "Rating"],
            searchFields: ["HotelName"]
        };
        searchResults = await searchClient.search("*", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
        // Query 5
        console.log('Query #5 - Lookup document:');
        let documentResult = await searchClient.getDocument('3');
        console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`);
        console.log();
    }
    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    main().catch((err) => {
        console.error("The sample encountered an error:", err);
    });
    
  2. Cree un archivo con el nombre hotels.json y pegue el siguiente código en hotels.json:

    {
        "value": [
            {
                "HotelId": "1",
                "HotelName": "Secret Point Motel",
                "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.",
                "Description_fr": "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": ["pool", "air conditioning", "concierge"],
                "ParkingIncluded": false,
                "LastRenovationDate": "1970-01-18T00:00:00Z",
                "Rating": 3.6,
                "Address": {
                    "StreetAddress": "677 5th Ave",
                    "City": "New York",
                    "StateProvince": "NY",
                    "PostalCode": "10022"
                }
            },
            {
                "HotelId": "2",
                "HotelName": "Twin Dome Motel",
                "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.",
                "Description_fr": "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": ["pool", "free wifi", "concierge"],
                "ParkingIncluded": "false",
                "LastRenovationDate": "1979-02-18T00:00:00Z",
                "Rating": 3.6,
                "Address": {
                    "StreetAddress": "140 University Town Center Dr",
                    "City": "Sarasota",
                    "StateProvince": "FL",
                    "PostalCode": "34243"
                }
            },
            {
                "HotelId": "3",
                "HotelName": "Triple 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.",
                "Description_fr": "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": ["air conditioning", "bar", "continental breakfast"],
                "ParkingIncluded": "true",
                "LastRenovationDate": "2015-09-20T00:00:00Z",
                "Rating": 4.8,
                "Address": {
                    "StreetAddress": "3393 Peachtree Rd",
                    "City": "Atlanta",
                    "StateProvince": "GA",
                    "PostalCode": "30326"
                }
            },
            {
                "HotelId": "4",
                "HotelName": "Sublime Cliff Hotel",
                "Description": "Sublime Cliff 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 Cliff is part of a lovingly restored 1800 palace.",
                "Description_fr": "Le sublime Cliff 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 Cliff fait partie d'un Palace 1800 restauré avec amour.",
                "Category": "Boutique",
                "Tags": ["concierge", "view", "24-hour front desk service"],
                "ParkingIncluded": true,
                "LastRenovationDate": "1960-02-06T00:00:00Z",
                "Rating": 4.6,
                "Address": {
                    "StreetAddress": "7400 San Pedro Ave",
                    "City": "San Antonio",
                    "StateProvince": "TX",
                    "PostalCode": "78216"
                }
            }
        ]
    }
    
  3. Cree un archivo con el nombre hotels_quickstart_index.json y pegue el siguiente código en hotels_quickstart_index.json:

    {
    	"name": "hotels-quickstart",
    	"fields": [
    		{
    			"name": "HotelId",
    			"type": "Edm.String",
    			"key": true,
    			"filterable": true
    		},
    		{
    			"name": "HotelName",
    			"type": "Edm.String",
    			"searchable": true,
    			"filterable": false,
    			"sortable": true,
    			"facetable": false
    		},
    		{
    			"name": "Description",
    			"type": "Edm.String",
    			"searchable": true,
    			"filterable": false,
    			"sortable": false,
    			"facetable": false,
    			"analyzerName": "en.lucene"
    		},
    		{
    			"name": "Description_fr",
    			"type": "Edm.String",
    			"searchable": true,
    			"filterable": false,
    			"sortable": false,
    			"facetable": false,
    			"analyzerName": "fr.lucene"
    		},
    		{
    			"name": "Category",
    			"type": "Edm.String",
    			"searchable": true,
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "Tags",
    			"type": "Collection(Edm.String)",
    			"searchable": true,
    			"filterable": true,
    			"sortable": false,
    			"facetable": true
    		},
    		{
    			"name": "ParkingIncluded",
    			"type": "Edm.Boolean",
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "LastRenovationDate",
    			"type": "Edm.DateTimeOffset",
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "Rating",
    			"type": "Edm.Double",
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "Address",
    			"type": "Edm.ComplexType",
    			"fields": [
    				{
    					"name": "StreetAddress",
    					"type": "Edm.String",
    					"filterable": false,
    					"sortable": false,
    					"facetable": false,
    					"searchable": true
    				},
    				{
    					"name": "City",
    					"type": "Edm.String",
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				},
    				{
    					"name": "StateProvince",
    					"type": "Edm.String",
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				},
    				{
    					"name": "PostalCode",
    					"type": "Edm.String",
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				},
    				{
    					"name": "Country",
    					"type": "Edm.String",
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				}
    			]
    		}
    	],
    	"suggesters": [
    		{
    			"name": "sg",
    			"searchMode": "analyzingInfixMatching",
    			"sourceFields": [
    				"HotelName"
    			]
    		}
    	]
    }
    
  4. Inicie sesión en Azure con el siguiente comando:

    az login
    
  5. Ejecute el código JavaScript con el siguiente comando:

    node index.js
    

Explicación del código

Crear índice

El archivo hotels_quickstart_index.json define cómo funciona la Búsqueda de Azure AI con los documentos que cargue en el siguiente paso. Cada campo se identifica con name y tiene un type específico. Cada campo también tiene una serie de atributos del índice que especifican si Azure AI Search puede buscar, filtrar, ordenar y cambiar las facetas del campo. La mayoría de los campos son tipos de datos simples; pero algunos, como AddressType, son tipos complejos que le permiten crear estructuras de datos enriquecidos en el índice. Puede leer más sobre los tipos de datos admitidos y los atributos de índice descritos en Creación de un índice (REST).

Con la definición del índice implementada, hay que importar hotels_quickstart_index.json al principio de index.js, con el fin de que la función principal pueda acceder a la definición del índice.

const indexDefinition = require('./hotels_quickstart_index.json');

Dentro de la función principal, se crea SearchIndexClient, que se usa para crear y administrar los índices de Azure AI Search.

const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));

Luego, se puede eliminar el índice si ya existe. Esta operación es una práctica común para el código de prueba o demostración.

Para ello, se define una función simple que intenta eliminar el índice.

async function deleteIndexIfExists(indexClient, indexName) {
    try {
        await indexClient.deleteIndex(indexName);
        console.log('Deleting index...');
    } catch {
        console.log('Index does not exist yet.');
    }
}

Para ejecutar la función, se extrae el nombre del índice de la definición del índice y se pasa indexName, junto con indexClient, a la función deleteIndexIfExists().

const indexName = indexDefinition["name"];

console.log('Checking if index exists...');
await deleteIndexIfExists(indexClient, indexName);

Después, se puede crear el índice con el método createIndex().

console.log('Creating index...');
let index = await indexClient.createIndex(indexDefinition);

console.log(`Index named ${index.name} has been created.`);

Carga de documentos

En Azure AI Search, los documentos son estructuras de datos que son tanto entradas para la indexación como salidas de las consultas. Estos datos se pueden insertar en el índice, o bien se puede usar un indexador. En ese caso, los documentos se insertarán mediante programación en el índice.

Las entradas de documentos pueden ser filas de una base de datos, blobs en Blob Storage o, como en este ejemplo, documentos JSON en el disco. De forma parecida a como se hizo con indexDefinition, también es preciso importar hotels.json al principio de index.js, con el fin de que se pueda acceder a los datos en la función principal.

const hotelData = require('./hotels.json');

Para indexar los datos en el índice de búsqueda, ahora es preciso crear SearchClient. Mientras que SearchIndexClient se usa para crear y administrar un índice, SearchClient se usa para cargar documentos y realizar consultas en el índice.

Hay dos formas de crear una SearchClient. La primera opción consiste en crear SearchClient desde cero:

 const searchClient = new SearchClient(endpoint, indexName, new AzureKeyCredential(apiKey));

Como alternativa, puede utilizar el método getSearchClient() de SearchIndexClient para crear SearchClient:

const searchClient = indexClient.getSearchClient(indexName);

Ahora que el cliente está definido, cargue los documentos en el índice de búsqueda. En este caso, usamos el método mergeOrUploadDocuments(), que carga los documentos o los combina con un documento existente si ya existe un documento con la misma clave.

console.log('Uploading documents...');
let indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotelData['value']);

console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);

Búsqueda de un índice

Una vez que haya creado un índice y se hayan cargado los documentos, estará listo para enviarle consultas. En esta sección, enviamos cinco consultas diferentes al índice de búsqueda para mostrar diferentes partes de la funcionalidad de consulta que tiene a su disposición.

Las consultas se escriben en una función sendQueries(), a la que llamamos en la función principal como se indica a continuación:

await sendQueries(searchClient);

Para enviar las consultas, se usa el método search() de searchClient. El primer parámetro es el texto de búsqueda y el segundo parámetro especifica opciones de búsqueda.

Ejemplo de consulta 1

La primera consulta busca *, lo que equivale a buscar todo y seleccionar tres de los campos del índice. Es aconsejable usar select solo en los campos en que sea necesario, ya que la extracción de datos innecesarios puede agregar latencia a las consultas.

En esta consulta, searchOptions también tiene includeTotalCount establecido en true, lo que devuelve el número de resultados coincidentes que se han encontrado.

async function sendQueries(searchClient) {
    console.log('Query #1 - search everything:');
    let searchOptions = {
        includeTotalCount: true,
        select: ["HotelId", "HotelName", "Rating"]
    };

    let searchResults = await searchClient.search("*", searchOptions);
    for await (const result of searchResults.results) {
        console.log(`${JSON.stringify(result.document)}`);
    }
    console.log(`Result count: ${searchResults.count}`);

    // remaining queries go here
}

Las restantes consultas que se describen a continuación también se deben agregar a la función sendQueries(). Aquí aparecen separadas para mejorar la legibilidad.

Ejemplo de consulta 2

En la siguiente consulta, se especifica el término de búsqueda "wifi" y también se incluye un filtro para que se devuelvan solo los resultados en los que el estado sea igual a 'FL'. Los resultados también se ordenan por el valor de Rating del hotel.

console.log('Query #2 - Search with filter, orderBy, and select:');
let state = 'FL';
searchOptions = {
    filter: odata`Address/StateProvince eq ${state}`,
    orderBy: ["Rating desc"],
    select: ["HotelId", "HotelName", "Rating"]
};

searchResults = await searchClient.search("wifi", searchOptions);
for await (const result of searchResults.results) {
    console.log(`${JSON.stringify(result.document)}`);
}

Ejemplo de consulta 3

Luego, la búsqueda se limita a un único campo mediante el uso del parámetro searchFields. Este enfoque es una excelente opción si desea que la consulta sea más eficaz, pero solo si sabe que le interesan las coincidencias de determinados campos.

console.log('Query #3 - Limit searchFields:');
searchOptions = {
    select: ["HotelId", "HotelName", "Rating"],
    searchFields: ["HotelName"]
};

searchResults = await searchClient.search("Sublime Palace", searchOptions);
for await (const result of searchResults.results) {
    console.log(`${JSON.stringify(result.document)}`);
}
console.log();

Ejemplo de consulta 4

Otra opción común que se incluye en las consultas es facets. Las facetas le permiten crear filtros en la interfaz de usuario, con el fin de que los usuarios sepan fácilmente por qué valores pueden filtrar.

console.log('Query #4 - Use facets:');
searchOptions = {
    facets: ["Category"],
    select: ["HotelId", "HotelName", "Rating"],
    searchFields: ["HotelName"]
};

searchResults = await searchClient.search("*", searchOptions);
for await (const result of searchResults.results) {
    console.log(`${JSON.stringify(result.document)}`);
}

Ejemplo de consulta 5

La consulta final utiliza el método getDocument() de searchClient. Esto le permite recuperar de forma eficaz un documento por su clave.

console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument(key='3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)

Resumen de consultas

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

La búsqueda de texto completo y los filtros se realizan usando 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.

Aprenda a usar la biblioteca cliente Azure.Search.Documents 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.

Esta guía de inicio rápido crea y consulta un pequeño índice de inicio rápido de hoteles que contiene datos sobre cuatro hoteles.

Sugerencia

Puede descargar y ejecutar un cuaderno terminado.

Requisitos previos

Requisitos previos de Microsoft Entra ID

Para la autenticación sin clave recomendada con Microsoft Entra ID, debe hacer lo siguiente:

  • Instale la CLI de Azure utilizada para la autenticación sin clave con Microsoft Entra ID.
  • Asigne los roles Search Service Contributor y Search Index Data Contributor a su cuenta de usuario. Puede asignar roles en Azure Portal en Control de acceso (IAM)>Agregar asignación de roles. Para más información, consulte Conexión a la Búsqueda de Azure AI usando los roles.

Recuperación de información de recursos

Debe recuperar la siguiente información para autenticar la aplicación con el servicio de Búsqueda de Azure AI:

Nombre de la variable Valor
SEARCH_API_ENDPOINT Este valor se puede encontrar en Azure Portal. Seleccione el servicio de búsqueda y, a continuación, en el menú de la izquierda, seleccione Información general. El valor de URL en Esenciales es el punto de conexión que necesita. Un punto de conexión de ejemplo podría ser similar a https://mydemo.search.windows.net.

Obtenga más información sobre la autenticación sin claves y la configuración de variables de entorno.

Configurar el entorno

Ejecute el código de ejemplo en un cuaderno de Jupyter Notebook. Por lo tanto, debe configurar su entorno para ejecutar cuadernos de Jupyter Notebook.

  1. Descargue o copie el cuaderno de ejemplo de GitHub.

  2. Abra el cuaderno en Visual Studio Code.

  3. Cree un nuevo entorno de Python para usarlo en la instalación de los paquetes que necesita para este tutorial.

    Importante

    No instale paquetes en la instalación global de Python. Siempre debe usar un entorno virtual o conda al instalar paquetes de Python; de lo contrario, puede interrumpir la instalación global de Python.

    py -3 -m venv .venv
    .venv\scripts\activate
    

    Puede tardar un minuto en configurarse. Si tiene problemas, consulte Entornos de Python en VS Code.

  4. Instale los cuadernos de Jupyter Notebook y el kernel de IPython para cuadernos de Jupyter si aún no los tiene.

    pip install jupyter
    pip install ipykernel
    python -m ipykernel install --user --name=.venv
    
  5. Seleccione el kernel del cuaderno.

    1. En la parte superior derecha del cuaderno, seleccione Seleccionar kernel.
    2. Si ve .venv en la lista, selecciónelo. Si no lo ve, seleccione Seleccionar otro kernel>Entornos de Python>.venv.

Creación, carga y consulta de un índice de búsqueda

En esta sección, añadirá código para crear un índice de búsqueda, cargarlo con documentos y ejecutar consultas. Ejecute el programa para ver los resultados en la consola. Para una explicación detallada del código, consulte la sección explicación del código.

  1. Asegúrese de que el cuaderno está abierto en el kernel .venv como se describe en la sección anterior.

  2. Ejecute la primera celda de código para instalar los paquetes necesarios, incluido azure-search-documents.

    ! pip install azure-search-documents==11.6.0b1 --quiet
    ! pip install azure-identity --quiet
    ! pip install python-dotenv --quiet
    
  3. Reemplace el contenido de la segunda celda de código con el siguiente código, según su método de autenticación.

    Nota:

    En este inicio rápido, el código de ejemplo usa Microsoft Entra ID para la autenticación sin clave recomendada. Si prefiere usar una clave de API, puede reemplazar el objeto DefaultAzureCredential por un objeto AzureKeyCredential.

    from azure.core.credentials import AzureKeyCredential
    from azure.identity import DefaultAzureCredential, AzureAuthorityHosts
    
    search_endpoint: str = "https://<Put your search service NAME here>.search.windows.net/"
    authority = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD
    credential = DefaultAzureCredential(authority=authority)
    
    index_name: str = "hotels-quickstart-python"
    
  4. Quite las dos líneas siguientes de la celda de código Crear un índice. Las credenciales ya están establecidas en la celda de código anterior.

    from azure.core.credentials import AzureKeyCredential
    credential = AzureKeyCredential(search_api_key)
    
  5. Ejecute la celda de código Crear un índice para crear un índice de búsqueda.

  6. Ejecute las celdas de código restantes de forma secuencial para cargar documentos y ejecutar consultas.

Explicación del código

Creación de un índice

SearchIndexClient se usa para crear y administrar índices para la Búsqueda de Azure AI. Cada campo se identifica con name y tiene un type específico.

Cada campo también tiene una serie de atributos del índice que especifican si Azure AI Search puede buscar, filtrar, ordenar y cambiar las facetas del campo. La mayoría de los campos son tipos de datos simples; pero algunos, como AddressType, son tipos complejos que le permiten crear estructuras de datos enriquecidos en el índice. Puede leer más sobre los tipos de datos admitidos y los atributos de índice descritos en Creación de un índice (REST).

Creación de una carga de documentos y carga de documentos

Use una acción de índice para el tipo de operación, como cargar o combinar y cargar. Los documentos se originan en el ejemplo de HotelsData en GitHub.

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.

Use el método search de la clase search.client.

Las consultas de ejemplo del cuaderno son:

  • Consulta básica: ejecuta una búsqueda vacía (search=*), que devuelve una lista no clasificada (puntuación de búsqueda = 1,0) de documentos arbitrarios. Dado que no hay ningún criterio, en los resultados se incluyen todos los documentos.
  • Consulta de términos: agrega términos completos a la expresión de búsqueda ("wifi"). Esta consulta especifica que los resultados contienen solo los campos de la instrucción select. La limitación de los campos que se devuelven reduce la cantidad de datos que se envían mediante la conexión y reduce la latencia de búsqueda.
  • Consulta filtrada: agrega una expresión de filtro que devuelva solo aquellos hoteles con una clasificación superior a cuatro en orden descendente.
  • Ámbito con campos: agrega search_fields a la ejecución de consultas de ámbito a campos específicos.
  • Facetas: genera facetas para las coincidencias positivas encontradas en los resultados de búsqueda. No hay ninguna coincidencia. Si los resultados de la búsqueda no incluyen el término wifi, wifi no aparece en la estructura de navegación por facetas.
  • Buscar un documento: devuelve un documento basado en su clave. Esta operación es útil si desea proporcionar detalles cuando un usuario selecciona un elemento en un resultado de búsqueda.
  • Autocompletar: proporciona posibles coincidencias como los tipos de usuario en el cuadro de búsqueda. Autocompletar usa un proveedor de sugerencias (sg) para saber qué campos contienen posibles coincidencias con las solicitudes del proveedor de sugerencias. En este inicio rápido, esos campos son Tags, Address/City, Address/Country. Para simular la función autocompletar, pase las letras sa como cadena parcial. El método autocompletar de SearchClient devuelve posibles coincidencias de términos.

Quitar el índice

Si ha terminado con este índice, puede eliminarlo ejecutando la celda de código Limpiar. Eliminar índices innecesarios libera espacio para recorrer más inicios rápidos y tutoriales.

Aprenda a usar la biblioteca cliente Azure.Search.Documents 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.

Esta guía de inicio rápido crea y consulta un pequeño índice de inicio rápido de hoteles que contiene datos sobre cuatro hoteles.

Sugerencia

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.

Requisitos previos

Requisitos previos de Microsoft Entra ID

Para la autenticación sin clave recomendada con Microsoft Entra ID, debe hacer lo siguiente:

  • Instale la CLI de Azure utilizada para la autenticación sin clave con Microsoft Entra ID.
  • Asigne los roles Search Service Contributor y Search Index Data Contributor a su cuenta de usuario. Puede asignar roles en Azure Portal en Control de acceso (IAM)>Agregar asignación de roles. Para más información, consulte Conexión a la Búsqueda de Azure AI usando los roles.

Recuperación de información de recursos

Debe recuperar la siguiente información para autenticar la aplicación con el servicio de Búsqueda de Azure AI:

Nombre de la variable Valor
SEARCH_API_ENDPOINT Este valor se puede encontrar en Azure Portal. Seleccione el servicio de búsqueda y, a continuación, en el menú de la izquierda, seleccione Información general. El valor de URL en Esenciales es el punto de conexión que necesita. Un punto de conexión de ejemplo podría ser similar a https://mydemo.search.windows.net.

Obtenga más información sobre la autenticación sin claves y la configuración de variables de entorno.

Configurar

  1. Cree una nueva carpeta full-text-quickstart para que contenga la aplicación y abra Visual Studio Code en esa carpeta con el siguiente comando:

    mkdir full-text-quickstart && cd full-text-quickstart
    
  2. Cree el package.json con el comando siguiente:

    npm init -y
    
  3. Actualice el package.json a ECMAScript con el siguiente comando:

    npm pkg set type=module
    
  4. Instale la biblioteca cliente de Búsqueda de Azure AI (Azure.Search.Documents) para JavaScript con:

    npm install @azure/search-documents
    
  5. Para la autenticación sin contraseña recomendada, instale la biblioteca cliente de Azure Identity con:

    npm install @azure/identity
    

Creación, carga y consulta de un índice de búsqueda

En la sección de configuración anterior, instaló la biblioteca cliente de Búsqueda de Azure AI y otras dependencias.

En esta sección, añadirá código para crear un índice de búsqueda, cargarlo con documentos y ejecutar consultas. Ejecute el programa para ver los resultados en la consola. Para una explicación detallada del código, consulte la sección explicación del código.

En este inicio rápido, el código de ejemplo usa Microsoft Entra ID para la autenticación sin clave recomendada. Si prefiere usar una clave de API, puede reemplazar el objeto DefaultAzureCredential por un objeto AzureKeyCredential.

const searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
const credential = new DefaultAzureCredential();
  1. Cree un nuevo archivo con el nombre index.ts y pegue el siguiente código en index.ts:

    // Import from the @azure/search-documents library
    import {
        SearchIndexClient,
        SearchClient,
        SearchFieldDataType,
        AzureKeyCredential,
        odata,
        SearchIndex
    } from "@azure/search-documents";
    
    // Import from the Azure Identity library
    import { DefaultAzureCredential } from "@azure/identity";
    
    // Importing the hotels sample data
    import hotelData from './hotels.json' assert { type: "json" };
    
    // Load the .env file if it exists
    import * as dotenv from "dotenv";
    dotenv.config();
    
    // Defining the index definition
    const indexDefinition: SearchIndex = {
    	"name": "hotels-quickstart",
    	"fields": [
    		{
    			"name": "HotelId",
    			"type": "Edm.String" as SearchFieldDataType,
    			"key": true,
    			"filterable": true
    		},
    		{
    			"name": "HotelName",
    			"type": "Edm.String" as SearchFieldDataType,
    			"searchable": true,
    			"filterable": false,
    			"sortable": true,
    			"facetable": false
    		},
    		{
    			"name": "Description",
    			"type": "Edm.String" as SearchFieldDataType,
    			"searchable": true,
    			"filterable": false,
    			"sortable": false,
    			"facetable": false,
    			"analyzerName": "en.lucene"
    		},
    		{
    			"name": "Description_fr",
    			"type": "Edm.String" as SearchFieldDataType,
    			"searchable": true,
    			"filterable": false,
    			"sortable": false,
    			"facetable": false,
    			"analyzerName": "fr.lucene"
    		},
    		{
    			"name": "Category",
    			"type": "Edm.String" as SearchFieldDataType,
    			"searchable": true,
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "Tags",
    			"type": "Collection(Edm.String)",
    			"searchable": true,
    			"filterable": true,
    			"sortable": false,
    			"facetable": true
    		},
    		{
    			"name": "ParkingIncluded",
    			"type": "Edm.Boolean",
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "LastRenovationDate",
    			"type": "Edm.DateTimeOffset",
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "Rating",
    			"type": "Edm.Double",
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "Address",
    			"type": "Edm.ComplexType",
    			"fields": [
    				{
    					"name": "StreetAddress",
    					"type": "Edm.String" as SearchFieldDataType,
    					"filterable": false,
    					"sortable": false,
    					"facetable": false,
    					"searchable": true
    				},
    				{
    					"name": "City",
    					"type": "Edm.String" as SearchFieldDataType,
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				},
    				{
    					"name": "StateProvince",
    					"type": "Edm.String" as SearchFieldDataType,
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				},
    				{
    					"name": "PostalCode",
    					"type": "Edm.String" as SearchFieldDataType,
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				},
    				{
    					"name": "Country",
    					"type": "Edm.String" as SearchFieldDataType,
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				}
    			]
    		}
    	],
    	"suggesters": [
    		{
    			"name": "sg",
    			"searchMode": "analyzingInfixMatching",
    			"sourceFields": [
    				"HotelName"
    			]
    		}
    	]
    };
    
    async function main() {
    
    	// Your search service endpoint
    	const searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
    
    	// Use the recommended keyless credential instead of the AzureKeyCredential credential.
    	const credential = new DefaultAzureCredential();
    	//const credential = new AzureKeyCredential(Your search service admin key);
    
    	// Create a SearchIndexClient to send create/delete index commands
    	const searchIndexClient: SearchIndexClient = new SearchIndexClient(
    		searchServiceEndpoint,
    		credential
    	);
    
    	// Creating a search client to upload documents and issue queries
    	const indexName: string  = "hotels-quickstart";
        const searchClient: SearchClient<any> = searchIndexClient.getSearchClient(indexName);
    
        console.log('Checking if index exists...');
        await deleteIndexIfExists(searchIndexClient, indexName);
    
        console.log('Creating index...');
        let index: SearchIndex = await searchIndexClient.createIndex(indexDefinition);
        console.log(`Index named ${index.name} has been created.`);
    
        console.log('Uploading documents...');
        let indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotelData['value']);
        console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)} `);
    
        // waiting one second for indexing to complete (for demo purposes only)
        sleep(1000);
    
        console.log('Querying the index...');
        console.log();
        await sendQueries(searchClient);
    }
    
    async function deleteIndexIfExists(searchIndexClient: SearchIndexClient, indexName: string) {
        try {
            await searchIndexClient.deleteIndex(indexName);
            console.log('Deleting index...');
        } catch {
            console.log('Index does not exist yet.');
        }
    }
    
    async function sendQueries(searchClient: SearchClient<any>) {
        // Query 1
        console.log('Query #1 - search everything:');
        let searchOptions: any = {
            includeTotalCount: true,
            select: ["HotelId", "HotelName", "Rating"]
        };
    
        let searchResults = await searchClient.search("*", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log(`Result count: ${searchResults.count}`);
        console.log();
    
    
        // Query 2
        console.log('Query #2 - search with filter, orderBy, and select:');
        let state = 'FL';
        searchOptions = {
            filter: odata`Address/StateProvince eq ${state}`,
            orderBy: ["Rating desc"],
            select: ["HotelId", "HotelName", "Rating"]
        };
    
        searchResults = await searchClient.search("wifi", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
    
        // Query 3
        console.log('Query #3 - limit searchFields:');
        searchOptions = {
            select: ["HotelId", "HotelName", "Rating"],
            searchFields: ["HotelName"]
        };
    
        searchResults = await searchClient.search("sublime cliff", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
    
        // Query 4
        console.log('Query #4 - limit searchFields and use facets:');
        searchOptions = {
            facets: ["Category"],
            select: ["HotelId", "HotelName", "Rating"],
            searchFields: ["HotelName"]
        };
    
        searchResults = await searchClient.search("*", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
    
        // Query 5
        console.log('Query #5 - Lookup document:');
        let documentResult = await searchClient.getDocument('3');
        console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`);
        console.log();
    }
    
    function sleep(ms: number) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    
    main().catch((err) => {
        console.error("The sample encountered an error:", err);
    });
    
  2. Cree un archivo con el nombre hotels.json y pegue el siguiente código en hotels.json:

    {
        "value": [
            {
                "HotelId": "1",
                "HotelName": "Secret Point Motel",
                "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.",
                "Description_fr": "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": ["pool", "air conditioning", "concierge"],
                "ParkingIncluded": false,
                "LastRenovationDate": "1970-01-18T00:00:00Z",
                "Rating": 3.6,
                "Address": {
                    "StreetAddress": "677 5th Ave",
                    "City": "New York",
                    "StateProvince": "NY",
                    "PostalCode": "10022"
                }
            },
            {
                "HotelId": "2",
                "HotelName": "Twin Dome Motel",
                "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.",
                "Description_fr": "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": ["pool", "free wifi", "concierge"],
                "ParkingIncluded": "false",
                "LastRenovationDate": "1979-02-18T00:00:00Z",
                "Rating": 3.6,
                "Address": {
                    "StreetAddress": "140 University Town Center Dr",
                    "City": "Sarasota",
                    "StateProvince": "FL",
                    "PostalCode": "34243"
                }
            },
            {
                "HotelId": "3",
                "HotelName": "Triple 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.",
                "Description_fr": "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": ["air conditioning", "bar", "continental breakfast"],
                "ParkingIncluded": "true",
                "LastRenovationDate": "2015-09-20T00:00:00Z",
                "Rating": 4.8,
                "Address": {
                    "StreetAddress": "3393 Peachtree Rd",
                    "City": "Atlanta",
                    "StateProvince": "GA",
                    "PostalCode": "30326"
                }
            },
            {
                "HotelId": "4",
                "HotelName": "Sublime Cliff Hotel",
                "Description": "Sublime Cliff 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 Cliff is part of a lovingly restored 1800 palace.",
                "Description_fr": "Le sublime Cliff 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 Cliff fait partie d'un Palace 1800 restauré avec amour.",
                "Category": "Boutique",
                "Tags": ["concierge", "view", "24-hour front desk service"],
                "ParkingIncluded": true,
                "LastRenovationDate": "1960-02-06T00:00:00Z",
                "Rating": 4.6,
                "Address": {
                    "StreetAddress": "7400 San Pedro Ave",
                    "City": "San Antonio",
                    "StateProvince": "TX",
                    "PostalCode": "78216"
                }
            }
        ]
    }
    
  3. Cree el archivo tsconfig.json para transpilar el código TypeScript y copie el código siguiente para ECMAScript.

    {
        "compilerOptions": {
          "module": "NodeNext",
          "target": "ES2022", // Supports top-level await
          "moduleResolution": "NodeNext",
          "skipLibCheck": true, // Avoid type errors from node_modules
          "strict": true // Enable strict type-checking options
        },
        "include": ["*.ts"]
    }
    
  4. Transpile de TypeScript a JavaScript.

    tsc
    
  5. Inicie sesión en Azure con el siguiente comando:

    az login
    
  6. Ejecute el código JavaScript con el siguiente comando:

    node index.js
    

Explicación del código

Crear índice

Cree el archivo hotels_quickstart_index.json. Este archivo define cómo funciona la Búsqueda de Azure AI con los documentos que cargue en el siguiente paso. Cada campo se identifica con name y tiene un type específico. Cada campo también tiene una serie de atributos del índice que especifican si Azure AI Search puede buscar, filtrar, ordenar y cambiar las facetas del campo. La mayoría de los campos son tipos de datos simples; pero algunos, como AddressType, son tipos complejos que le permiten crear estructuras de datos enriquecidos en el índice. Puede leer más sobre los tipos de datos admitidos y los atributos de índice descritos en Creación de un índice (REST).

Queremos importar hotels_quickstart_index.json para que la función principal pueda acceder a la definición del índice.

import indexDefinition from './hotels_quickstart_index.json';

interface HotelIndexDefinition {
    name: string;
    fields: SimpleField[] | ComplexField[];
    suggesters: SearchSuggester[];
};
const hotelIndexDefinition: HotelIndexDefinition = indexDefinition as HotelIndexDefinition;

Dentro de la función principal, se crea SearchIndexClient, que se usa para crear y administrar los índices de Azure AI Search.

const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));

Luego, se puede eliminar el índice si ya existe. Esta operación es una práctica común para el código de prueba o demostración.

Para ello, se define una función simple que intenta eliminar el índice.

async function deleteIndexIfExists(indexClient: SearchIndexClient, indexName: string): Promise<void> {
    try {
        await indexClient.deleteIndex(indexName);
        console.log('Deleting index...');
    } catch {
        console.log('Index does not exist yet.');
    }
}

Para ejecutar la función, se extrae el nombre del índice de la definición del índice y se pasa indexName, junto con indexClient, a la función deleteIndexIfExists().

// Getting the name of the index from the index definition
const indexName: string = hotelIndexDefinition.name;

console.log('Checking if index exists...');
await deleteIndexIfExists(indexClient, indexName);

Después, se puede crear el índice con el método createIndex().

console.log('Creating index...');
let index = await indexClient.createIndex(hotelIndexDefinition);

console.log(`Index named ${index.name} has been created.`);

Carga de documentos

En Azure AI Search, los documentos son estructuras de datos que son tanto entradas para la indexación como salidas de las consultas. Estos datos se pueden insertar en el índice, o bien se puede usar un indexador. En ese caso, los documentos se insertarán mediante programación en el índice.

Las entradas de documentos pueden ser filas de una base de datos, blobs en Blob Storage o, como en este ejemplo, documentos JSON en el disco. Puede descargar hotels.json, o bien puede crear su propio archivo hotels.json con el contenido siguiente:

De forma similar a lo que hicimos con indexDefinition, también es necesario importar hotels.json en la parte superior de index.ts para que se pueda acceder a los datos en nuestra función principal.

import hotelData from './hotels.json';

interface Hotel {
    HotelId: string;
    HotelName: string;
    Description: string;
    Description_fr: string;
    Category: string;
    Tags: string[];
    ParkingIncluded: string | boolean;
    LastRenovationDate: string;
    Rating: number;
    Address: {
        StreetAddress: string;
        City: string;
        StateProvince: string;
        PostalCode: string;
    };
};

const hotels: Hotel[] = hotelData["value"];

Para indexar los datos en el índice de búsqueda, ahora es preciso crear SearchClient. Mientras que SearchIndexClient se usa para crear y administrar un índice, SearchClient se usa para cargar documentos y realizar consultas en el índice.

Hay dos formas de crear una SearchClient. La primera opción consiste en crear SearchClient desde cero:

 const searchClient = new SearchClient<Hotel>(endpoint, indexName, new AzureKeyCredential(apiKey));

Como alternativa, puede utilizar el método getSearchClient() de SearchIndexClient para crear SearchClient:

const searchClient = indexClient.getSearchClient<Hotel>(indexName);

Ahora que el cliente está definido, cargue los documentos en el índice de búsqueda. En este caso, usamos el método mergeOrUploadDocuments(), que carga los documentos o los combina con un documento existente si ya existe un documento con la misma clave. A continuación, compruebe que la operación se realizó correctamente porque existe al menos el primer documento.

console.log("Uploading documents...");
const indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotels);

console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);

Vuelva a ejecutar el programa con tsc && node index.ts. Verá un conjunto de mensajes ligeramente distintos de los que ha visto en el paso 1. Esta vez, el índice existe y debería ver un mensaje relativo a su eliminación antes de que la aplicación cree el nuevo y publique datos en él.

Antes de ejecutar las consultas en el paso siguiente, defina una función para que el programa espere un segundo. Esta operación se realiza con fines de demostración o prueba, con el fin de garantizar que la indexación finaliza y que los documentos están disponibles en el índice para nuestras consultas.

function sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
}

Para que el programa espere un segundo, llame a la función sleep:

sleep(1000);

Búsqueda de un índice

Una vez que haya creado un índice y se hayan cargado los documentos, estará listo para enviarle consultas. En esta sección, enviamos cinco consultas diferentes al índice de búsqueda para mostrar diferentes partes de la funcionalidad de consulta que tiene a su disposición.

Las consultas se escriben en una función sendQueries(), a la que llamamos en la función principal como se indica a continuación:

await sendQueries(searchClient);

Para enviar las consultas, se usa el método search() de searchClient. El primer parámetro es el texto de búsqueda y el segundo parámetro especifica opciones de búsqueda.

Ejemplo de consulta 1

La primera consulta busca *, lo que equivale a buscar todo y seleccionar tres de los campos del índice. Es aconsejable usar select solo en los campos en que sea necesario, ya que la extracción de datos innecesarios puede agregar latencia a las consultas.

En esta consulte, searchOptions también tiene includeTotalCount establecido en true, lo que devolverá el número de resultados coincidentes que se han encontrado.

async function sendQueries(
    searchClient: SearchClient<Hotel>
): Promise<void> {

    // Query 1
    console.log('Query #1 - search everything:');
    const selectFields: SearchFieldArray<Hotel> = [
        "HotelId",
        "HotelName",
        "Rating",
    ];
    const searchOptions1 = { 
        includeTotalCount: true, 
        select: selectFields 
    };

    let searchResults = await searchClient.search("*", searchOptions1);
    for await (const result of searchResults.results) {
        console.log(`${JSON.stringify(result.document)}`);
    }
    console.log(`Result count: ${searchResults.count}`);

    // remaining queries go here
}

Las restantes consultas que se describen a continuación también se deben agregar a la función sendQueries(). Aquí aparecen separadas para mejorar la legibilidad.

Ejemplo de consulta 2

En la siguiente consulta, se especifica el término de búsqueda "wifi" y también se incluye un filtro para que se devuelvan solo los resultados en los que el estado sea igual a 'FL'. Los resultados también se ordenan por el valor de Rating del hotel.

console.log('Query #2 - search with filter, orderBy, and select:');
let state = 'FL';
const searchOptions2 = {
    filter: odata`Address/StateProvince eq ${state}`,
    orderBy: ["Rating desc"],
    select: selectFields
};
searchResults = await searchClient.search("wifi", searchOptions2);
for await (const result of searchResults.results) {
    console.log(`${JSON.stringify(result.document)}`);
}

Ejemplo de consulta 3

Luego, la búsqueda se limita a un único campo mediante el uso del parámetro searchFields. Este enfoque es una excelente opción si desea que la consulta sea más eficaz, pero solo si sabe que le interesan las coincidencias de determinados campos.

console.log('Query #3 - limit searchFields:');
const searchOptions3 = {
    select: selectFields,
    searchFields: ["HotelName"] as const
};

searchResults = await searchClient.search("Sublime Palace", searchOptions3);
for await (const result of searchResults.results) {
    console.log(`${JSON.stringify(result.document)}`);
}

Ejemplo de consulta 4

Otra opción común que se incluye en las consultas es facets. Las facetas permiten proporcionar la exploración en profundidad autodireccional de los resultados en la interfaz de usuario. Los resultados de facetas se pueden convertir en casillas en el panel de resultados.

console.log('Query #4 - limit searchFields and use facets:');
const searchOptions4 = {
    facets: ["Category"],
    select: selectFields,
    searchFields: ["HotelName"] as const
};

searchResults = await searchClient.search("*", searchOptions4);
for await (const result of searchResults.results) {
    console.log(`${JSON.stringify(result.document)}`);
}

Ejemplo de consulta 5

La consulta final utiliza el método getDocument() de searchClient. Esto le permite recuperar de forma eficaz un documento por su clave.

console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument('3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)

Resumen de consultas

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

La búsqueda de texto completo y los filtros se realizan usando 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.

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 bien eliminar el grupo de recursos para eliminar todo el conjunto de recursos.

Puede encontrar y administrar recursos en Azure 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 Azure 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.