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


Руководство. Создание первого приложения поиска в Когнитивном поиске 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();

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

Поиск *pool*

Обзор

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

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

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

Предпосылки

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

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

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

  2. В корневой папке выберите Код, затем Клонировать или Скачать ZIP, чтобы сделать личную локальную копию проекта.

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

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

    Поиск *вай-фай*

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

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

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

  1. В Visual Studio выберите новый>проект, а затем ASP.NET Core Web App (model-View-Controller).

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

  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). Тип выходных данных является критически важным, SearchResults<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;
        }
    }
    

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

Шаблоны проектов имеют ряд клиентских представлений, расположенных в папке "Представления ". Точные представления зависят от используемой версии 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 по умолчанию.

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

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

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

  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;
    

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

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

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

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

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

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

Обратите внимание на обработку ошибок и другие представления и методы по умолчанию

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

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

В примере 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 определяет поля, возвращаемые в результатах, и таким образом соответствует именам свойств в классе отеля . Если вы не используете Select, возвращаются все нескрытые поля, что может быть неэффективно, если вас интересует только подмножество всех возможных полей.

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

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

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

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

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

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

    Поиск *beach*

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

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

  5. Попробуйте иными словами: "бассейн", "солнце", "вид" и все, что угодно. Вы увидите, что Когнитивный поиск Azure работает на самом простом, но по-прежнему убедительным уровнем.

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

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

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

  2. Запустите приложение, введите "бар" в качестве текста для поиска и нажмите на значок поиска. Исключение должно привести к отображению вида ошибки.

    Принудительное создание ошибки

    Это важно

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

  3. Удалите Throw new Exception(), когда убедитесь, что обработка ошибок работает правильно.

Общие выводы

Рассмотрим следующие выносы из этого проекта:

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

Дальнейшие шаги

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

Эти дальнейшие действия рассматриваются в оставшихся руководствах. Начнем с разбиения по страницам.