Como usar Azure.Search.Documents em um aplicativo .NET em C#

Este artigo explica como criar e gerenciar objetos de pesquisa usando C# e a biblioteca de cliente Azure.Search.Documents (versão 11) no SDK do Azure para .NET.

Sobre a versão 11

O SDK do Azure para .NET inclui uma biblioteca de cliente Azure.Search.Documents da equipe do SDK do Azure que é funcionalmente equivalente à biblioteca de cliente anterior, Microsoft.Azure.Search. A versão 11 é mais consistente em termos de programação do Azure. Alguns exemplos incluem AzureKeyCredential autenticação de chave e System.Text.Json.Serialization para serialização JSON.

Tal como nas versões anteriores, pode utilizar esta biblioteca para:

  • Crie e gerencie índices de pesquisa, fontes de dados, indexadores, conjuntos de habilidades e mapas de sinônimos
  • Carregar e gerenciar documentos de pesquisa em um índice
  • Execute consultas, tudo sem ter que lidar com os detalhes de HTTP e JSON
  • Invoque e gerencie o enriquecimento de IA (conjuntos de habilidades) e saídas

A biblioteca é distribuída como um único pacote NuGet Azure.Search.Documents, que inclui todas as APIs usadas para acesso programático a um serviço de pesquisa.

A biblioteca de cliente define classes como , e , bem como operações como SearchIndexSearchIndexClient.CreateIndex e SearchClient.Search sobre as SearchIndexClient classes andSearchClient. SearchFieldSearchDocument Essas classes são organizadas nos seguintes namespaces:

Azure.Search.Documents (versão 11) tem como alvo a especificação do serviço de pesquisa 2020-06-30.

A biblioteca de cliente não fornece operações de gerenciamento de serviços, como criar e dimensionar serviços de pesquisa e gerenciar chaves de API. Se você precisar gerenciar seus recursos de pesquisa de um aplicativo .NET, use a biblioteca Microsoft.Azure.Management.Search no SDK do Azure para .NET.

Atualizar para v11

Se você estiver usando a versão anterior do SDK do .NET e quiser atualizar para a versão atual disponível em geral, consulte Atualizar para o SDK do .NET do Azure AI Search versão 11.

Requisitos do SDK

  • Visual Studio 2019 ou posterior.

  • Seu próprio serviço Azure AI Search. Para usar o SDK, você precisará do nome do seu serviço e de uma ou mais chaves de API. Crie um serviço no portal se não tiver um.

  • Baixe o pacote Azure.Search.Documents usando Ferramentas>Gerenciador de Pacotes NuGet Gerenciar Pacotes>NuGet para Solução no Visual Studio. Procure o nome Azure.Search.Documentsdo pacote .

O SDK do Azure para .NET está em conformidade com o .NET Standard 2.0.

Aplicação de exemplo

Este artigo "ensina por exemplo", baseando-se no exemplo de código DotNetHowTo no GitHub para ilustrar conceitos fundamentais no Azure AI Search - especificamente, como criar, carregar e consultar um índice de pesquisa.

Para o resto deste artigo, suponha um novo índice chamado "hotéis", preenchido com alguns documentos, com várias consultas que correspondem aos resultados.

Abaixo está o programa principal, mostrando o fluxo geral:

// This sample shows how to delete, create, upload documents and query an index
static void Main(string[] args)
{
    IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
    IConfigurationRoot configuration = builder.Build();

    SearchIndexClient indexClient = CreateSearchIndexClient(configuration);

    string indexName = configuration["SearchIndexName"];

    Console.WriteLine("{0}", "Deleting index...\n");
    DeleteIndexIfExists(indexName, indexClient);

    Console.WriteLine("{0}", "Creating index...\n");
    CreateIndex(indexName, indexClient);

    SearchClient searchClient = indexClient.GetSearchClient(indexName);

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

    SearchClient indexClientForQueries = CreateSearchClientForQueries(indexName, configuration);

    Console.WriteLine("{0}", "Run queries...\n");
    RunQueries(indexClientForQueries);

    Console.WriteLine("{0}", "Complete.  Press any key to end application...\n");
    Console.ReadKey();
}

Em seguida, é uma captura de tela parcial da saída, supondo que você execute este aplicativo com um nome de serviço válido e chaves de API:

Screenshot of the Console.WriteLine output from the sample program.

Tipos de clientes

A biblioteca de cliente usa três tipos de cliente para várias operações: SearchIndexClient para criar, atualizar ou excluir índices, SearchClient para carregar ou consultar um índice e para trabalhar com indexadores e SearchIndexerClient conjuntos de habilidades. Este artigo concentra-se nos dois primeiros.

No mínimo, todos os clientes exigem o nome do serviço ou ponto de extremidade e uma chave de API. É comum fornecer essas informações em um arquivo de configuração, semelhante ao que você encontra no appsettings.json arquivo do aplicativo de exemplo DotNetHowTo. Para ler a partir do arquivo de configuração, adicione using Microsoft.Extensions.Configuration; ao seu programa.

A instrução a seguir cria o cliente de índice usado para criar, atualizar ou excluir índices. É necessário um ponto de extremidade de serviço e uma chave de API de administração.

private static SearchIndexClient CreateSearchIndexClient(IConfigurationRoot configuration)
{
    string searchServiceEndPoint = configuration["SearchServiceEndPoint"];
    string adminApiKey = configuration["SearchServiceAdminApiKey"];

    SearchIndexClient indexClient = new SearchIndexClient(new Uri(searchServiceEndPoint), new AzureKeyCredential(adminApiKey));
    return indexClient;
}

A próxima instrução cria o cliente de pesquisa usado para carregar documentos ou executar consultas. SearchClient requer um índice. Você precisará de uma chave de API de administração para carregar documentos, mas pode usar uma chave de API de consulta para executar consultas.

string indexName = configuration["SearchIndexName"];

private static SearchClient CreateSearchClientForQueries(string indexName, IConfigurationRoot configuration)
{
    string searchServiceEndPoint = configuration["SearchServiceEndPoint"];
    string queryApiKey = configuration["SearchServiceQueryApiKey"];

    SearchClient searchClient = new SearchClient(new Uri(searchServiceEndPoint), indexName, new AzureKeyCredential(queryApiKey));
    return searchClient;
}

Nota

Se você fornecer uma chave inválida para a operação de importação (por exemplo, uma chave de consulta onde uma chave de administrador foi necessária), o SearchClient lançará um com a mensagem de erro "Proibido" na primeira vez que você chamar um CloudException método de operação nele. Se isso acontecer com você, verifique novamente a chave da API.

Eliminar o índice

Nos estágios iniciais de desenvolvimento, convém incluir uma instrução para excluir um índice de trabalho em andamento para que possa recriá-lo com uma DeleteIndex definição atualizada. O código de exemplo para o Azure AI Search geralmente inclui uma etapa de exclusão para que você possa executar novamente o exemplo.

A seguinte linha chama DeleteIndexIfExists:

Console.WriteLine("{0}", "Deleting index...\n");
DeleteIndexIfExists(indexName, indexClient);

Este método usa o dado SearchIndexClient para verificar se o índice existe e, em caso afirmativo, o exclui:

private static void DeleteIndexIfExists(string indexName, SearchIndexClient indexClient)
{
    try
    {
        if (indexClient.GetIndex(indexName) != null)
        {
            indexClient.DeleteIndex(indexName);
        }
    }
    catch (RequestFailedException e) when (e.Status == 404)
    {
        // Throw an exception if the index name isn't found
        Console.WriteLine("The index doesn't exist. No deletion occurred.");

Nota

O código de exemplo neste artigo usa os métodos síncronos para simplificar, mas você deve usar os métodos assíncronos em seus próprios aplicativos para mantê-los escaláveis e responsivos. Por exemplo, no método acima você pode usar DeleteIndexAsync em vez de DeleteIndex.

Criar um índice

Você pode usar SearchIndexClient para criar um índice.

O método abaixo cria um novo objeto com uma lista de SearchField objetos que definem o esquema do novo SearchIndex índice. Cada campo tem um nome, tipo de dados e vários atributos que definem seu comportamento de pesquisa.

Os campos podem ser definidos a partir de uma classe de modelo usando FieldBuilder. A FieldBuilder classe usa reflexão para criar uma lista de SearchField objetos para o índice, examinando as propriedades públicas e atributos da classe de modelo dada Hotel . Veremos mais de perto a Hotel aula mais adiante.

private static void CreateIndex(string indexName, SearchIndexClient indexClient)
{
    FieldBuilder fieldBuilder = new FieldBuilder();
    var searchFields = fieldBuilder.Build(typeof(Hotel));

    var definition = new SearchIndex(indexName, searchFields);

    indexClient.CreateOrUpdateIndex(definition);
}

Além dos campos, você também pode adicionar perfis de pontuação, sugestões ou opções CORS ao índice (esses parâmetros são omitidos da amostra por brevidade). Você pode encontrar mais informações sobre o objeto SearchIndex e suas partes constituintes SearchIndex na lista de propriedades, bem como na referência da API REST.

Nota

Você sempre pode criar a lista de objetos diretamente em vez de Field usar FieldBuilder , se necessário. Por exemplo, talvez você não queira usar uma classe de modelo ou talvez precise usar uma classe de modelo existente que não deseja modificar adicionando atributos.

Chamar CreateIndex em Main()

Main cria um novo índice "hotéis" chamando o método acima:

Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, indexClient);

Usar uma classe de modelo para representação de dados

O exemplo DotNetHowTo usa classes de modelo para as estruturas de dados Hotel, Address e Room. Hotel referências Address, um tipo complexo de nível único (um campo com várias partes) e Room (uma coleção de campos com várias partes).

Você pode usar esses tipos para criar e carregar o índice e para estruturar a resposta de uma consulta:

// Use-case: <Hotel> in a field definition
FieldBuilder fieldBuilder = new FieldBuilder();
var searchFields = fieldBuilder.Build(typeof(Hotel));

// Use-case: <Hotel> in a response
private static void WriteDocuments(SearchResults<Hotel> searchResults)
{
    foreach (SearchResult<Hotel> result in searchResults.GetResults())
    {
        Console.WriteLine(result.Document);
    }

    Console.WriteLine();
}

Uma abordagem alternativa é adicionar campos a um índice diretamente. O exemplo a seguir mostra apenas alguns campos.

 SearchIndex index = new SearchIndex(indexName)
 {
     Fields =
         {
             new SimpleField("hotelId", SearchFieldDataType.String) { IsKey = true, IsFilterable = true, IsSortable = true },
             new SearchableField("hotelName") { IsFilterable = true, IsSortable = true },
             new SearchableField("hotelCategory") { IsFilterable = true, IsSortable = true },
             new SimpleField("baseRate", SearchFieldDataType.Int32) { IsFilterable = true, IsSortable = true },
             new SimpleField("lastRenovationDate", SearchFieldDataType.DateTimeOffset) { IsFilterable = true, IsSortable = true }
         }
 };

Definições de campo

Seu modelo de dados no .NET e seu esquema de índice correspondente devem oferecer suporte à experiência de pesquisa que você gostaria de oferecer ao usuário final. Cada objeto de nível superior no .NET, como um documento de pesquisa em um índice de pesquisa, corresponde a um resultado de pesquisa que você apresentaria na interface do usuário. Por exemplo, em um aplicativo de pesquisa de hotel, seus usuários finais podem querer pesquisar por nome de hotel, características do hotel ou características de um quarto específico.

Dentro de cada classe, um campo é definido com um tipo de dados e atributos que determinam como ele é usado. O nome de cada propriedade pública em cada classe é mapeado para um campo com o mesmo nome na definição de índice.

Dê uma olhada no trecho a seguir que extrai várias definições de campo da classe Hotel. Observe que Address e Rooms são tipos C# com suas próprias definições de classe (consulte o código de exemplo se quiser visualizá-los). Ambos são tipos complexos. Para obter mais informações, consulte Como modelar tipos complexos.

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(IsFilterable = true, IsSortable = true, IsFacetable = true)]
    public string Category { get; set; }

    [JsonIgnore]
    public bool? SmokingAllowed => (Rooms != null) ? Array.Exists(Rooms, element => element.SmokingAllowed == true) : (bool?)null;

    [SearchableField]
    public Address Address { get; set; }

    public Room[] Rooms { get; set; }

Escolhendo uma classe de campo

Ao definir campos, você pode usar a classe base SearchField ou modelos auxiliares derivados que servem como "modelos", com propriedades pré-configuradas.

Exatamente um campo no índice deve servir como a chave do documento (IsKey = true). Deve ser uma cadeia de caracteres e deve identificar exclusivamente cada documento. Também é necessário ter IsHidden = true, o que significa que não pode ser visível nos resultados de pesquisa.

Tipo de campo Descrição e utilização
SearchField Classe base, com a maioria das propriedades definidas como null, exceto Name o que é necessário e AnalyzerName o padrão para o padrão Lucene.
SimpleField Modelo auxiliar. 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 Modelo auxiliar. 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.

Adicionando atributos de campo

Observe como cada campo é decorado com atributos como IsFilterable, , IsKeyIsSortablee AnalyzerName. Esses atributos são mapeados diretamente para os atributos de campo correspondentes em um índice do Azure AI Search. A FieldBuilder classe usa essas propriedades para construir definições de campo para o índice.

Mapeamento de tipo de campo

Os tipos .NET das propriedades são mapeados para seus tipos de campo equivalentes na definição de índice. Por exemplo, a propriedade da cadeia Category mapeia para o campo category, que é do tipo Edm.String. Existem mapeamentos de tipo semelhantes entre bool?, , Edm.BooleanDateTimeOffset?, e Edm.DateTimeOffset assim por diante.

Por acaso reparou no SmokingAllowed imóvel?

[JsonIgnore]
public bool? SmokingAllowed => (Rooms != null) ? Array.Exists(Rooms, element => element.SmokingAllowed == true) : (bool?)null;

O JsonIgnore atributo nessa propriedade informa para não FieldBuilder serializá-lo para o índice como um campo. Esta é uma ótima maneira de criar propriedades calculadas do lado do cliente que você pode usar como auxiliares em seu aplicativo. Neste caso, a SmokingAllowed propriedade reflete se algum Room na Rooms coleção permite fumar. Se todos forem falsos, isso indica que todo o hotel não permite fumar.

Carregar um índice

A próxima etapa preenche Main o recém-criado índice de "hotéis". Esta população de índice é feita no seguinte método: (Alguns códigos substituídos por "..." para fins ilustrativos. Consulte a solução de exemplo completa para obter o código de população de dados completo.)

private static void UploadDocuments(SearchClient searchClient)
{
    IndexDocumentsBatch<Hotel> batch = IndexDocumentsBatch.Create(
        IndexDocumentsAction.Upload(
            new Hotel()
            {
                HotelId = "1",
                HotelName = "Secret Point Motel",
                ...
                Address = new Address()
                {
                    StreetAddress = "677 5th Ave",
                    ...
                },
                Rooms = new Room[]
                {
                    new Room()
                    {
                        Description = "Budget Room, 1 Queen Bed (Cityside)",
                        ...
                    },
                    new Room()
                    {
                        Description = "Budget Room, 1 King Bed (Mountain View)",
                        ...
                    },
                    new Room()
                    {
                        Description = "Deluxe Room, 2 Double Beds (City View)",
                        ...
                    }
                }
            }),
        IndexDocumentsAction.Upload(
            new Hotel()
            {
                HotelId = "2",
                HotelName = "Twin Dome Motel",
                ...
                {
                    StreetAddress = "140 University Town Center Dr",
                    ...
                },
                Rooms = new Room[]
                {
                    new Room()
                    {
                        Description = "Suite, 2 Double Beds (Mountain View)",
                        ...
                    },
                    new Room()
                    {
                        Description = "Standard Room, 1 Queen Bed (City View)",
                        ...
                    },
                    new Room()
                    {
                        Description = "Budget Room, 1 King Bed (Waterfront View)",
                        ...
                    }
                }
            }),
        IndexDocumentsAction.Upload(
            new Hotel()
            {
                HotelId = "3",
                HotelName = "Triple Landscape Hotel",
                ...
                Address = new Address()
                {
                    StreetAddress = "3393 Peachtree Rd",
                    ...
                },
                Rooms = new Room[]
                {
                    new Room()
                    {
                        Description = "Standard Room, 2 Queen Beds (Amenities)",
                        ...
                    },
                    new Room ()
                    {
                        Description = "Standard Room, 2 Double Beds (Waterfront View)",
                        ...
                    },
                    new Room()
                    {
                        Description = "Deluxe Room, 2 Double Beds (Cityside)",
                        ...
                    }
                }
            }
        };

    try
    {
        IndexDocumentsResult result = searchClient.IndexDocuments(batch);
    }
    catch (Exception)
    {
        // Sometimes when your Search service is under load, indexing will fail for some of the documents in
        // the batch. Depending on your application, you can take compensating actions like delaying and
        // retrying. For this simple demo, we just log the failed document keys and continue.
        Console.WriteLine("Failed to index some of the documents: {0}");
    }

    Console.WriteLine("Waiting for documents to be indexed...\n");
    Thread.Sleep(2000);

Este método tem quatro partes. O primeiro cria uma matriz de três objetos, cada um com três HotelRoom objetos que servirão como nossos dados de entrada para carregar no índice. Esses dados são codificados para simplificar. Em um aplicativo real, os dados provavelmente virão de uma fonte de dados externa, como um banco de dados SQL.

A segunda parte cria um IndexDocumentsBatch contendo os documentos. Você especifica a operação que deseja aplicar ao lote no momento em que o cria, neste caso chamando IndexDocumentsAction.Upload. O lote é então carregado no índice do Azure AI Search pelo IndexDocuments método.

Nota

Neste exemplo, estamos apenas a carregar documentos. Se você quiser mesclar alterações em documentos existentes ou excluir documentos, você pode criar lotes chamando IndexDocumentsAction.Merge, IndexDocumentsAction.MergeOrUploadou IndexDocumentsAction.Delete em vez disso. Você também pode misturar diferentes operações em um único lote chamando IndexBatch.New, que usa uma coleção de IndexDocumentsAction objetos, cada um dos quais diz ao Azure AI Search para executar uma operação específica em um documento. Você pode criar cada um IndexDocumentsAction com sua própria operação chamando o método correspondente, como IndexDocumentsAction.Merge, IndexAction.Uploade assim por diante.

A terceira parte desse método é um bloco catch que lida com um caso de erro importante para indexação. Se o seu serviço de pesquisa não conseguir indexar alguns dos documentos do lote, um RequestFailedException será lançado. Uma exceção pode acontecer se você estiver indexando documentos enquanto o serviço estiver sob carga pesada. Recomendamos vivamente que processe explicitamente este caso no seu código. Pode atrasar e, em seguida, repetir a indexação dos documentos que falharam, pode iniciar sessão e continuar como no exemplo ou pode fazer algo diferente dependendo dos requisitos de consistência de dados da aplicação. Uma alternativa é usar SearchIndexingBufferedSender para processamento em lote inteligente, liberação automática e novas tentativas para ações de indexação com falha. Veja este exemplo para obter mais contexto.

Finalmente, o método atrasa UploadDocuments por dois segundos. A indexação acontece de forma assíncrona em seu serviço de pesquisa, portanto, o aplicativo de exemplo precisa esperar um curto período de tempo para garantir que os documentos estejam disponíveis para pesquisa. Este género de atrasos são normalmente necessários apenas para demonstrações gratuitas, testes e aplicações de exemplo.

Chamada UploadDocuments em Main()

O trecho de código a seguir configura uma instância de uso do SearchClientGetSearchClient método indexClient. O indexClient usa uma chave de API admin em suas solicitações, que é necessária para carregar ou atualizar documentos.

Uma abordagem alternativa é chamar SearchClient diretamente, passando uma chave de API admin no AzureKeyCredential.

SearchClient searchClient = indexClient.GetSearchClient(indexName);

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

Executar consultas

Primeiro, configure um SearchClient que leia o ponto de extremidade do serviço e a chave da API de consulta do appsettings.json:

private static SearchClient CreateSearchClientForQueries(string indexName, IConfigurationRoot configuration)
{
    string searchServiceEndPoint = configuration["SearchServiceEndPoint"];
    string queryApiKey = configuration["SearchServiceQueryApiKey"];

    SearchClient searchClient = new SearchClient(new Uri(searchServiceEndPoint), indexName, new AzureKeyCredential(queryApiKey));
    return searchClient;
}

Em segundo lugar, defina um método que envie uma solicitação de consulta.

Cada vez que o método executa uma consulta, ele cria um novo SearchOptions objeto. Este objeto é usado para especificar opções adicionais para a consulta, como classificação, filtragem, paginação e facetagem. Neste método, estamos definindo a Filterpropriedade , Selecte OrderBy para consultas diferentes. Para obter mais informações sobre a sintaxe da expressão de consulta de pesquisa, sintaxe de consulta simples.

A próxima etapa é a execução da consulta. A execução da pesquisa é feita usando o SearchClient.Search método. Para cada consulta, passe o texto de pesquisa para usar como uma cadeia de caracteres (ou "*" se não houver texto de pesquisa), além das opções de pesquisa criadas anteriormente. Também especificamos Hotel como o parâmetro type para , que diz ao SDK para SearchClient.Searchdesserializar documentos nos resultados da pesquisa em objetos do tipo Hotel.

private static void RunQueries(SearchClient searchClient)
{
    SearchOptions options;
    SearchResults<Hotel> results;

    Console.WriteLine("Query 1: Search for 'motel'. Return only the HotelName in results:\n");

    options = new SearchOptions();
    options.Select.Add("HotelName");

    results = searchClient.Search<Hotel>("motel", options);

    WriteDocuments(results);

    Console.Write("Query 2: Apply a filter to find hotels with rooms cheaper than $100 per night, ");
    Console.WriteLine("returning the HotelId and Description:\n");

    options = new SearchOptions()
    {
        Filter = "Rooms/any(r: r/BaseRate lt 100)"
    };
    options.Select.Add("HotelId");
    options.Select.Add("Description");

    results = searchClient.Search<Hotel>("*", options);

    WriteDocuments(results);

    Console.Write("Query 3: Search the entire index, order by a specific field (lastRenovationDate) ");
    Console.Write("in descending order, take the top two results, and show only hotelName and ");
    Console.WriteLine("lastRenovationDate:\n");

    options =
        new SearchOptions()
        {
            Size = 2
        };
    options.OrderBy.Add("LastRenovationDate desc");
    options.Select.Add("HotelName");
    options.Select.Add("LastRenovationDate");

    results = searchClient.Search<Hotel>("*", options);

    WriteDocuments(results);

    Console.WriteLine("Query 4: Search the HotelName field for the term 'hotel':\n");

    options = new SearchOptions();
    options.SearchFields.Add("HotelName");

    //Adding details to select, because "Location" isn't supported yet when deserializing search result to "Hotel"
    options.Select.Add("HotelId");
    options.Select.Add("HotelName");
    options.Select.Add("Description");
    options.Select.Add("Category");
    options.Select.Add("Tags");
    options.Select.Add("ParkingIncluded");
    options.Select.Add("LastRenovationDate");
    options.Select.Add("Rating");
    options.Select.Add("Address");
    options.Select.Add("Rooms");

    results = searchClient.Search<Hotel>("hotel", options);

    WriteDocuments(results);
}

Em terceiro lugar, defina um método que escreva a resposta, imprimindo cada documento no console:

private static void WriteDocuments(SearchResults<Hotel> searchResults)
{
    foreach (SearchResult<Hotel> result in searchResults.GetResults())
    {
        Console.WriteLine(result.Document);
    }

    Console.WriteLine();
}

Chamar RunQueries em Main()

SearchClient indexClientForQueries = CreateSearchClientForQueries(indexName, configuration);

Console.WriteLine("{0}", "Running queries...\n");
RunQueries(indexClientForQueries);

Explore construções de consulta

Vamos dar uma olhada mais de perto em cada uma das consultas por vez. Aqui está o código para executar a primeira consulta:

options = new SearchOptions();
options.Select.Add("HotelName");

results = searchClient.Search<Hotel>("motel", options);

WriteDocuments(results);

Neste caso, estamos pesquisando todo o índice para a palavra "motel" em qualquer campo pesquisável e queremos apenas recuperar os nomes dos Select hotéis, conforme especificado pela opção. Eis os resultados:

Name: Secret Point Motel

Name: Twin Dome Motel

Na segunda consulta, use um filtro para selecionar quartos com uma taxa noturna inferior a US$ 100. Devolva apenas o ID do hotel e a descrição nos resultados:

options = new SearchOptions()
{
    Filter = "Rooms/any(r: r/BaseRate lt 100)"
};
options.Select.Add("HotelId");
options.Select.Add("Description");

results = searchClient.Search<Hotel>("*", options);

A consulta acima usa uma expressão OData $filter , , Rooms/any(r: r/BaseRate lt 100)para filtrar os documentos no índice. Isso usa qualquer operador para aplicar o 'BaseRate lt 100' a cada item da coleção Rooms. Para obter mais informações, consulte Sintaxe do filtro OData.

Na terceira consulta, encontre os dois principais hotéis que foram renovados mais recentemente e mostre o nome do hotel e a data da última renovação. Aqui está o código:

options =
    new SearchOptions()
    {
        Size = 2
    };
options.OrderBy.Add("LastRenovationDate desc");
options.Select.Add("HotelName");
options.Select.Add("LastRenovationDate");

results = searchClient.Search<Hotel>("*", options);

WriteDocuments(results);

Na última consulta, encontre todos os nomes de hotéis que correspondam à palavra "hotel":

options.Select.Add("HotelId");
options.Select.Add("HotelName");
options.Select.Add("Description");
options.Select.Add("Category");
options.Select.Add("Tags");
options.Select.Add("ParkingIncluded");
options.Select.Add("LastRenovationDate");
options.Select.Add("Rating");
options.Select.Add("Address");
options.Select.Add("Rooms");

results = searchClient.Search<Hotel>("hotel", options);

WriteDocuments(results);

Esta seção conclui esta introdução ao SDK do .NET, mas não pare por aqui. A próxima seção sugere outros recursos para aprender mais sobre programação com o Azure AI Search.

Próximos passos