Compartilhar via


Tutorial: criar seu primeiro aplicativo de pesquisa no Azure Cognitive Search com o SDK do .NET

Este tutorial mostra como criar um aplicativo Web que consulta e retorna resultados de um índice de pesquisa usando o Azure Cognitive Search e o Visual Studio.

Neste tutorial, você aprenderá como:

  • Configurar um ambiente de desenvolvimento
  • Modelar estruturas de dados
  • Criar uma página da Web para coletar entradas de consulta e exibir resultados
  • Definir um método de pesquisa
  • Testar o aplicativo

Você também aprenderá como uma chamada de pesquisa é simples. As principais instruções no código são encapsuladas nas poucas linhas a seguir:

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

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

Apenas uma chamada consulta o índice de pesquisa e retorna resultados.

Pesquisando *pool*

Visão geral

Este tutorial usa o hotels-sample-index, o qual você pode criar rapidamente em seu próprio serviço de pesquisa percorrendo o Guia de início rápido importar dados. O índice contém dados fictícios de hotéis, que ficam disponíveis como uma fonte de dados interna em todos os serviços de pesquisa.

A primeira lição desse tutorial cria uma estrutura de consulta básica e uma página de pesquisa, as quais você aprimorará nas lições subsequentes para incluir paginação, facetas e uma experiência de preenchimento automático.

Uma versão finalizada do código pode ser encontrada no seguinte projeto:

Pré-requisitos

Instalar e executar o projeto usando o GitHub

Se você quiser avançar para um aplicativo em funcionamento, siga as etapas abaixo para baixar e executar o código concluído.

  1. Localize o exemplo no GitHub: Criar primeiro aplicativo.

  2. Na pasta raiz, selecione Código, seguido por Clonar ou Baixar ZIP para fazer sua cópia local privada do projeto.

  3. Usando o Visual Studio, navegue até e abra a solução para a página de pesquisa básica ("1-basic-search-page") e selecione Iniciar sem depurar (ou pressione F5) para compilar e executar o programa.

  4. Este é um índice de hotéis, portanto, digite algumas palavras que você possa usar para pesquisar hotéis (por exemplo, "WiFi", "vista", "bar", "estacionamento"). Examine os resultados.

    Pesquisando *wifi*

Os componentes essenciais para pesquisas mais avançadas estão incluídos neste aplicativo. Se você não estiver familiarizado com o desenvolvimento de pesquisa, poderá recriar este aplicativo passo a passo para aprender o fluxo de trabalho. As seções a seguir mostram como.

Configurar um ambiente de desenvolvimento

Para criar esse projeto do zero e, assim, reforçar os conceitos do Azure Cognitive Search, comece com um projeto do Visual Studio.

  1. No Visual Studio, selecione Novo>Projeto e Aplicativo Web do ASP.NET Core (Model-View-Controller).

    Como criar um projeto de nuvem

  2. Dê um nome ao projeto, como "FirstSearchApp", e defina a localização. Selecione Avançar.

  3. Aceite os padrões para estrutura de destino, tipo de autenticação e HTTPS. Selecione Criar.

  4. Instalar a biblioteca de clientes. Em Ferramentas>Gerenciador de Pacotes NuGet>Gerenciar Pacotes NuGet da Solução..., selecione Procurar e procure "azure.search.documents". Instale o Azure.Search.Documents (versão 11 ou posterior), aceitando os contratos de licença e as dependências.

    Como usar o NuGet para adicionar bibliotecas do Azure

Nesta etapa, defina o ponto de extremidade e a chave de acesso para se conectar ao serviço de pesquisa que fornece o índice de exemplo de hotéis.

  1. Abra appsettings.jsno e substitua as linhas padrão pelo URL do serviço de pesquisa (no formato https://<service-name>.search.windows.net) e uma chave de API de consulta ou administrador do seu serviço de pesquisa. Como você não precisa criar ou atualizar um índice, você pode usar a chave de consulta para esse tutorial.

    {
        "SearchServiceUri": "<YOUR-SEARCH-SERVICE-URI>",
        "SearchServiceQueryApiKey": "<YOUR-SEARCH-SERVICE-API-KEY>"
    }
    
  2. Em Gerenciador de Soluções, selecione o arquivo e, em Propriedades, altere a configuração Copiar para o Diretório de Saída para Copiar se for mais recente.

    Como copiar as configurações do aplicativo para a saída

Modelar estruturas de dados

Os modelos (classes C#) são usados para comunicar dados entre o cliente (a exibição), o servidor (o controlador) e também a nuvem do Azure usando a arquitetura MVC (modelo, exibição, controlador). Normalmente, esses modelos refletem a estrutura dos dados que estão sendo acessados.

Nesta etapa, você modelará as estruturas de dados do índice de pesquisa, bem como a cadeia de pesquisa usada em comunicações de exibição/controlador. No índice de hotéis, cada hotel tem muitos quartos e cada hotel tem um endereço de várias partes. No geral, a representação completa de um hotel é uma estrutura de dados hierárquica e aninhada. Você precisará de três classes para criar cada componente.

O conjunto das classes Hotel, Endereço e Quarto é conhecido como tipos complexos, um recurso importante do Azure Cognitive Search. Tipos complexos podem ter muitos níveis de profundidade de classes e subclasses e possibilitam a representação de estruturas de dados muito mais complexas do que usando tipos simples (uma classe contendo somente membros primitivos).

  1. No Gerenciador de Soluções, clique com o botão direito do mouse em Modelos>Adicionar>Novo Item.

  2. Selecione Classe e dê ao item o nome de Hotel.cs. Substitua todo o conteúdos de Hotel.cs pelo código a seguir. Observe os membros Endereço e Quarto da classe; esses campos são classes em si, assim, você precisará de modelos para eles também.

    using Azure.Search.Documents.Indexes;
    using Azure.Search.Documents.Indexes.Models;
    using Microsoft.Spatial;
    using System;
    using System.Text.Json.Serialization;
    
    namespace FirstAzureSearchApp.Models
    {
        public partial class Hotel
        {
            [SimpleField(IsFilterable = true, IsKey = true)]
            public string HotelId { get; set; }
    
            [SearchableField(IsSortable = true)]
            public string HotelName { get; set; }
    
            [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.EnLucene)]
            public string Description { get; set; }
    
            [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.FrLucene)]
            [JsonPropertyName("Description_fr")]
            public string DescriptionFr { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string Category { get; set; }
    
            [SearchableField(IsFilterable = true, IsFacetable = true)]
            public string[] Tags { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public bool? ParkingIncluded { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public DateTimeOffset? LastRenovationDate { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public double? Rating { get; set; }
    
            public Address Address { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true)]
            public GeographyPoint Location { get; set; }
    
            public Room[] Rooms { get; set; }
        }
    }
    
  3. Repita o mesmo processo de criação de um modelo para a classe de Endereços, dando ao arquivo o nome de Address.cs. Substitua o conteúdo pelo seguinte.

    using Azure.Search.Documents.Indexes;
    
    namespace FirstAzureSearchApp.Models
    {
        public partial class Address
        {
            [SearchableField]
            public string StreetAddress { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string City { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string StateProvince { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string PostalCode { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string Country { get; set; }
        }
    }
    
  4. Novamente, siga o mesmo processo para criar a classe Quarto, dando ao arquivo o nome de Room.cs.

    using Azure.Search.Documents.Indexes;
    using Azure.Search.Documents.Indexes.Models;
    using System.Text.Json.Serialization;
    
    namespace FirstAzureSearchApp.Models
    {
        public partial class Room
        {
            [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.EnMicrosoft)]
            public string Description { get; set; }
    
            [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.FrMicrosoft)]
            [JsonPropertyName("Description_fr")]
            public string DescriptionFr { get; set; }
    
            [SearchableField(IsFilterable = true, IsFacetable = true)]
            public string Type { get; set; }
    
            [SimpleField(IsFilterable = true, IsFacetable = true)]
            public double? BaseRate { get; set; }
    
            [SearchableField(IsFilterable = true, IsFacetable = true)]
            public string BedOptions { get; set; }
    
            [SimpleField(IsFilterable = true, IsFacetable = true)]
            public int SleepsCount { get; set; }
    
            [SimpleField(IsFilterable = true, IsFacetable = true)]
            public bool? SmokingAllowed { get; set; }
    
            [SearchableField(IsFilterable = true, IsFacetable = true)]
            public string[] Tags { get; set; }
        }
    }
    
  5. O último modelo que você criará neste tutorial é uma classe chamada SearchData e representa a entrada do usuário (searchText) e a saída da pesquisa (resultList). O tipo de saída é crítico, SearchResult<Hotel> , pois ele corresponde exatamente aos resultados da pesquisa e você precisa passar essa referência para a exibição. Substitua o modelo padrão pelo código a seguir.

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

Criar uma página da Web

Os modelos de projeto vêm com várias exibições de cliente localizadas na pasta Views. As exibições exatas dependem da versão do .NET Core que você está usando (este exemplo usa a 3.1). Para este tutorial, você modificará index.cshtml para incluir os elementos de uma página de pesquisa.

Exclua o conteúdo de Index.cshtml em sua totalidade e recompile o arquivo nas etapas a seguir.

  1. O tutorial usa duas imagens pequenas na exibição: um logotipo do Azure e um ícone de lupa de pesquisa (azure-logo.png e search.png). Copie entre as imagens do projeto do GitHub para a pasta wwwroot/images em seu projeto.

  2. A primeira linha de Index.cshtml deve fazer referência ao modelo usado para comunicar dados entre o cliente (a exibição) e o servidor (o controlador), que é o modelo SearchData criado antes. Adicione essa linha ao arquivo Index.cshtml.

    @model FirstAzureSearchApp.Models.SearchData
    
  3. É uma prática padrão inserir um título para a exibição, portanto, as próximas linhas devem ser:

    @{
        ViewData["Title"] = "Home Page";
    }
    
  4. Após o título, insira uma referência a uma folha de estilos HTML, que você criará em breve.

    <head>
        <link rel="stylesheet" href="~/css/hotels.css" />
    </head>
    
  5. O corpo da exibição lida com dois casos de uso. Primeiro, ele deve fornecer uma página vazia no primeiro uso antes da inserção de qualquer texto de pesquisa. Em segundo lugar, ele deve lidar com os resultados, além da caixa de texto de pesquisa, para consultas repetidas.

    Para lidar com ambos os casos, você precisa verificar se o modelo fornecido para a exibição é nulo. Um modelo nulo indica o primeiro caso de uso (a execução inicial do aplicativo). Adicione o seguinte ao arquivo Index. cshtml e leia os comentários.

    <body>
    <h1 class="sampleTitle">
        <img src="~/images/azure-logo.png" width="80" />
        Hotels Search
    </h1>
    
    @using (Html.BeginForm("Index", "Home", FormMethod.Post))
    {
        // Display the search text box, with the search icon to the right of it.
        <div class="searchBoxForm">
            @Html.TextBoxFor(m => m.searchText, new { @class = "searchBox" }) <input class="searchBoxSubmit" type="submit" value="">
        </div>
    
        @if (Model != null)
        {
            // Show the result count.
            <p class="sampleText">
                @Model.resultList.TotalCount Results
            </p>
    
            var results = Model.resultList.GetResults().ToList();
    
            @for (var i = 0; i < results.Count; i++)
            {
                // Display the hotel name and description.
                @Html.TextAreaFor(m => results[i].Document.HotelName, new { @class = "box1" })
                @Html.TextArea($"desc{i}", results[i].Document.Description, new { @class = "box2" })
            }
        }
    }
    </body>
    
  6. Adicione a folha de estilos. No Visual Studio, em Arquivo>Novo>Arquivo, selecione Folha de Estilos (com Geral realçado).

    Substitua o código padrão pelo seguinte. Não vamos entrar em mais detalhes sobre esse arquivo, os estilos são HTML padrão.

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

Isso conclui nossa exibição. Neste ponto, os modelos e as exibições são concluídos. Somente o controlador é deixado para unir tudo.

Definir métodos

Nesta etapa, modifique o conteúdo de Controlador da Página Inicial.

  1. Abra o arquivo HomeController.cs e substitua as instruções using pelo seguinte.

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

Adicionar métodos Index

Em um aplicativo MVC, o método Index() é um método de ação padrão para qualquer controlador. Ele abre a página de índice HTML. O método padrão, que não usa parâmetros, é usado neste tutorial para o caso de uso de inicialização do aplicativo: renderizar uma página de pesquisa vazia.

Nesta seção, estendemos o método para dar suporte a um segundo caso de uso: renderizar a página quando um usuário insere o texto de pesquisa. Para dar suporte a esse caso, o método de índice é estendido para pegar um modelo como um parâmetro.

  1. Adicione o seguinte método após o método Index() padrão.

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

    Observe a declaração async do método e a chamada await para RunQueryAsync. Essas palavras-chave cuidam da realização das chamadas assíncronas, portanto, evitam bloquear threads no servidor.

    O bloco catch usa o modelo de erro padrão criado.

Observe o tratamento de erro e outras exibições e métodos padrão

Dependendo de qual versão do .NET Core você está usando, um conjunto ligeiramente diferente de exibições padrão é criado. Para o .NET Core 3.1, as exibições padrão são Índice, Privacidade e Erro. Você pode exibir essas páginas padrão ao executar o aplicativo e examinar como elas são processadas no controlador.

Você testará a exibição de Erro mais adiante neste tutorial.

No exemplo do GitHub, exibições não utilizadas e suas ações associadas são excluídas.

Adicionar o método RunQueryAsync

A chamada à Pesquisa Cognitiva do Azure é encapsulada em nosso método RunQueryAsync.

  1. Primeiro, adicione algumas variáveis estáticas para configurar o serviço do Azure e uma chamada para iniciá-las.

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

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

    Nesse método, primeiro inicie a configuração do Azure e então defina algumas opções de pesquisa. A opção Selecionar especifica quais campos retornar nos resultados e, portanto, corresponde aos nomes das propriedades na classe hotel. Se você omitir Selecionar, todos os campos não ocultos serão retornados, o que poderá ser ineficiente se você estiver interessado apenas em um subconjunto de todos os campos possíveis.

    A chamada assíncrona para pesquisar formula a solicitação (modelada como searchText) e a resposta (modelada como searchResult). Se você está depurando esse código, a classe SearchResult é um bom candidato para definir um ponto de interrupção se você precisa examinar o conteúdo de model.resultList. Você verá que é um processo intuitivo, que fornece os dados que você pediu e não muito mais.

Testar o aplicativo

Agora, vamos verificar se o aplicativo é executado corretamente.

  1. Selecione Depurar>Iniciar Sem Depurar ou pressione F5. Se o aplicativo for executado conforme o esperado, você deverá obter a exibição de índice inicial.

    Como abrir o aplicativo

  2. Insira uma cadeia de consulta como "praia" (ou qualquer texto que lhe venha à cabeça) e clique no ícone de pesquisa para enviar a solicitação.

    Pesquisando *praia*

  3. Tente inserir "cinco estrelas". Observe que essa consulta não retorna nenhum resultado. Uma pesquisa mais sofisticada trataria "cinco estrelas" como sinônimo de "luxo" e retornaria esses resultados. O suporte para sinônimos está disponível no Azure Cognitive Search, mas não é abordado nesta série de tutoriais.

  4. Tente inserir "hot" como texto de pesquisa. Isso não retorna entradas com a palavra "hotel". Nossa pesquisa só está localizando palavras inteiras, embora alguns resultados sejam retornados.

  5. Experimente outras palavras: "piscina", "sol", "vista", o que for. Você verá o Azure Cognitive Search funcionando no nível mais simples, mas ainda convincente.

Testar condições de borda e erros

É importante verificar se nossos recursos de tratamento de erro funcionam como deveriam, mesmo quando as coisas estão funcionando perfeitamente.

  1. No método Index, depois da chamada try { , insira a linha Throw new Exception() . Essa exceção forçará um erro ao pesquisar no texto.

  2. Execute o aplicativo, insira "bar" como o texto de pesquisa e clique no ícone de pesquisa. A exceção deve resultar na exibição de erro.

    Forçar um erro

    Importante

    Retornar os números de erro internos em páginas de erro é considerado um risco de segurança. Se o aplicativo for destinado ao uso geral, siga as melhores práticas de segurança sobre o que retornar quando ocorrer um erro.

  3. Remova Throw new Exception() quando estiver convencido de que o tratamento de erro funciona como deveria.

Observações

Considere as principais conclusões deste projeto:

  • Uma chamada do Azure Cognitive Search é concisa e os resultados são fáceis de interpretar.
  • As chamadas assíncronas aumentam a complexidade do controlador, mas são a melhor prática que aprimora o desempenho.
  • Este aplicativo realizou uma pesquisa de texto simples, definida pelo que é configurado em searchOptions. No entanto, essa classe pode ser preenchida com muitos membros que adicionam sofisticação a uma pesquisa. Com um pouco mais de trabalho, você pode tornar esse aplicativo substancialmente mais eficaz.

Próximas etapas

Para aprimorar a experiência do usuário, adicione recursos, especialmente paginação (usando números de página ou rolagem infinita) e preenchimento automático/sugestões. Também é possível considerar outras opções de pesquisa (por exemplo, pesquisas geográficas de hotéis em um raio especificado de determinado ponto) e ordenação dos resultados da pesquisa.

As próximas etapas são abordadas nos tutoriais restantes. Vamos começar com paginação.