Criar um aplicativo de pesquisa no ASP.NET Core
Neste tutorial, crie um aplicativo básico do ASP.NET Core (Model-View-Controller) que seja executado no localhost e se conecte ao hotels-sample-index em seu serviço de pesquisa. Neste tutorial, saiba como:
- Criar uma página de pesquisa básica
- Filtrar resultados
- Ordenar os resultados
Este tutorial coloca o foco nas operações do lado do servidor chamadas por meio das APIs de Pesquisa. Embora seja comum classificar e filtrar no script do lado do cliente, saber como invocar essas operações no servidor oferece mais opções ao projetar a experiência de pesquisa.
O código de exemplo para este tutorial pode ser encontrado no repositório azure-search-dotnet-samples no GitHub.
Pré-requisitos
- Visual Studio
- Pacote NuGet Azure.Search.Documents
- Azure AI Search, qualquer camada, mas deve ter acesso à rede pública.
- Índice de amostras de hotéis
Percorra o assistente Importar dados para criar o índice de amostra de hotéis no seu serviço de pesquisa. Ou altere o nome do HomeController.cs
índice no arquivo.
Criar o projeto
Inicie o Visual Studio e selecione Criar um novo projeto.
Selecione ASP.NET Core Web App (Model-View-Controller) e, em seguida, selecione Next.
Forneça um nome de projeto e selecione Avançar.
Na página seguinte, selecione .NET 6.0 ou .NET 7.0 ou .NET 8.0.
Verifique se a opção Não usar instruções de nível superior está desmarcada.
Selecione Criar.
Adicionar pacotes NuGet
Em Ferramentas, selecione Gerenciador de>Pacotes NuGet Gerenciar Pacotes NuGet para a solução.
Procure
Azure.Search.Documents
e instale a versão estável mais recente.Procure e instale o
Microsoft.Spatial
pacote. O índice de exemplo inclui um tipo de dados GeographyPoint. A instalação deste pacote evita erros de tempo de execução. Como alternativa, remova o campo "Local" da classe Hotéis se não quiser instalar o pacote. Esse campo não é usado neste tutorial.
Adicionar informações de serviço
Para a conexão, o aplicativo apresenta uma chave de API de consulta para sua URL de pesquisa totalmente qualificada. Ambos são especificados no appsettings.json
arquivo.
Modifique appsettings.json
para especificar o serviço de pesquisa e a chave da API de consulta.
{
"SearchServiceUri": "<YOUR-SEARCH-SERVICE-URL>",
"SearchServiceQueryApiKey": "<YOUR-SEARCH-SERVICE-QUERY-API-KEY>"
}
Você pode obter a URL do serviço e a chave da API no portal do Azure. Como esse código está consultando um índice e não criando um, você pode usar uma chave de consulta em vez de uma chave de administrador.
Certifique-se de especificar o serviço de pesquisa que tem o índice de amostra de hotéis.
Adicionar modelos
Nesta etapa, crie modelos que representem o esquema do índice de amostra de hotéis.
No Gerenciador de soluções, selecione com o botão direito do mouse Modelos e adicione uma nova classe chamada "Hotel" para o seguinte código:
using Azure.Search.Documents.Indexes.Models; using Azure.Search.Documents.Indexes; using Microsoft.Spatial; using System.Text.Json.Serialization; namespace HotelDemoApp.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 Rooms[] Rooms { get; set; } } }
Adicione uma classe chamada "Address" e substitua-a pelo seguinte código:
using Azure.Search.Documents.Indexes; namespace HotelDemoApp.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; } } }
Adicione uma classe chamada "Rooms" e substitua-a pelo seguinte código:
using Azure.Search.Documents.Indexes.Models; using Azure.Search.Documents.Indexes; using System.Text.Json.Serialization; namespace HotelDemoApp.Models { public partial class Rooms { [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; } } }
Adicione uma classe chamada "SearchData" e substitua-a pelo seguinte código:
using Azure.Search.Documents.Models; namespace HotelDemoApp.Models { public class SearchData { // The text to search for. public string searchText { get; set; } // The list of results. public SearchResults<Hotel> resultList; } }
Modificar o controlador
Para este tutorial, modifique o padrão HomeController
para conter métodos que são executados em seu serviço de pesquisa.
No Gerenciador de soluções, em Modelos, abra
HomeController
.Substitua o padrão pelo seguinte conteúdo:
using Azure; using Azure.Search.Documents; using Azure.Search.Documents.Indexes; using HotelDemoApp.Models; using Microsoft.AspNetCore.Mvc; using System.Diagnostics; namespace HotelDemoApp.Controllers { public class HomeController : Controller { public IActionResult Index() { return View(); } [HttpPost] public async Task<ActionResult> Index(SearchData model) { try { // Check for a search string if (model.searchText == null) { model.searchText = ""; } // Send the query to Search. await RunQueryAsync(model); } catch { return View("Error", new ErrorViewModel { RequestId = "1" }); } return View(model); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } 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-sample-index"); } private async Task<ActionResult> RunQueryAsync(SearchData model) { InitSearch(); var options = new SearchOptions() { IncludeTotalCount = true }; // Enter Hotel property names to specify which fields are returned. // If Select is empty, all "retrievable" fields are returned. options.Select.Add("HotelName"); options.Select.Add("Category"); options.Select.Add("Rating"); options.Select.Add("Tags"); options.Select.Add("Address/City"); options.Select.Add("Address/StateProvince"); 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); } public IActionResult Privacy() { return View(); } } }
Modificar a vista
No Gerenciador de soluções, em Página inicial de modos de exibição>, abra .
index.cshtml
Substitua o padrão pelo seguinte conteúdo:
@model HotelDemoApp.Models.SearchData; @{ ViewData["Title"] = "Index"; } <div> <h2>Search for Hotels</h2> <p>Use this demo app to test server-side sorting and filtering. Modify the RunQueryAsync method to change the operation. The app uses the default search configuration (simple search syntax, with searchMode=Any).</p> <form asp-controller="Home" asp-action="Index"> <p> <input type="text" name="searchText" /> <input type="submit" value="Search" /> </p> </form> </div> <div> @using (Html.BeginForm("Index", "Home", FormMethod.Post)) { @if (Model != null) { // Show the result count. <p>@Model.resultList.TotalCount Results</p> // Get search results. var results = Model.resultList.GetResults().ToList(); { <table class="table"> <thead> <tr> <th>Name</th> <th>Category</th> <th>Rating</th> <th>Tags</th> <th>City</th> <th>State</th> <th>Description</th> </tr> </thead> <tbody> @foreach (var d in results) { <tr> <td>@d.Document.HotelName</td> <td>@d.Document.Category</td> <td>@d.Document.Rating</td> <td>@d.Document.Tags[0]</td> <td>@d.Document.Address.City</td> <td>@d.Document.Address.StateProvince</td> <td>@d.Document.Description</td> </tr> } </tbody> </table> } } } </div>
Executar o exemplo
Pressione F5 para compilar e executar o projeto. O aplicativo é executado no host local e abre em seu navegador padrão.
Selecione Pesquisar para retornar todos os resultados.
Este código usa a configuração de pesquisa padrão, suportando a sintaxe simples e
searchMode=Any
. Você pode inserir palavras-chave, aumentar com operadores booleanos ou executar uma pesquisa de prefixo (pool*
).
Nas próximas seções, modifique o método RunQueryAsync na HomeController
para adicionar filtros e classificação.
Filtrar resultados
Os atributos de campo de índice determinam quais campos são pesquisáveis, filtráveis, classificáveis, compatíveis e recuperáveis. No índice de amostra de hotéis, os campos filtráveis incluem Categoria, Endereço/Cidade e Endereço/EstadoProvíncia. Este exemplo adiciona uma expressão $Filter em Category.
Um filtro sempre é executado primeiro, seguido por uma consulta supondo que uma seja especificada.
Abra o
HomeController
e localize o método RunQueryAsync . Adicionar filtro avar options = new SearchOptions()
:private async Task<ActionResult> RunQueryAsync(SearchData model) { InitSearch(); var options = new SearchOptions() { IncludeTotalCount = true, Filter = "search.in(Category,'Budget,Suite')" }; options.Select.Add("HotelName"); options.Select.Add("Category"); options.Select.Add("Rating"); options.Select.Add("Tags"); options.Select.Add("Address/City"); options.Select.Add("Address/StateProvince"); options.Select.Add("Description"); model.resultList = await _searchClient.SearchAsync<Hotel>(model.searchText, options).ConfigureAwait(false); return View("Index", model); }
Execute a aplicação.
Selecione Pesquisar para executar uma consulta vazia. O filtro retorna 18 documentos em vez dos 50 originais.
Para obter mais informações sobre expressões de filtro, consulte Filtros no Azure AI Search e OData $filter sintaxe no Azure AI Search.
Ordenar os resultados
No índice de amostra de hotéis, os campos classificáveis incluem Rating e LastRenovated. Este exemplo adiciona uma expressão $OrderBy ao campo Rating.
Abra o
HomeController
método RunQueryAsync e substitua pela seguinte versão:private async Task<ActionResult> RunQueryAsync(SearchData model) { InitSearch(); var options = new SearchOptions() { IncludeTotalCount = true, }; options.OrderBy.Add("Rating desc"); options.Select.Add("HotelName"); options.Select.Add("Category"); options.Select.Add("Rating"); options.Select.Add("Tags"); options.Select.Add("Address/City"); options.Select.Add("Address/StateProvince"); options.Select.Add("Description"); model.resultList = await _searchClient.SearchAsync<Hotel>(model.searchText, options).ConfigureAwait(false); return View("Index", model); }
Execute a aplicação. Os resultados são ordenados por Classificação por ordem decrescente.
Para obter mais informações sobre classificação, consulte OData $orderby sintaxe no Azure AI Search.
Próximos passos
Neste tutorial, você criou um projeto ASP.NET Core (MVC) que se conectou a um serviço de pesquisa e chamou APIs de pesquisa para filtragem e classificação do lado do servidor.
Se você quiser explorar o código do lado do cliente que responde às ações do usuário, considere adicionar um modelo React à sua solução: