Поделиться через


Руководство. Создание первого приложения поиска в Когнитивный поиск Azure с помощью пакета SDK для .NET

В этом руководстве показано, как создать веб-приложение, которое выполняет запрос и возвращает результаты из индекса поиска с помощью Когнитивного поиска Azure и Visual Studio.

Из этого руководства вы узнаете, как выполнять следующие задачи:

  • Настройка среды разработки
  • Моделирование структур данных
  • Создание веб-страницы для сбора входных данных запроса и отображения результатов.
  • Определение метода поиска.
  • Тестирование приложения

Вы также узнаете, насколько простой поисковый вызов. Ключевые операторы в коде инкапсулируются в следующих нескольких строках:

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();

Только один вызов запрашивает индекс поиска и возвращает результаты.

Поиск по запросу

Обзор

В этом учебнике используется функция hotels-sample-index, которую можно быстро создать в собственной службе поиска с помощью Краткого руководства по импорту данных. Этот индекс содержит вымышленные данные об отелях, доступные в качестве встроенного источника данных в каждой службе поиска.

В первом уроке этого учебника создается базовая структура запроса и страница поиска, которые улучшаются в последующих уроках, включая разбиение на страницы, аспекты и автозавершение.

Готовую версию кода можно найти в следующем проекте:

Предварительные требования

Установка и запуск проекта из GitHub

Если вы хотите сразу перейти к рабочему приложению, выполните приведенные ниже действия, чтобы скачать и выполнить готовый код.

  1. Найдите пример приложения на GitHub: Создание первого приложения.

  2. В корневой папке выберите Code (Код), а затем — Clone (Клонировать) или Download ZIP (Скачать ZIP-файл), чтобы создать собственную локальную копию проекта.

  3. В Visual Studio откройте базовую страницу поиска этого решения (1-basic-search-page) и выберите Запуск без отладки (или нажмите клавишу F5), чтобы выполнить сборку программы и запустить ее.

  4. Это индекс отелей, поэтому введите некоторые слова, которые можно использовать для поиска отелей (например, "wifi", "view", "bar", "parking"). Изучите результаты.

    Поиск по запросу

Основные компоненты для более сложных поисковых запросов включены в это приложение. Если вы не знакомы с разработкой поиска, вы можете повторно создать это приложение, чтобы изучить рабочий процесс. В следующих разделах показано, как это сделать.

Настройка среды разработки

Чтобы создать этот проект с нуля и тем самым укрепить концепции Когнитивный поиск Azure, начните с проекта Visual Studio.

  1. В Visual Studio выберите Новый>проект, а затем ASP.NET Core Веб-приложение (модель-представление-контроллер).

    Создание облачного проекта

  2. Присвойте проекту имя, например "FirstSearchApp", и задайте расположение. Выберите Далее.

  3. Примите значения по умолчанию для целевой платформы, типа проверки подлинности и HTTPS. Нажмите кнопку создания.

  4. Установите клиентскую библиотеку. В разделе Средства> Диспетчер >пакетов NuGetУправление пакетами NuGet для решения... выберите Обзор и выполните поиск по запросу azure.search.documents. Установите пакет Azure.Search.Documents (версии 11 или более поздней версии), приняв условия лицензионного соглашения и использования зависимостей.

    Добавление библиотек Azure с помощью NuGet

На этом шаге задайте конечную точку и ключ доступа для подключения к службе поиска, которая предоставляет пример индекса отелей.

  1. Откройте appsettings.json и замените строки по умолчанию URL-адресом службы поиска (в формате https://<service-name>.search.windows.net) и ключом администратора или API службы поиска. Поскольку вам не нужно создавать или обновлять индекс, для выполнения заданий из этого учебника можно использовать ключ запроса.

    {
        "SearchServiceUri": "<YOUR-SEARCH-SERVICE-URI>",
        "SearchServiceQueryApiKey": "<YOUR-SEARCH-SERVICE-API-KEY>"
    }
    
  2. Выберите файл в обозревателе решений и в окне "Свойства" измените значение параметра Копировать в выходной каталог на Копировать более новые.

    Копирование параметров приложения в выходные данные

Моделирование структур данных

Модели (классы C#) используются для обмена данными между клиентом (представление), сервером (контроллер) и облаком Azure с использованием архитектуры MVC (модель, представление и контроллер). Как правило, эти модели отражают структуру данных, к которым осуществляется доступ.

На этом шаге вы будете моделировать структуры данных индекса поиска, а также строку поиска, используемую при взаимодействии между представлением и контроллером. В индексе гостиниц каждая гостиница содержит много номеров, и у каждой гостиницы есть адрес, состоящий из нескольких частей Полное представление гостиницы является иерархической вложенной структурой данных. Для создания каждого компонента потребуется три класса.

Набор классов Hotel, Address и Room называется сложным типом. Это очень важная особенность в Когнитивном поиске Azure. Сложные типы могут иметь много уровней вложенности, классов и подклассов, поддерживая намного более сложные структуры данных, чем простые типы (классы, содержащие только примитивные члены).

  1. В обозревателе решений щелкните правой кнопкой мыши и выберите Модели>Добавить>Новый элемент.

  2. Выберите Класс и присвойте элементу имя Hotel.cs. Замените содержимое файла Hotel.cs кодом, приведенным ниже. Обратите внимание на элементы Address и Room класса, эти поля сами являются классами, поэтому вам также понадобятся модели для них.

    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. Повторите тот же процесс создания модели для класса Address, только назовите файл Address.cs. Замените содержимое этого файла следующим кодом.

    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. И еще раз повторите процедуру для класса Room, используя имя файла 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. Последняя модель, которую вы создадите в этом руководстве, — это класс SearchData, который представляет входные данные пользователя (searchText) и выходные данные поиска (resultList). Тип выходных данных (SearchResult<Hotel> ) критически важен, так как он точно соответствует результатам поиска, и эту ссылку необходимо передать в представление. Замените шаблон по умолчанию приведенным ниже кодом.

    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;
        }
    }
    

Создание веб-страницы

Шаблоны проектов содержат несколько клиентских представлений, расположенных в папке Views. Точные представления зависят от используемой версии Core .NET (в этом примере используется версия 3.1). В этом руководстве вы измените Index.cshtml , включив элементы страницы поиска.

Полностью удалите содержимое файла Index.cshtml и создайте его заново, выполнив следующие действия.

  1. В этом руководстве используются два небольших изображения в представлении: логотип Azure и значок лупы (azure-logo.png и search.png). Скопируйте эти изображения из проекта GitHub в папку wwwroot/Images в своем проекте.

  2. Первая строка файла Index.cshtml должна содержать ссылку на модель для обмена данными между клиентом (представлением) и сервером (контроллером). Это созданная ранее модель SearchData. Добавьте эту строку в файл Index.cshtml.

    @model FirstAzureSearchApp.Models.SearchData
    
  3. Обычно ввести заголовок представления, поэтому следующие строки должны быть следующими:

    @{
        ViewData["Title"] = "Home Page";
    }
    
  4. После заголовка введите ссылку на таблицу стилей HTML, которую вы создадите в ближайшее время.

    <head>
        <link rel="stylesheet" href="~/css/hotels.css" />
    </head>
    
  5. Текст представления обрабатывает два варианта использования. Во-первых, при первом вводе искомого текста необходимо предоставить пустую страницу. Во-вторых, помимо текстового поля поиска, нужно обрабатывать результаты для повторных запросов.

    Чтобы реализовать эти два варианта, нужно проверять, содержит ли переданная в представление модель значение NULL. Модель со значением NULL указывает на первый вариант использования (исходный запуск приложения). Добавьте следующий код в файл Index.cshtml и просмотрите комментарии.

    <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. Добавьте таблицу стилей. В Visual Studio выберите Файл>Создать>Файл, а затем — Таблица стилей (при выделенном параметре Общие).

    Замените код по умолчанию приведенным ниже: Мы не будем вдаваться в этот файл более подробно, стили являются стандартными HTML.

    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. Сохраните файл таблицы стилей с именем hotels.css в папку wwwroot/css рядом с предложенным по умолчанию файлом site.css.

Этим мы завершаем подготовку представления. На этом этапе модели и представления полностью готовы. Осталось лишь добавить контроллер.

Определение методов

На этом шаге измените содержимое HomeController.

  1. Откройте файл HomeController.cs и замените операторы using следующим кодом.

    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;
    

Добавление методов Index

В приложении MVC метод Index() является методом действия по умолчанию для любого контроллера. Он открывает HTML-страницу индекса. Метод по умолчанию, который не принимает параметры, используется в этом руководстве для варианта первого запуска приложения, то есть для подготовки пустой страницы поиска.

В этом разделе мы расширяем данный метод для поддержки второго варианта использования, то есть для отображения страницы после того, как пользователь ввел искомый текст. Для поддержки этого варианта метод Index расширяется, чтобы принимать модель в качестве параметра.

  1. Добавьте следующий метод после уже существующего метода Index() .

        [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);
        }
    

    Обратите внимание, что метод объявлен как async и содержит вызов await для RunQueryAsync. Эти ключевые слова позволяют выполнять асинхронные вызовы, чтобы избежать блокирования потоков на сервере.

    Блок catch использует созданную модель ошибок по умолчанию.

Изучение механизма обработки ошибок и других представлений и методов, созданных по умолчанию

В зависимости от используемой версии .NET Core создается несколько другой набор представлений по умолчанию. Для платформы .NET Core 3.1 по умолчанию создаются представления Index, Privacy и Error. Вы можете просмотреть эти страницы по умолчанию при запуске приложения и проверить, как они обрабатываются в контроллере.

Вы будете тестировать представление ошибки далее в этом руководстве.

В примере на GitHub неиспользуемые представления и связанные с ними действия удалены.

Добавление метода RunQueryAsync

Вызов к Когнитивному поиску Azure инкапсулируется в метод RunQueryAsync.

  1. Сначала добавьте статические переменные, которые настраивают службу Azure, и вызов для их запуска.

        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. Теперь добавьте метод 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);
    }
    

    В этом методе сначала нужно убедиться, что конфигурация Azure успешно инициирована, а затем — настроить несколько параметров поиска. Параметр Select указывает, какие поля должны возвращаться в результатах, что соответствуют именам свойств в классе hotel. Если не выбрать , возвращаются все незахваченные поля, что может оказаться неэффективным, если вас интересует только подмножество всех возможных полей.

    Асинхронный вызов для поиска формирует запрос (моделируемый как searchText) и ответ (моделируемый как searchResult). При отладке этого кода класс SearchResult является хорошим кандидатом для установки точки останова, если необходимо изучить содержимое model.resultList. Вы должны обнаружить, что это интуитивно понятно, предоставляя только запрашиваемые данные, а не многое другое.

Тестирование приложения

Теперь давайте проверим, правильно ли выполняется приложение.

  1. Выберите Отладка>Запустить без отладки или нажмите клавишу F5. Если приложение выполнится ожидаемым образом, отобразится исходное представление индекса.

    Открытие приложения

  2. Введите строку запроса, например "beach" (или любой другой вариант, который придет вам в голову), и щелкните значок поиска, чтобы отправить запрос.

    Поиск по запросу

  3. Попробуйте ввести five star (пятизвездочный). Обратите внимание на то, что этот запрос не возвращает результатов. Более сложный поиск будет воспринимать "пятизвездочный" как синоним слова "первоклассный" и возвратит соответствующие результаты. Поддержка синонимов доступна в Когнитивном поиске Azure, но не рассматривается в этой серии руководств.

  4. Выполните поиск по запросу hot (горячий). Он не возвращает записи со словом "hotel" в них. Наша функция поиска умеет искать только целые слова, и результатов здесь будет мало.

  5. Поэкспериментируйте с разными словами: pool (бассейн), sunshiny (солнечный), view (вид) и так далее. Вы увидите, что Когнитивный поиск Azure работает на самом простом, но все же убедительном уровне.

Проверка пограничных условий и ошибок

Важно убедиться, что функции обработки ошибок работают должным образом, даже если все работает идеально.

  1. В методе Index после вызова try { введите строку Throw new Exception() . Это исключение создаст ошибку при поиске любого текста.

  2. Откройте приложение, введите для поиска слово bar и щелкните значок поиска. Исключение сразу откроет представление ошибки.

    Принудительный вызов ошибки

    Важно!

    Возврат внутренних номеров ошибок на страницах ошибок считается угрозой безопасности. Если приложение предназначено для общего использования, следуйте рекомендациям по обеспечению безопасности, чтобы узнать, что следует возвращать при возникновении ошибки.

  3. Удалить исключение Исключения(), если вы удовлетворены тем, что обработка ошибок работает должным образом.

Общие выводы

По результатам этого проекта можно сделать такие выводы:

  • Вызов Когнитивный поиск Azure является кратким, и его легко интерпретировать результаты.
  • Асинхронные вызовы усложняют контроллер, но это рекомендуется для повышения производительности.
  • Это приложение выполняет простой поиск текста, определяемый тем, что настроено в searchOptions. Но наш единственный класс может быть заполнен большим числом элементов, что повышает сложность поиска. Немного больше работы, вы можете сделать это приложение значительно более мощным.

Дальнейшие действия

Чтобы повысить удобство работы пользователей, можно добавить еще несколько функций, в частности разбивку на страницы (по номерам страниц или с бесконечной прокруткой) и автозаполнение с поисковыми подсказками. Можно также рассмотреть другие варианты поиска (например, географический поиск по отелям в заданном радиусе от заданной точки) и упорядочивание результатов поиска.

Это рассматривается в оставшихся руководствах серии. Давайте начнем с разбивки на страницы.