Как использовать Microsoft.Azure.Search в приложении .NET на C#
В этой статье объясняется, как создавать объекты поиска и управлять ими с помощью C# и устаревшей клиентской библиотеки Microsoft.Azure.Search (версия 10) в пакете Azure SDK для .NET.
Версия 10 — это последняя версия пакета Microsoft.Azure.Search. После перемещения новые функции будут выдаваться в Azure.Search.Documents из группы Azure SDK.
Примечание
Если у вас есть существующие или разрабатываемые проекты, в них можно и далее использовать версию 10. Для всех новых проектов и для применения новых возможностей следует перейти на новую библиотеку.
О версии 10
Пакет SDK содержит клиентские библиотеки, позволяющие управлять индексами, источниками данных, индексаторами и картами синонимов, а также отправлять документы, управлять ими и выполнять запросы, не вникая в детали HTTP и JSON. Эти клиентские библиотеки распределяются как пакеты NuGet.
Главный пакет NuGet — Microsoft.Azure.Search
. Он представляет собой мета пакет, содержащий все остальные пакеты в виде зависимостей. Используйте этот пакет, если вы только начинаете работу или знаете, что приложение потребует всех функций Когнитивного поиска Azure.
Ниже перечислены другие пакеты NuGet в пакете SDK.
-
Microsoft.Azure.Search.Data
: используйте этот пакет, если вы разрабатываете приложение .NET с помощью службы "Когнитивный поиск Azure" и вам необходимо только запрашивать и обновлять документы в индексах. Если требуется также создавать или обновлять индексы, карты синонимов и другие ресурсы уровня службы, используйте пакетMicrosoft.Azure.Search
. -
Microsoft.Azure.Search.Service
: используйте этот пакет, если вы разрабатываете автоматизацию в .NET для управления индексами, картами синонимов, индексаторами, источниками данных службы "Когнитивный поиск Azure" или другими ресурсами уровня службы. Если требуется только запрашивать и обновлять документы в индексах, используйте пакетMicrosoft.Azure.Search.Data
. Чтобы использовать все функциональные возможности Когнитивного поиска Azure, используйте пакетMicrosoft.Azure.Search
. -
Microsoft.Azure.Search.Common
: общие типы, необходимые библиотекам .NET службы "Когнитивный поиск Azure". Вам не нужно использовать этот пакет прямо в приложении. Подразумевается, что он используется только в качестве зависимости.
Различные клиентские библиотеки определяют такие классы, как Index
, Field
и Document
, а также такие операции, как Indexes.Create
и Documents.Search
в классах SearchServiceClient
и SearchIndexClient
. Эти классы упорядочены в следующие подразделы:
Если вы хотите оставить отзыв о будущем обновлении пакета SDK, ознакомьтесь со страницей отзывов или задайте вопрос на сайте GitHub и упомяните Когнитивный поиск Azure в названии проблемы.
Пакет SDK для .NET используется для версии 2019-05-06
интерфейса REST API Когнитивного поиска Azure. Эта версия включает поддержку сложных типов, обогащения с помощью ИИ, автозаполнения и режима синтаксического анализа JsonLines при индексировании больших двоичных объектов Azure.
Этот пакет SDK не поддерживает такие операции управления, как создание и масштабирование служб поиска или управление ключами API. Если вы хотите управлять ресурсами службы поиска из приложения .NET, можно использовать пакет SDK управления .NET для Когнитивного поиска Azure.
Обновление до версии 10
Если вы уже используете старую версию пакета SDK для .NET для Когнитивного поиска Azure и хотите обновиться до последней общедоступной версии, ознакомьтесь с инструкциями в этой статье .
Требования пакета SDK
- Visual Studio 2017 или более поздней версии.
- Ваша собственная служба "Когнитивный поиск Azure". Чтобы использовать пакет SDK, необходимо указать имя службы и один или несколько ключей API. Создание службы на портале поможет вам это сделать.
- Скачайте пакет SDK для Когнитивного поиска Azure Search в .NET в виде пакета NuGet с помощью команды "Управление пакетами NuGet" в Visual Studio. Достаточно найти имя пакета
Microsoft.Azure.Search
на сайте NuGet.org (или одно из указанных выше имен пакета, если требуется только подмножество функциональных возможностей).
Пакет SDK для .NET для Когнитивного поиска Azure поддерживает приложения, предназначенные для .NET Framework 4.5.2 и более поздних версий, а также .NET Core 2.0 и более поздних версий.
Основные сценарии
В приложении поиска необходимо выполнить несколько действий. В данном руководстве мы обсудим эти основные сценарии:
- Создание индекса
- Заполнение индекса документами
- Поиск документов с помощью полнотекстового поиска и фильтров
Следующий пример кода демонстрирует каждый из этих сценариев. Вы можете использовать фрагменты кода в собственном приложении.
Обзор
Образец приложения, который мы будем рассматривать, создает новый индекс с именем 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();
SearchServiceClient serviceClient = CreateSearchServiceClient(configuration);
string indexName = configuration["SearchIndexName"];
Console.WriteLine("{0}", "Deleting index...\n");
DeleteIndexIfExists(indexName, serviceClient);
Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, serviceClient);
ISearchIndexClient indexClient = serviceClient.Indexes.GetClient(indexName);
Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(indexClient);
ISearchIndexClient indexClientForQueries = CreateSearchIndexClient(configuration);
RunQueries(indexClientForQueries);
Console.WriteLine("{0}", "Complete. Press any key to end application...\n");
Console.ReadKey();
}
Примечание
Полный исходный код образца приложения, используемого в этом пошаговом руководстве, см. в репозитории GitHub.
Мы рассмотрим ее шаг за шагом. Для начала необходимо создать SearchServiceClient
. Этот объект позволяет управлять индексами. Чтобы создать его, необходимо указать имя службы когнитивного поиска Azure и ключ API администратора. Эту информацию можно указать в файле appsettings.json
из примера приложения.
private static SearchServiceClient CreateSearchServiceClient(IConfigurationRoot configuration)
{
string searchServiceName = configuration["SearchServiceName"];
string adminApiKey = configuration["SearchServiceAdminApiKey"];
SearchServiceClient serviceClient = new SearchServiceClient(searchServiceName, new SearchCredentials(adminApiKey));
return serviceClient;
}
Примечание
Если указать неправильный ключ (например, ключ запроса вместо ключа администратора), SearchServiceClient
вызовет CloudException
с сообщением об ошибке "Доступ запрещен" при первом вызове метода операции, например Indexes.Create
. В этом случае проверьте ключ API еще раз.
Следующие несколько строк вызывают методы для создания индекса под названием hotels, предварительно удалив его, если он уже существует. Мы рассмотрим эти методы немного позже.
Console.WriteLine("{0}", "Deleting index...\n");
DeleteIndexIfExists(indexName, serviceClient);
Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, serviceClient);
Затем необходимо заполнить индекс. Чтобы заполнить индекс, нам потребуется SearchIndexClient
. Его можно получить двумя способами: создать его самостоятельно или вызвать метод Indexes.GetClient
в SearchServiceClient
. Для удобства мы используем второй способ.
ISearchIndexClient indexClient = serviceClient.Indexes.GetClient(indexName);
Примечание
В типичном приложении поиска управление индексами и его заполнение может обрабатываться отдельным компонентом из поисковых запросов. Элемент Indexes.GetClient
удобен для заполнения индекса, так как он избавляет от необходимости указывать дополнительный элемент SearchCredentials
. Это достигается благодаря передачи ключа администратора, который использовался для создания SearchServiceClient
, в новый элемент SearchIndexClient
. Однако в части приложения, которая выполняет запросы, лучше создать SearchIndexClient
напрямую, чтобы передать ключ запроса, что позволит только читать данные в отличие от ключа администратора. Это согласуется с принципом наименьших прав доступа и поможет сделать приложение более безопасным. Дополнительные сведения о ключах администраторов и запросов см. здесь.
Теперь, когда у нас есть SearchIndexClient
, мы можем заполнить индекс. Заполнение индекса выполняется другим методом, который мы рассмотрим позже.
Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(indexClient);
Наконец, мы выполним несколько поисковых запросов и отобразим результаты. На этот раз мы используем другой метод SearchIndexClient
:
ISearchIndexClient indexClientForQueries = CreateSearchIndexClient(indexName, configuration);
RunQueries(indexClientForQueries);
Метод RunQueries
мы позже рассмотрим подробно. Это код для создания нового объекта SearchIndexClient
:
private static SearchIndexClient CreateSearchIndexClient(string indexName, IConfigurationRoot configuration)
{
string searchServiceName = configuration["SearchServiceName"];
string queryApiKey = configuration["SearchServiceQueryApiKey"];
SearchIndexClient indexClient = new SearchIndexClient(searchServiceName, indexName, new SearchCredentials(queryApiKey));
return indexClient;
}
На этот раз мы воспользуемся ключом запроса, так как доступ на запись к индексу не требуется. Эту информацию можно указать в файле appsettings.json
из примера приложения.
Если запустить это приложение с допустимым именем службы и ключами API, выходные данные должны выглядеть, как в следующем примере (некоторые выходные данные консоли были заменены на "..." в целях иллюстрации):
Deleting index...
Creating index...
Uploading documents...
Waiting for documents to be indexed...
Search the entire index for the term 'motel' and return only the HotelName field:
Name: Secret Point Motel
Name: Twin Dome Motel
Apply a filter to the index to find hotels with a room cheaper than $100 per night, and return the hotelId and description:
HotelId: 1
Description: The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Times Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.
HotelId: 2
Description: The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts.
Search the entire index, order by a specific field (lastRenovationDate) in descending order, take the top two results, and show only hotelName and lastRenovationDate:
Name: Triple Landscape Hotel
Last renovated on: 9/20/2015 12:00:00 AM +00:00
Name: Twin Dome Motel
Last renovated on: 2/18/1979 12:00:00 AM +00:00
Search the hotel names for the term 'hotel':
HotelId: 3
Name: Triple Landscape Hotel
...
Complete. Press any key to end application...
Полный исходный код приложения представлен в конце этой статьи.
Далее мы подробнее рассмотрим каждый из методов, вызываемых элементом Main
.
Создание индекса
После создания SearchServiceClient
Main
удаляет индекс hotels, если он уже существует. Удаление происходит следующим образом:
private static void DeleteIndexIfExists(string indexName, SearchServiceClient serviceClient)
{
if (serviceClient.Indexes.Exists(indexName))
{
serviceClient.Indexes.Delete(indexName);
}
}
Этот метод использует соответствующий SearchServiceClient
, чтобы проверить наличие индекса, и если он существует, удалить его.
Примечание
В примере кода для этой статьи для простоты используются синхронные методы пакета SDK .NET для Когнитивного поиска Azure. Рекомендуем использовать в ваших приложениях асинхронные методы, чтобы сохранить масштабируемость и скорость отклика. Например, в приведенном выше методе можно использовать ExistsAsync
и DeleteAsync
вместо Exists
и Delete
.
Затем Main
создает новый индекс hotels, вызывая следующий метод:
private static void CreateIndex(string indexName, SearchServiceClient serviceClient)
{
var definition = new Index()
{
Name = indexName,
Fields = FieldBuilder.BuildForType<Hotel>()
};
serviceClient.Indexes.Create(definition);
}
Этот метод создает новый объект Index
со списком объектов Field
, который определяет схему нового индекса. Каждое поле имеет имя, тип данных и несколько атрибутов, которые определяют его поведение при поиске. Класс FieldBuilder
использует отражение, чтобы создать список объектов Field
для соответствующего индекса. Он проверяет общедоступные свойства и атрибуты полученного класса модели Hotel
. Класс Hotel
мы рассмотрим подробно позже.
Примечание
При необходимости вы всегда можете создать список объектов Field
напрямую, а не с помощью FieldBuilder
. Это полезно, например, если вы не хотите использовать класс модели или используете существующий класс модели, в который не хотите добавлять атрибуты.
Помимо полей, вы также можете добавить в индекс профили оценивания, средства подбора или параметры CORS (для краткости они не включены в пример). Дополнительные сведения об объекте Index и его составных частях см. в справочнике по SDK, а также в справочнике по REST API Когнитивного поиска Azure.
Заполнение индекса
Следующий шаг в Main
— заполнение созданного индекса. Это заполнение индекса выполняется в следующем методе: (Некоторый код заменен на "..." для иллюстрации. См. полный пример решения для полного кода заполнения данных.)
private static void UploadDocuments(ISearchIndexClient indexClient)
{
var hotels = new Hotel[]
{
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)",
...
}
}
},
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)",
...
}
}
},
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)",
...
}
}
}
};
var batch = IndexBatch.Upload(hotels);
try
{
indexClient.Documents.Index(batch);
}
catch (IndexBatchException e)
{
// 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}",
String.Join(", ", e.IndexingResults.Where(r => !r.Succeeded).Select(r => r.Key)));
}
Console.WriteLine("Waiting for documents to be indexed...\n");
Thread.Sleep(2000);
}
Этот метод состоит из четырех частей. В первой части создается массив из трех объектов Hotel
, каждый из которых имеет три объекта Room
, которые будут служить входными данными для отправки в индекс. Эти данные жестко запрограммированы для простоты. В вашем приложении данные скорее всего будут поступать из внешнего источника, например базы данных SQL.
Вторая часть создает IndexBatch
, содержащий документы. Вы указываете операцию, которую необходимо применить к пакету во время его создания, в данном случае путем вызова метода IndexBatch.Upload
. Затем этот пакет отправляется в индекс службы когнитивного поиска Azure с помощью метода Documents.Index
.
Примечание
В этом примере мы просто отправляем документы. Если требуется внести изменения в существующие документы или удалить документы, можно создать пакеты, вызвав методы IndexBatch.Merge
, IndexBatch.MergeOrUpload
или IndexBatch.Delete
. Можно также смешать различные операции в одном пакете, вызвав метод IndexBatch.New
, принимающий коллекцию объектов IndexAction
, каждый из которых указывает Когнитивному поиску Azure выполнять определенную операцию с документом. Можно создать каждый IndexAction
с отдельной операцией, вызвав соответствующий метод, например IndexAction.Merge
, IndexAction.Upload
и т. д.
Третья часть метода — это блок catch, который обрабатывает важные ошибки индексирования. Если Когнитивному поиску Azure не удается индексировать некоторые документы в пакете, Documents.Index
вызывает IndexBatchException
. Такое исключение может произойти, если вы индексируете документы службы при интенсивной нагрузке. Настоятельно рекомендуется явно обрабатывать этот случай в коде. Вы можете задержать и повторить попытку индексирования соответствующих документов либо занести ошибку в журнал и продолжить работу, как в нашем примере, а также выполнить другие действия в зависимости от требований вашего приложения к целостности данных.
Примечание
С помощью метода FindFailedActionsToRetry
вы можете создать новый пакет, содержащий только те действия, которые не удалось выполнить в предыдущем вызове метода Index
. На сайте StackOverflow опубликовано обсуждение правил его использования.
И наконец, метод UploadDocuments
вызывает задержку на две секунды. Индексирование в службе когнитивного поиска Azure происходит асинхронно, поэтому образец приложения должен подождать немного, пока документы не станут доступными для поиска. Такие задержки обычно необходимы только в демонстрациях, тестах и примерах приложений.
Обработка документов пакетом SDK для .NET
Возможно, вам интересно, как пакет SDK для Когнитивного поиска Azure в .NET может передавать экземпляры пользовательского класса, например Hotel
, в индекс. Чтобы ответить на этот вопрос, рассмотрим класс Hotel
:
using System;
using Microsoft.Azure.Search;
using Microsoft.Azure.Search.Models;
using Microsoft.Spatial;
using Newtonsoft.Json;
public partial class Hotel
{
[System.ComponentModel.DataAnnotations.Key]
[IsFilterable]
public string HotelId { get; set; }
[IsSearchable, IsSortable]
public string HotelName { get; set; }
[IsSearchable]
[Analyzer(AnalyzerName.AsString.EnLucene)]
public string Description { get; set; }
[IsSearchable]
[Analyzer(AnalyzerName.AsString.FrLucene)]
[JsonProperty("Description_fr")]
public string DescriptionFr { get; set; }
[IsSearchable, IsFilterable, IsSortable, IsFacetable]
public string Category { get; set; }
[IsSearchable, IsFilterable, IsFacetable]
public string[] Tags { get; set; }
[IsFilterable, IsSortable, IsFacetable]
public bool? ParkingIncluded { get; set; }
// SmokingAllowed reflects whether any room in the hotel allows smoking.
// The JsonIgnore attribute indicates that a field should not be created
// in the index for this property and it will only be used by code in the client.
[JsonIgnore]
public bool? SmokingAllowed => (Rooms != null) ? Array.Exists(Rooms, element => element.SmokingAllowed == true) : (bool?)null;
[IsFilterable, IsSortable, IsFacetable]
public DateTimeOffset? LastRenovationDate { get; set; }
[IsFilterable, IsSortable, IsFacetable]
public double? Rating { get; set; }
public Address Address { get; set; }
[IsFilterable, IsSortable]
public GeographyPoint Location { get; set; }
public Room[] Rooms { get; set; }
}
Первое, что следует заметить: имя каждого открытого свойства в классе Hotel
будет сопоставляться с полем с тем же именем в определении индекса. Если требуется, чтобы каждое поле начинались с строчной буквы (camel case), пакет SDK может автоматически сопоставлять имена свойств с атрибутом [SerializePropertyNamesAsCamelCase]
в стиле "camel case". Этот сценарий часто встречается в приложениях .NET, которые выполняют привязку данных, когда целевая схема находится вне контроля разработчика приложения без необходимости нарушать рекомендации по именованию стиля "Pascal case" в .NET.
Примечание
Пакет SDK службы когнитивного поиска Azure для .NET использует библиотеку NewtonSoft JSON.NET для сериализации и десериализации настраиваемых объектов модели в JSON и обратно. При необходимости эту сериализацию можно настроить. Дополнительные сведения см. в разделе Пользовательская сериализация с помощью JSON.NET.
Второе, что нужно отметить: каждое свойство дополнено атрибутами, такими как IsFilterable
, IsSearchable
, Key
и Analyzer
. Эти атрибуты сопоставляются непосредственно с аналогичными атрибутами поля в службе когнитивного поиска Azure. Класс FieldBuilder
на основе этих свойств создает определения полей для индекса.
Третий важный аспект класса Hotel
— это типы данных общедоступных свойств. Типы .NET этих свойств сопоставляются с эквивалентными типами полей в определении индекса. Например, свойство строки Category
сопоставляется с полем category
, которое имеет тип Edm.String
. Аналогичные сопоставления присутствуют между типами bool?
, Edm.Boolean
, DateTimeOffset?
, Edm.DateTimeOffset
и т. д. Конкретные правила сопоставления типов указаны в описании метода Documents.Get
в справочнике по SDK для .NET службы когнитивного поиска Azure. Хотя класс FieldBuilder
выполняет это сопоставление, этот механизм полезно понимать на случай, если вам понадобится устранять неполадки, связанные с сериализацией.
Вы заметили свойство SmokingAllowed
?
[JsonIgnore]
public bool? SmokingAllowed => (Rooms != null) ? Array.Exists(Rooms, element => element.SmokingAllowed == true) : (bool?)null;
Атрибут JsonIgnore
в этом свойстве указывает для FieldBuilder
, что его не следует сериализовать в индекс в качестве поля. Это отличный способ создания вычисляемых свойств на стороне клиента, которые можно использовать в качестве вспомогательных функций в приложении. В этом случае свойство SmokingAllowed
отражает, разрешено ли курение в каком-либо номере Room
из коллекции Rooms
. Если все имеют значение false, это означает, что во всех гостиницах курение запрещено.
Некоторые свойства, такие как Address
и Rooms
, являются экземплярами классов .NET. Эти свойства представляют более сложные структуры данных, и в результате для них требуются поля со сложным типом данных в индексе.
Свойство Address
представляет набор из нескольких значений в классе Address
, определенном ниже.
using System;
using Microsoft.Azure.Search;
using Microsoft.Azure.Search.Models;
using Newtonsoft.Json;
namespace AzureSearch.SDKHowTo
{
public partial class Address
{
[IsSearchable]
public string StreetAddress { get; set; }
[IsSearchable, IsFilterable, IsSortable, IsFacetable]
public string City { get; set; }
[IsSearchable, IsFilterable, IsSortable, IsFacetable]
public string StateProvince { get; set; }
[IsSearchable, IsFilterable, IsSortable, IsFacetable]
public string PostalCode { get; set; }
[IsSearchable, IsFilterable, IsSortable, IsFacetable]
public string Country { get; set; }
}
}
Этот класс содержит стандартные значения, используемые для описания адресов в США или Канаде. Такие типы можно использовать для группирования логических полей в индексе.
Свойство Rooms
представляет массив объектов Room
.
using System;
using Microsoft.Azure.Search;
using Microsoft.Azure.Search.Models;
using Newtonsoft.Json;
namespace AzureSearch.SDKHowTo
{
public partial class Room
{
[IsSearchable]
[Analyzer(AnalyzerName.AsString.EnMicrosoft)]
public string Description { get; set; }
[IsSearchable]
[Analyzer(AnalyzerName.AsString.FrMicrosoft)]
[JsonProperty("Description_fr")]
public string DescriptionFr { get; set; }
[IsSearchable, IsFilterable, IsFacetable]
public string Type { get; set; }
[IsFilterable, IsFacetable]
public double? BaseRate { get; set; }
[IsSearchable, IsFilterable, IsFacetable]
public string BedOptions { get; set; }
[IsFilterable, IsFacetable]
public int SleepsCount { get; set; }
[IsFilterable, IsFacetable]
public bool? SmokingAllowed { get; set; }
[IsSearchable, IsFilterable, IsFacetable]
public string[] Tags { get; set; }
}
}
Модель данных в .NET и соответствующую ей схему индексов должны быть спроектированы для поддержки возможностей поиска, которые нужно предоставить конечному пользователю. Каждый объект верхнего уровня в .NET, т. е. документ в индексе, соответствует результату поиска, который будет представлен в пользовательском интерфейсе. Например, в приложении поиска гостиниц конечные пользователи могут захотеть искать по имени гостиницы, возможностям в ней или характеристикам определенной комнаты. Некоторые примеры запросов будут рассмотрены чуть позже.
Возможность использовать собственные классы для взаимодействия с документами в индексе работает и в обратную сторону. Вы также можете получать результаты поиска и поручить пакету SDK автоматическую десериализацию к выбранному типу, как показано в следующем разделе.
Примечание
Пакет SDK для Когнитивного поиска Azure в .NET также поддерживает документы динамических типов, поддерживающие класс Document
, то есть сопоставление ключей и значений с именами и значениями полей. Это полезно в тех случаях, когда во время разработки вам неизвестна схема индекса или привязка к конкретным классам моделей нецелесообразна. Все методы пакета SDK, связанные с документами, имеют перегрузки, работающие с классом Document
, а также строго типизированные перегрузки, принимающие параметр универсального типа. Пример кода в данном руководстве содержит только перегрузки второго типа. Класс Document
наследует от Dictionary<string, object>
.
Почему следует использовать типы данных, допускающие значение NULL
При разработке собственных классов модели для сопоставления с индексом когнитивного поиска Azure рекомендуется объявлять свойства типов значений, такие как bool
и int
, допускающих нулевое значение (например: bool?
вместо bool
). При использовании ненулевого свойства необходимо гарантировать , что документы в индексе не будут содержать значение NULL для соответствующего поля. Ни пакет SDK, ни служба когнитивного поиска Azure не помогут вам это обеспечить.
Это не просто гипотетическое соображение. Представьте себе ситуацию, когда вы добавляете новое поле в существующий индекс с типом Edm.Int32
. После обновления определения индекса все документы будут иметь значение null для этого нового поля (поскольку все типы допускают значение NULL в службе когнитивного поиска Azure). Если затем для этого поля вы используете класс модели со свойством int
, не допускающим нулевое значение, при попытке получения документов вы получите JsonSerializationException
следующим образом:
Error converting value {null} to type 'System.Int32'. Path 'IntValue'.
По этой причине по-прежнему рекомендуется использовать типы, допускающие значения NULL, в классах модели.
Пользовательская сериализация с помощью JSON.NET
Пакет SDK использует JSON.NET для сериализации и десериализации документов. В случае необходимости можно настроить сериализацию и десериализацию, определив собственные элементы JsonConverter
или IContractResolver
. Дополнительные сведения см. в документации по JSON.NET. Это может быть полезно, если нужно адаптировать существующий класс модели из приложения для использования с когнитивным поиском Azure и других более сложных сценариев. Например, с помощью пользовательской сериализации можно делать следующее.
- Включить или исключить определенные свойства класса модели с сохранением в виде поля документа.
- Реализовать сопоставление между именами свойств в коде и именами полей в индексе.
- Создать настраиваемые атрибуты, которые можно использовать для сопоставления свойств с полями документов.
Примеры реализации пользовательской сериализации можно найти в модульных тестах к пакету SDK для .NET для когнитивного поиска Azure на GitHub. Хорошей отправной точкой будет эта папка. Она содержит классы, используемые в тестах пользовательской сериализации.
Поиск документов в индексе
Последний шаг в примере приложения — поиск некоторых документов в индексе.
private static void RunQueries(ISearchIndexClient indexClient)
{
SearchParameters parameters;
DocumentSearchResult<Hotel> results;
Console.WriteLine("Search the entire index for the term 'motel' and return only the HotelName field:\n");
parameters =
new SearchParameters()
{
Select = new[] { "HotelName" }
};
results = indexClient.Documents.Search<Hotel>("motel", parameters);
WriteDocuments(results);
Console.Write("Apply a filter to the index to find hotels with a room cheaper than $100 per night, ");
Console.WriteLine("and return the hotelId and description:\n");
parameters =
new SearchParameters()
{
Filter = "Rooms/any(r: r/BaseRate lt 100)",
Select = new[] { "HotelId", "Description" }
};
results = indexClient.Documents.Search<Hotel>("*", parameters);
WriteDocuments(results);
Console.Write("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");
parameters =
new SearchParameters()
{
OrderBy = new[] { "LastRenovationDate desc" },
Select = new[] { "HotelName", "LastRenovationDate" },
Top = 2
};
results = indexClient.Documents.Search<Hotel>("*", parameters);
WriteDocuments(results);
Console.WriteLine("Search the entire index for the term 'hotel':\n");
parameters = new SearchParameters();
results = indexClient.Documents.Search<Hotel>("hotel", parameters);
WriteDocuments(results);
}
При каждом выполнении запроса этот метод прежде всего создает объект SearchParameters
. Этот объект позволяет задать дополнительные параметры запроса, например сортировку, фильтрацию, разбиение на страницы и фасетизацию. В этом методе мы задаем свойства Filter
, Select
, OrderBy
и Top
для разных запросов. Все свойства SearchParameters
описаны здесь.
Следующий этап — фактическое выполнение поискового запроса. Выполнение поиска выполняется с помощью метода Documents.Search
. Для каждого запроса мы передаем текст для поиска в строковом формате (или "*"
, если текст для поиска отсутствует) и созданные ранее параметры поиска. Мы также указываем Hotel
как параметр типа Documents.Search
, в результате чего пакет SDK будет десериализовать документы в результатах поиска к объектам типа Hotel
.
Примечание
Дополнительные сведения о синтаксисе выражений запросов см. здесь.
А теперь, после завершения каждого запроса (поиска совпадений в результатах поиска), этот метод последовательно выводит в консоль все удовлетворяющие условиям документы.
private static void WriteDocuments(DocumentSearchResult<Hotel> searchResults)
{
foreach (SearchResult<Hotel> result in searchResults.Results)
{
Console.WriteLine(result.Document);
}
Console.WriteLine();
}
Давайте подробнее рассмотрим каждый из запросов. Ниже приведен код для выполнения первого запроса:
parameters =
new SearchParameters()
{
Select = new[] { "HotelName" }
};
results = indexClient.Documents.Search<Hotel>("motel", parameters);
WriteDocuments(results);
В этом случае мы ищем по всему индексу слово "motel" в любом поле с возможностью поиска, и мы хотим получить только названия гостиниц, как указано в параметре Select
. Результаты приведены ниже.
Name: Secret Point Motel
Name: Twin Dome Motel
Следующий запрос чуть более интересный. Теперь нам нужно найти гостиницы с ценой комнаты ниже 100 долларов США за ночь, а затем получить идентификаторы и описания этих гостиниц:
parameters =
new SearchParameters()
{
Filter = "Rooms/any(r: r/BaseRate lt 100)",
Select = new[] { "HotelId", "Description" }
};
results = indexClient.Documents.Search<Hotel>("*", parameters);
WriteDocuments(results);
Этот запрос использует выражение OData $filter
(по значению Rooms/any(r: r/BaseRate lt 100)
), чтобы отфильтровать в индексе нужные документы. При этом используется любой оператор для применения "BaseRate lt 100" к каждому элементу в коллекции комнат. Дополнительные сведения о синтаксисе OData, который поддерживает служба поиска Azure, см. здесь.
Вот результат выполнения этого запроса:
HotelId: 1
Description: The hotel is ideally located on the main commercial artery of the city in the heart of New York...
HotelId: 2
Description: The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to...
Теперь нам нужно найти две гостиницы с самым свежим ремонтом и отобразить для них название и дату ремонта. Ниже приведен код:
parameters =
new SearchParameters()
{
OrderBy = new[] { "LastRenovationDate desc" },
Select = new[] { "HotelName", "LastRenovationDate" },
Top = 2
};
results = indexClient.Documents.Search<Hotel>("*", parameters);
WriteDocuments(results);
В этом случае мы снова используем синтаксис OData, присвоив параметру OrderBy
значение lastRenovationDate desc
. Мы также указываем значение 2 для параметра Top
, чтобы получить только два первых документа. Как и ранее, параметр Select
содержит поля, которые мы хотим получить.
Результаты приведены ниже.
Name: Fancy Stay Last renovated on: 6/27/2010 12:00:00 AM +00:00
Name: Roach Motel Last renovated on: 4/28/1982 12:00:00 AM +00:00
В последнем примере мы найдем все имена гостиниц, отвечающие запросу со словом "hotel":
parameters = new SearchParameters()
{
SearchFields = new[] { "HotelName" }
};
results = indexClient.Documents.Search<Hotel>("hotel", parameters);
WriteDocuments(results);
Ниже представлены результаты этого запроса. Они включают все поля, так как мы не указали свойство Select
:
HotelId: 3
Name: Triple Landscape Hotel
...
На этом руководство заканчивается, но вам не стоит останавливаться. **Дальнейшие действия представлены дополнительные материалы о Когнитивном поиске Azure.
Дальнейшие действия
- Изучите справочную информацию о пакете SDK для .NET и REST API.
- Изучите соглашения о наименовании , чтобы узнать правила именования различных объектов.
- Изучите поддерживаемые типы данных в службе когнитивного поиска Azure.