Guia de início rápido: pesquisa de texto completo usando os SDKs do Azure

Saiba como usar a biblioteca de cliente Azure.Search.Documents em um SDK do Azure para criar, carregar e consultar um índice de pesquisa usando dados de exemplo para pesquisa de texto completo. A pesquisa de texto completo usa o Apache Lucene para indexação e consultas, e um algoritmo de classificação BM25 para pontuar resultados.

Este guia de início rápido tem etapas para os seguintes SDKs:

Pré-requisitos

  • Uma conta do Azure com uma subscrição ativa. Crie uma conta gratuitamente.

  • Um serviço Azure AI Search. Crie um serviço se não tiver um. Você pode usar uma camada gratuita para este início rápido.

  • Uma chave de API e um ponto de extremidade de serviço. Entre no portal do Azure e encontre seu serviço de pesquisa.

    Em Visão geral, copie o URL e salve-o no Bloco de Notas para uma etapa posterior. Um ponto final de exemplo poderá ser parecido com https://mydemo.search.windows.net.

    Em Chaves, copie e salve uma chave de administrador para obter direitos completos para criar e excluir objetos. Existem duas chaves primárias e secundárias intercambiáveis. Escolha uma delas.

    Obter um ponto de extremidade HTTP e uma chave de acesso

Criar, carregar e consultar um índice

Escolha uma linguagem de programação para a próxima etapa. As bibliotecas de cliente Azure.Search.Documents estão disponíveis em SDKs do Azure para .NET, Python, Java e JavaScript.

Crie um aplicativo de console usando a biblioteca de cliente Azure.Search.Documents para criar, carregar e consultar um índice de pesquisa. Como alternativa, você pode baixar o código-fonte para começar com um projeto concluído ou seguir estas etapas para criar o seu próprio.

Configurar o ambiente

  1. Inicie o Visual Studio e crie um novo projeto para um aplicativo de console.

  2. Em Ferramentas>Gerenciador de Pacotes NuGet, selecione Gerenciar Pacotes NuGet para Solução....

  3. Selecione Procurar.

  4. Procure o pacote Azure.Search.Documents e selecione a versão 11.0 ou posterior.

  5. Selecione Instalar à direita para adicionar o assembly ao seu projeto e solução.

Criar um cliente de pesquisa

  1. No Program.cs, altere o namespace para AzureSearch.SDK.Quickstart.v11 e adicione as seguintes using diretivas.

    using Azure;
    using Azure.Search.Documents;
    using Azure.Search.Documents.Indexes;
    using Azure.Search.Documents.Indexes.Models;
    using Azure.Search.Documents.Models;
    
  2. Crie dois clientes: SearchIndexClient cria o índice e SearchClient carrega e consulta um índice existente. Ambos precisam do ponto de extremidade do serviço e de uma chave de API de administrador para autenticação com direitos de criação/exclusão.

    Como o código cria o URI para você, especifique apenas o nome do serviço de pesquisa na propriedade "serviceName".

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

Criar um índice

Este guia de início rápido cria um índice de hotéis que você carregará com os dados do hotel e executará consultas. Nesta etapa, defina os campos no índice. Cada definição de campo inclui um nome, tipo de dados e atributos que determinam como o campo é usado.

Neste exemplo, os métodos síncronos da biblioteca Azure.Search.Documents são usados para simplicidade e legibilidade. No entanto, para cenários de produção, você deve usar métodos assíncronos para manter seu aplicativo escalável e responsivo. Por exemplo, você usaria CreateIndexAsync em vez de CreateIndex.

  1. Adicione uma definição de classe vazia ao seu projeto: Hotel.cs

  2. Copie o código a seguir para Hotel.cs para definir a estrutura de um documento do hotel. Os atributos no campo determinam como ele é usado em um aplicativo. Por exemplo, o IsFilterable atributo deve ser atribuído a cada campo que ofereça suporte a uma expressão 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; }
        }
    }
    

    Na biblioteca de cliente Azure.Search.Documents, você pode usar SearchableField e SimpleField para simplificar as definições de campo. Ambos são derivados de um SearchField e podem simplificar seu código:

    • SimpleField pode ser qualquer tipo de dados, é sempre não pesquisável (é ignorado para consultas de pesquisa de texto completo) e é recuperável (não está oculto). Outros atributos estão desativados por padrão, mas podem ser habilitados. Você pode usar um SimpleField para IDs de documento ou campos usados apenas em filtros, facetas ou perfis de pontuação. Em caso afirmativo, certifique-se de aplicar todos os atributos necessários para o cenário, como IsKey = true para uma ID de documento. Para obter mais informações, consulte SimpleFieldAttribute.cs no código-fonte.

    • SearchableField deve ser uma cadeia de caracteres e é sempre pesquisável e recuperável. Outros atributos estão desativados por padrão, mas podem ser habilitados. Como esse tipo de campo é pesquisável, ele suporta sinônimos e o complemento completo das propriedades do analisador. Para obter mais informações, consulte o SearchableFieldAttribute.cs no código-fonte.

    Se você usa a API básica SearchField ou um dos modelos auxiliares, você deve habilitar explicitamente os atributos de filtro, faceta e classificação. Por exemplo, IsFilterable, IsSortable e IsFacetable devem ser explicitamente atribuídos, conforme mostrado no exemplo acima.

  3. Adicione uma segunda definição de classe vazia ao seu projeto: Address.cs. Copie o código a seguir para a classe.

    using Azure.Search.Documents.Indexes;
    
     namespace AzureSearch.Quickstart
     {
         public partial class Address
         {
             [SearchableField(IsFilterable = true)]
             public string StreetAddress { get; set; }
    
             [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
             public string City { get; set; }
    
             [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
             public string StateProvince { get; set; }
    
             [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
             public string PostalCode { get; set; }
    
             [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
             public string Country { get; set; }
         }
     }
    
  4. Crie mais duas classes: Hotel.Methods.cs e Address.Methods.cs para substituições ToString(). Essas classes são usadas para renderizar resultados de pesquisa na saída do console. O conteúdo dessas classes não é fornecido neste artigo, mas você pode copiar o código de arquivos no GitHub.

  5. No Program.cs, crie um objeto SearchIndex e chame o método CreateIndex para expressar o índice em seu serviço de pesquisa. O índice também inclui um SearchSuggester para habilitar o preenchimento automático nos campos especificados.

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

Carregar documentos

O Azure AI Search pesquisa sobre o conteúdo armazenado no serviço. Nesta etapa, você carregará documentos JSON que estejam em conformidade com o índice de hotéis que você acabou de criar.

No Azure AI Search, os documentos de pesquisa são estruturas de dados que são entradas para indexação e saídas de consultas. Conforme obtido de uma fonte de dados externa, as entradas de documentos podem ser linhas em um banco de dados, blobs no armazenamento de Blob ou documentos JSON no disco. Neste exemplo, estamos pegando um atalho e incorporando documentos JSON para quatro hotéis no próprio código.

Ao carregar documentos, você deve usar um objeto IndexDocumentsBatch . Um IndexDocumentsBatch objeto contém uma coleção de Ações, cada uma das quais contém um documento e uma propriedade informando ao Azure AI Search qual ação executar (carregar, mesclar, excluir e mesclarOrUpload).

  1. No Program.cs, crie uma matriz de documentos e ações de índice e, em seguida, passe a matriz para IndexDocumentsBatch. Os documentos abaixo estão em conformidade com o índice de início rápido de hotéis, conforme definido pela classe do 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 = "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}");
        }
    }
    

    Depois de inicializar o objeto IndexDocumentsBatch, você pode enviá-lo para o índice chamando IndexDocuments no objeto SearchClient.

  2. Adicione as seguintes linhas a Main(). O carregamento de documentos é feito usando SearchClient, mas a operação também requer direitos de administrador no serviço, que normalmente é associado a SearchIndexClient. Uma maneira de configurar essa operação é obter SearchClient através de SearchIndexClient (adminClient neste exemplo).

     SearchClient ingesterClient = adminClient.GetSearchClient(indexName);
    
     // Load documents
     Console.WriteLine("{0}", "Uploading documents...\n");
     UploadDocuments(ingesterClient);
    
  3. Como este é um aplicativo de console que executa todos os comandos sequencialmente, adicione um tempo de espera de 2 segundos entre a indexação e as 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);
    

    O atraso de 2 segundos compensa a indexação, que é assíncrona, para que todos os documentos possam ser indexados antes que as consultas sejam executadas. A codificação em um atraso normalmente só é necessária em demonstrações, testes e aplicativos de exemplo.

Pesquisar um índice

Você pode obter os resultados da consulta assim que o primeiro documento for indexado, mas o teste real do índice deve aguardar até que todos os documentos sejam indexados.

Esta seção adiciona duas partes de funcionalidade: lógica de consulta e resultados. Para consultas, use o método Search . Esse método usa texto de pesquisa (a cadeia de caracteres de consulta) e outras opções.

A classe SearchResults representa os resultados.

  1. No Program.cs, crie um método WriteDocuments que imprima os resultados da pesquisa no console.

    // 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. Crie um método RunQueries para executar consultas e retornar resultados. Os resultados são objetos do Hotel. Este exemplo mostra a assinatura do método e a primeira consulta. Esta consulta demonstra o parâmetro Select que permite compor o resultado usando campos selecionados do documento.

    // Run queries, use WriteDocuments to print output
    private static void RunQueries(SearchClient srchclient)
    {
        SearchOptions options;
        SearchResults<Hotel> response;
    
        // Query 1
        Console.WriteLine("Query #1: Search on empty term '*' to return all documents, showing a subset of fields...\n");
    
        options = new SearchOptions()
        {
            IncludeTotalCount = true,
            Filter = "",
            OrderBy = { "" }
        };
    
        options.Select.Add("HotelId");
        options.Select.Add("HotelName");
        options.Select.Add("Address/City");
    
        response = srchclient.Search<Hotel>("*", options);
        WriteDocuments(response);
    
  3. Na segunda consulta, pesquise um termo, adicione um filtro que selecione documentos em que Classificação é maior que 4 e, em seguida, classifique por Classificação em ordem decrescente. Filter é uma expressão booleana que é avaliada em campos IsFilterable em um índice. As consultas de filtro incluem ou excluem valores. Como tal, não há pontuação de relevância associada a uma consulta de filtro.

    // Query 2
    Console.WriteLine("Query #2: Search on 'hotels', filter on 'Rating gt 4', sort by Rating in descending order...\n");
    
    options = new SearchOptions()
    {
        Filter = "Rating gt 4",
        OrderBy = { "Rating desc" }
    };
    
    options.Select.Add("HotelId");
    options.Select.Add("HotelName");
    options.Select.Add("Rating");
    
    response = srchclient.Search<Hotel>("hotels", options);
    WriteDocuments(response);
    
  4. A terceira consulta demonstra searchFields, usada para definir o escopo de uma operação de pesquisa de texto completo para campos específicos.

    // Query 3
    Console.WriteLine("Query #3: Limit search to specific fields (pool in Tags field)...\n");
    
    options = new SearchOptions()
    {
        SearchFields = { "Tags" }
    };
    
    options.Select.Add("HotelId");
    options.Select.Add("HotelName");
    options.Select.Add("Tags");
    
    response = srchclient.Search<Hotel>("pool", options);
    WriteDocuments(response);
    
  5. A quarta consulta demonstra facetas, que podem ser usadas para estruturar uma estrutura de navegação facetada.

     // Query 4
     Console.WriteLine("Query #4: Facet on 'Category'...\n");
    
     options = new SearchOptions()
     {
         Filter = ""
     };
    
     options.Facets.Add("Category");
    
     options.Select.Add("HotelId");
     options.Select.Add("HotelName");
     options.Select.Add("Category");
    
     response = srchclient.Search<Hotel>("*", options);
     WriteDocuments(response);
    
  6. Na quinta consulta, retorne um documento específico. Uma pesquisa de documento é uma resposta típica ao evento OnClick em um conjunto de resultados.

     // Query 5
     Console.WriteLine("Query #5: Look up a specific document...\n");
    
     Response<Hotel> lookupResponse;
     lookupResponse = srchclient.GetDocument<Hotel>("3");
    
     Console.WriteLine(lookupResponse.Value.HotelId);
    
  7. A última consulta mostra a sintaxe para preenchimento automático, simulando uma entrada parcial do usuário de "sa" que resolve para duas correspondências possíveis no sourceFields associado ao sugeridor que você definiu no índice.

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

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

As consultas anteriores mostram várias maneiras de corresponder termos em uma consulta: pesquisa de texto completo, filtros e preenchimento automático.

A pesquisa de texto completo e os filtros são realizados usando o método SearchClient.Search . Uma consulta de pesquisa pode ser passada na searchText cadeia de caracteres, enquanto uma expressão de filtro pode ser passada na propriedade Filter da classe SearchOptions . Para filtrar sem pesquisar, basta passar "*" pelo parâmetro do método SearchsearchText. Para pesquisar sem filtrar, deixe a Filter propriedade desdefinida ou não passe em uma SearchOptions instância.

Execute o programa

Pressione F5 para reconstruir o aplicativo e executar o programa em sua totalidade.

A saída inclui mensagens de Console.WriteLine, com a adição de informações de consulta e resultados.

Clean up resources (Limpar recursos)

Ao trabalhar na sua própria subscrição, recomendamos que verifique, depois de concluir um projeto, se ainda vai precisar dos recursos que criou. Os recursos que deixar em execução podem custar dinheiro. Pode eliminar recursos individualmente ou eliminar o grupo de recursos para eliminar todo o conjunto de recursos.

Você pode encontrar e gerenciar recursos no portal, usando o link Todos os recursos ou Grupos de recursos no painel de navegação esquerdo.

Se você estiver usando um serviço gratuito, lembre-se de que está limitado a três índices, indexadores e fontes de dados. Você pode excluir itens individuais no portal para ficar abaixo do limite.

Próximos passos

Neste início rápido, você trabalhou em um conjunto de tarefas para criar um índice, carregá-lo com documentos e executar consultas. Em diferentes estágios, pegamos atalhos para simplificar o código para legibilidade e compreensão. Agora que você está familiarizado com os conceitos básicos, experimente um tutorial que chama as APIs do Azure AI Search em um aplicativo Web.