Использование Azure.Search.Documents в приложении .NET на C#

В этой статье объясняется, как создавать и управлять объектами поиска с помощью C# и клиентской библиотеки Azure.Search.Documents (версия 11) в пакете SDK Azure для .NET.

О версии 11

Пакет SDK Azure для .NET включает клиентская библиотека Azure.Search.Documents из команды azure SDK, которая функционально эквивалентна предыдущей клиентской библиотеке Microsoft.Azure.Search. Версия 11 более согласована с точки зрения программирования Azure. Среди примеров — проверка подлинности ключей AzureKeyCredential и System.Text.Json.Serialization для сериализации JSON.

Как и в предыдущих версиях, эту библиотеку можно использовать в следующих целях:

  • Создание поисковых индексов, источников данных, индексаторов, наборов навыков и сопоставлений синонимов и управление ими
  • Загрузка документов поиска в индекс и управление ими
  • Выполнение запросов без необходимости вникать в особенности работы HTTP и JSON
  • Вызов обогащения и управление ими (наборы навыков) и выходные данные

Эта библиотека распространяется как один пакет NuGet Azure.Search.Documents, который включает все интерфейсы API, используемые для программного доступа к службе поиска.

Клиентская библиотека определяет такие классы, как SearchIndex, SearchField и SearchDocument, а также такие операции, как SearchIndexClient.CreateIndex и SearchClient.Search в классах SearchIndexClient и SearchClient. Эти классы упорядочены в следующие подразделы:

Azure.Search.Documents (версия 11) предназначена для спецификации службы поиска 2020-06-30.

Клиентская библиотека не предоставляет операции управления службами, например создание и масштабирование служб поиска и управление ключами API. Если вам нужно управлять ресурсами поиска из приложения .NET, используйте библиотеку Microsoft.Azure.Management.Search в пакете Azure SDK для .NET.

Переход на версию 11

Если вы использовали предыдущую версию пакета SDK для .NET и хотите обновить ее до текущей общедоступной версии, см . статью "Обновление до пакета SDK для .NET для поиска ИИ Azure" версии 11.

Требования к пакету SDK

  • Visual Studio 2019 или более поздней версии.

  • Собственный служба ИИ Azure. Чтобы использовать пакет SDK, вам потребуется имя службы и один или несколько ключей API. Создайте службу на портале, если у вас ее еще нет.

  • Скачайте пакет Azure.Search.Documents (Сервис>Диспетчер пакетов NuGet>Управление пакетами NuGet для решения в Visual Studio). Выполните поиск по имени пакета Azure.Search.Documents.

Пакет SDK Azure для .NET соответствует .NET Standard 2.0.

Пример приложения

В этой статье описывается пример кода DotNetHowTo на сайте GitHub для иллюстрации фундаментальных понятий в службе "Поиск ИИ Azure", в частности, создание, загрузка и запрос индекса поиска.

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

Вот основная программа, в которой реализован общий поток операций.

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

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

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

Типы клиентов

В клиентской библиотеке используется три типа клиентов для выполнения различных операций: SearchIndexClient для создания, обновления и удаления индексов, SearchClient для загрузки или запроса индекса, а также SearchIndexerClient для работы с индексаторами и навыками. В этой статье рассматриваются первые два.

Всем клиентам требуются как минимум имя службы или конечная точка, а также ключ API. Обычно эти сведения задаются в файле конфигурации аналогично файлу appsettings.json в примере приложения DotNetHowTo. Для чтения данных из файла конфигурации добавьте в программу using Microsoft.Extensions.Configuration;.

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

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

Следующий оператор создает клиент поиска, используемый для загрузки документов или выполнения запросов. Для SearchClient требуется индекс. Для загрузки документов потребуется ключ API администратора, но для выполнения запросов можно использовать ключ API запросов.

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

Примечание.

Если указать недействительный ключ для импорта (например, ключ запроса вместо ключа администратора), SearchClient вызовет CloudException с сообщением об ошибке "Доступ запрещен" при первом вызове метода операции. В этом случае проверьте ключ API еще раз.

Удаление индекса

На ранних стадиях разработки может потребоваться добавить инструкцию DeleteIndex для удаления рабочего индекса, чтобы затем создать его повторно с обновленным определением. Пример кода для поиска ИИ Azure часто включает шаг удаления, чтобы повторно запустить пример.

В следующей строке вызывается DeleteIndexIfExists:

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

Этот метод использует заданный SearchIndexClient, чтобы проверить наличие индекса и удалить его, если он существует.

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.");

Примечание.

В примере кода в этой статье для простоты используются синхронные методы, но в реальных приложениях рекомендуем вам применять для масштабируемости и скорости асинхронные методы. Например, в методе выше можно использовать DeleteIndexAsync вместо DeleteIndex.

Создание индекса

Для создания индекса можно использовать SearchIndexClient.

Метод ниже создает новый объект SearchIndex со списком объектов SearchField, который определяет схему нового индекса. Каждое поле имеет имя, тип данных и несколько атрибутов, которые определяют его поведение при поиске.

Поля можно определить из класса модели с помощью FieldBuilder. Класс FieldBuilder использует отражение, чтобы создать список объектов SearchField для соответствующего индекса. Он проверяет общедоступные свойства и атрибуты полученного класса модели Hotel. Класс Hotel мы рассмотрим подробно позже.

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

Помимо полей, вы также можете добавить в индекс профили оценивания, средства подбора или параметры CORS (для краткости они не включены в пример). Дополнительные сведения об объекте SearchIndex и его составных частях см. в списке свойств SearchIndex, а также в справочнике по REST API.

Примечание.

При необходимости вы всегда можете создать список объектов Field напрямую, а не с помощью FieldBuilder. Это полезно, например, если вы не хотите использовать класс модели или используете существующий класс модели, в который не хотите добавлять атрибуты.

Вызов CreateIndex в Main()

Main создает новый индекс hotels, вызывая приведенный выше метод:

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

Использование класса модели для представления данных

В примере DotNetHowTo для структур данных Hotel (Отель), Address (Адрес) и Room (Номер) используются классы моделей. Hotel ссылается на Address, сложный одноуровневый тип (многокомпонентное поле), и Room (коллекцию многокомпонентных полей).

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

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

Альтернативный подход — добавление полей непосредственно в индекс. В следующем примере показано лишь несколько полей.

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

Определения полей

Модель данных в .NET и соответствующую ей схему индексов должны поддерживать возможности поиска, которые нужно предоставить конечному пользователю. Каждый объект верхнего уровня в .NET, например документ в поисковом индексе, соответствует результату поиска, который будет представлен в пользовательском интерфейсе. Например, в приложении поиска гостиниц конечные пользователи могут захотеть искать по имени гостиницы, возможностям в ней или характеристикам определенной комнаты.

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

Рассмотрим следующий фрагмент кода, который извлекает несколько определений полей из класса Hotel. Обратите внимание, что Address и Room являются типами C# с собственными определениями классов (просмотреть их можно в примере кода). Оба эти типа являются сложными. Подробнее см. в статье Как моделировать сложные типы.

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

Выбор класса поля

При создании полей можно использовать базовый класс SearchField или производные вспомогательные модели, которые служат "шаблонами" с предварительно настроенными свойствами.

Ключом документа (IsKey = true) должно служить только одно поле в индексе. Оно должно быть строкой и должно уникальным образом идентифицировать каждый документ. Это также необходимо для того IsHidden = true, чтобы иметь , что означает, что он не может быть видимым в результатах поиска.

Тип поля Описание и использование
SearchField Базовый класс, для большинства свойств которого задано значение null, за исключением свойств Name (оно обязательно) и AnalyzerName (значение по умолчанию — Standard Lucene).
SimpleField Вспомогательная модель. Может относиться к любому типу данных, всегда недоступна для поиска (игнорируется в запросах полнотекстового поиска) и ее можно извлечь (она не скрыта). Другие атрибуты отключены по умолчанию, но их можно включить. SimpleField можно использовать для идентификаторов документов или полей, используемых только в фильтрах, аспектах или профилях оценки. Если это так, обязательно примените все необходимые для сценария атрибуты, например IsKey = true для идентификатора документа. Чтобы узнать больше, см. SimpleFieldAttribute.cs в исходном коде.
SearchableField Вспомогательная модель. Должна быть строкой и всегда доступна для поиска и извлечения. Другие атрибуты отключены по умолчанию, но их можно включить. Так как этот тип поля доступен для поиска, он поддерживает синонимы и полное дополнение свойств анализатора. Чтобы узнать больше, см. SearchableFieldAttribute.cs в исходном коде.

Независимо от того, используется ли базовый API SearchField или одна из вспомогательных моделей, необходимо явно включить атрибуты фильтров, аспектов и сортировки. Например, IsFilterable, IsSortable и IsFacetable должны присваиваться явным образом, как в примере выше.

Добавление атрибутов полей

Обратите внимание, что каждое поле обладает атрибутами, такими как IsFilterable, IsSortable, IsKey и AnalyzerName. Эти атрибуты сопоставляют непосредственно с соответствующими атрибутами поля в индексе поиска ИИ Azure. Класс FieldBuilder на основе этих свойств создает определения полей для индекса.

Сопоставление типов полей

Типы .NET свойств сопоставляются с эквивалентными типами полей в определении индекса. Например, свойство строки Category сопоставляется с полем category, которое имеет тип Edm.String. Аналогичные сопоставления присутствуют между типами bool?, Edm.Boolean, DateTimeOffset?, Edm.DateTimeOffset и т. д.

Вы заметили свойство SmokingAllowed?

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

Атрибут JsonIgnore в этом свойстве указывает для FieldBuilder, что его не следует сериализовать в индекс в качестве поля. Это отличный способ создания вычисляемых свойств на стороне клиента, которые можно использовать в качестве вспомогательных функций в приложении. В этом случае свойство SmokingAllowed отражает, разрешено ли курение в каком-либо номере Room из коллекции Rooms. Если все являются ложными, это означает, что весь отель не разрешает курить.

Загрузка индекса.

Следующий шаг заполняет Main только что созданный индекс "hotels". Эта популяция индекса выполняется в следующем методе: (Некоторый код заменен на "..." в целях иллюстрации. Полный пример решения для полного кода заполнения данных.)

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

Этот метод состоит из четырех частей. Первый создает массив из трех объектов с тремя HotelRoom объектами, которые будут служить входными данными для отправки в индекс. Эти данные жестко запрограммированы для простоты. В фактическом приложении данные, скорее всего, будут поступать из внешнего источника данных, например базы данных SQL.

Вторая часть создает IndexDocumentsBatch, содержащий документы. Вы указываете операцию, которую необходимо применить к пакету во время его создания, в данном случае путем вызова метода IndexDocumentsAction.Upload. Затем пакет отправляется в индекс поиска ИИ Azure с помощью IndexDocuments метода.

Примечание.

В этом примере мы просто отправляем документы. Если требуется внести изменения в существующие документы или удалить документы, можно создать пакеты, вызвав методы IndexDocumentsAction.Merge, IndexDocumentsAction.MergeOrUpload или IndexDocumentsAction.Delete. Вы также можете смешивать различные операции в одном пакете, вызвав IndexBatch.Newколлекцию IndexDocumentsAction объектов, каждый из которых сообщает службе "Поиск ИИ Azure" выполнить определенную операцию в документе. Можно создать каждый IndexDocumentsAction с отдельной операцией, вызвав соответствующий метод, например IndexDocumentsAction.Merge, IndexAction.Upload и т. д.

Третья часть метода — это блок catch, который обрабатывает важные ошибки индексирования. Если служба поиска не сможет индексировать некоторые документы в пакете, RequestFailedException создается исключение. Исключение может произойти, если вы индексируете документы, пока служба находится под большой нагрузкой. Настоятельно рекомендуется явно обрабатывать этот случай в коде. Вы можете задержать и повторить попытку индексирования соответствующих документов либо занести ошибку в журнал и продолжить работу, как в нашем примере, а также выполнить другие действия в зависимости от требований вашего приложения к целостности данных. Альтернативой является использование SearchIndexingBufferedSender для интеллектуальной пакетной обработки, автоматической очистки и повторных попыток для неудачных действий индексирования. Дополнительные сведения см . в этом примере .

И наконец, метод UploadDocuments вызывает задержку на две секунды. Индексирование в службе поиска происходит асинхронно, поэтому образец приложения должен подождать немного, пока документы не станут доступными для поиска. Такие задержки обычно необходимы только в демонстрациях, тестах и примерах приложений.

Вызов UploadDocuments в Main()

Следующий фрагмент кода настраивает экземпляр SearchClient с помощью метода GetSearchClient в indexClient. indexClient использует для своих запросов ключ API администратора, который необходим для загрузки или обновления документов.

Альтернативный подход — прямой вызов SearchClient с передачей ключа API администратора в AzureKeyCredential.

SearchClient searchClient = indexClient.GetSearchClient(indexName);

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

Выполнение запросов

Сначала настройте SearchClient конечную точку службы и ключ API запросов из 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;
}

Затем определите метод, отправляющий запрос.

Каждый раз, когда метод выполняет запрос, он создает новый объект SearchOptions. Этот объект позволяет задать дополнительные параметры запроса, например сортировку, фильтрацию, разбиение на страницы и фасетизацию. В этом методе мы задаем свойства Filter, Select и OrderBy для разных запросов. Дополнительные сведения о синтаксисе выражений запросов см. в статье Синтаксис простых запросов.

Следующий шаг — выполнение запросов. Выполнение поиска выполняется с помощью метода SearchClient.Search. Для каждого запроса передайте текст поиска в качестве строки (или "*" если нет текста поиска), а также параметры поиска, созданные ранее. Мы также указываем Hotel как параметр типа SearchClient.Search, в результате чего пакет SDK будет десериализовать документы в результатах поиска к объектам типа 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);
}

В-третьих, определите метод, записывающий ответ с выводом каждого документа на консоль:

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

    Console.WriteLine();
}

Вызов RunQueries в Main()

SearchClient indexClientForQueries = CreateSearchClientForQueries(indexName, configuration);

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

Изучение конструкций запросов

Давайте подробнее рассмотрим каждый из запросов. Ниже приведен код для выполнения первого запроса:

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

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

WriteDocuments(results);

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

Name: Secret Point Motel

Name: Twin Dome Motel

Во втором запросе используйте фильтр для выбора комнат с ночной ставкой менее $ 100. Запрос возвращает в результатах только идентификатор отеля и описание:

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

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

Запрос выше использует выражение OData $filter (по значению Rooms/any(r: r/BaseRate lt 100)), чтобы отфильтровать в индексе нужные документы. При этом для применения "BaseRate lt 100" к каждому элементу в коллекции комнат используется оператор any. Дополнительные сведения см. в статье о синтаксисе фильтров OData.

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

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

В последнем запросе мы найдем все имена гостиниц, отвечающие запросу со словом "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);

Этот раздел завершает знакомство с пакетом SDK для .NET, однако не стоит останавливаться. В следующем разделе приведены другие ресурсы для получения дополнительных возможностей по программированию с помощью службы "Поиск ИИ Azure".

Следующие шаги