Выберите язык программирования для следующего шага. Клиентские библиотеки Azure.Search.Documents доступны в пакетах SDK Azure для .NET, Python, Java и JavaScript/Typescript.
Создайте консольное приложение с помощью клиентской библиотеки Azure.Search.Documents для создания, загрузки и запроса индекса поиска.
Кроме того, можно скачать исходный код , чтобы начать с готового проекта или выполнить следующие действия, чтобы создать собственный.
Настройка среды
Запустите Visual Studio и создайте проект для консольного приложения.
В разделе Инструменты>Диспетчер пакетов NuGet выберите Управление пакетами NugGet для решения....
Выберите Обзор.
Найдите пакет Azure.Search.Documents и выберите версию 11.0 или более позднюю.
Выберите " Установить" , чтобы добавить сборку в проект и решение.
Создание клиента для поиска
В файле Program.cs измените пространство имен на AzureSearch.SDK.Quickstart.v11
, а затем добавьте следующие директивы using
.
using Azure;
using Azure.Search.Documents;
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;
using Azure.Search.Documents.Models;
Скопируйте следующий код, чтобы создать два клиента. SearchIndexClient создает индекс, а SearchClient загружает и запрашивает существующий индекс. Обоим клиентам нужны конечная точка службы и ключ API администрирования, чтобы пройти проверку подлинности и получить права на создание и удаление.
Так как код создает универсальный код ресурса (URI) для вас, укажите только имя службы поиска в свойстве serviceName
.
static void Main(string[] args)
{
string serviceName = "<your-search-service-name>";
string apiKey = "<your-search-service-admin-api-key>";
string indexName = "hotels-quickstart";
// Create a SearchIndexClient to send create/delete index commands
Uri serviceEndpoint = new Uri($"https://{serviceName}.search.windows.net/");
AzureKeyCredential credential = new AzureKeyCredential(apiKey);
SearchIndexClient adminClient = new SearchIndexClient(serviceEndpoint, credential);
// Create a SearchClient to load and query documents
SearchClient srchclient = new SearchClient(serviceEndpoint, indexName, credential);
. . .
}
Создание индекса
В этом кратком руководстве показано, как создать индекс Hotels, в который вы позже загрузите данные об отелях и к которому будете выполнять запросы. На этом шаге следует определить поля в этом индексе. Каждое определение поля содержит имя, тип данных и атрибуты, которые определяют способ использования этого поля.
В этом примере синхронные методы библиотеки Azure.Search.Documents используются для простоты и удобства чтения. Но для рабочих сценариев лучше использовать асинхронные методы, чтобы приложение было масштабируемым и отзывчивым. Например, следует использовать CreateIndexAsync вместо CreateIndex.
Добавление пустого определения класса в проект: Hotel.cs
Скопируйте следующий код в Hotel.cs, чтобы определить структуру документа hotel. Атрибуты в поле определяют, как оно используется в приложении. Например, атрибут IsFilterable
должен быть присвоен каждому полю, которое поддерживает выражение фильтра.
using System;
using System.Text.Json.Serialization;
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;
namespace AzureSearch.Quickstart
{
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(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; }
[SearchableField]
public Address Address { get; set; }
}
}
В клиентской библиотеке Azure.Search.Documents можно использовать SearchableField и SimpleField для упрощения определений полей. Оба метода являются производными от SearchField и потенциально могут упростить код:
SimpleField
может быть любым типом данных, он всегда недоступен для поиска (он игнорируется для запросов полнотекстового поиска) и его можно извлечь (он не скрыт). Другие атрибуты отключены по умолчанию, но их можно включить. SimpleField
можно использовать для идентификаторов документов или полей, используемых только в фильтрах, аспектах или профилях оценки. Если это так, обязательно примените все необходимые для сценария атрибуты, например IsKey = true
для идентификатора документа. Чтобы узнать больше, см. SimpleFieldAttribute.cs в исходном коде.
SearchableField
должен быть строкой и всегда доступен для поиска и извлечения. Другие атрибуты отключены по умолчанию, но их можно включить. Так как этот тип поля доступен для поиска, он поддерживает синонимы и полное дополнение свойств анализатора. Чтобы узнать больше, см. SearchableFieldAttribute.cs в исходном коде.
Независимо от того, используется ли базовый API SearchField
или одна из вспомогательных моделей, необходимо явно включить атрибуты фильтров, аспектов и сортировки. Например, IsFilterable, IsSortable и IsFacetable должны быть явно атрибутированы, как показано в предыдущем примере.
Добавьте в проект второе пустое определение класса: Address.cs. Скопируйте приведенный ниже код и вставьте его в класс.
using Azure.Search.Documents.Indexes;
namespace AzureSearch.Quickstart
{
public partial class Address
{
[SearchableField(IsFilterable = true)]
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; }
}
}
Создайте еще два класса: Hotel.Methods.cs и Address.Methods.cs для ToString()
переопределения. Эти классы используются для отображения результатов поиска в выходных данных консоли. Содержимое этих классов не указано в этой статье, но вы можете скопировать код из файлов в GitHub.
В Program.cs создайте объект SearchClient, а затем вызовите метод CreateIndex, чтобы отразить индекс в службе поиска. Индекс также содержит SearchSuggester, чтобы применить автозаполнение для указанных полей.
// Create hotels-quickstart index
private static void CreateIndex(string indexName, SearchIndexClient adminClient)
{
FieldBuilder fieldBuilder = new FieldBuilder();
var searchFields = fieldBuilder.Build(typeof(Hotel));
var definition = new SearchIndex(indexName, searchFields);
var suggester = new SearchSuggester("sg", new[] { "HotelName", "Category", "Address/City", "Address/StateProvince" });
definition.Suggesters.Add(suggester);
adminClient.CreateOrUpdateIndex(definition);
}
Загрузка документов
Поиск azure AI выполняет поиск по содержимому, хранящемуся в службе. На этом шаге вы отправите документы JSON, которые соответствуют формату созданного индекса отелей.
В поиске ИИ Azure документы поиска — это структуры данных, которые являются входными данными для индексирования и выходных данных из запросов. Полученные из внешнего источника данных входные документы могут содержать строки базы данных, большие двоичные объекты из хранилища BLOB-объектов или сохраненные на диске документы JSON. В нашем примере мы выбрали самый простой путь, внедрив прямо в код документы JSON с информацией о четырех отелях.
При отправке документов необходимо использовать объект IndexDocumentsBatch. Объект IndexDocumentsBatch
содержит коллекцию Actions, каждая из которых содержит документ и свойство, указывающее службе "Поиск ИИ Azure", какие действия необходимо выполнить (отправка, слияние, удаление и слияниеOrUpload).
В файле Program.cs создайте массив документов и действий индексирования, а затем передайте этот массив в IndexDocumentsBatch
. Следующие документы соответствуют индексу hotels-quickstart, как определено классом отеля.
// Upload documents in a single Upload request.
private static void UploadDocuments(SearchClient searchClient)
{
IndexDocumentsBatch<Hotel> batch = IndexDocumentsBatch.Create(
IndexDocumentsAction.Upload(
new Hotel()
{
HotelId = "1",
HotelName = "Stay-Kay City Hotel",
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 Time's 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.",
DescriptionFr = "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.",
Category = "Boutique",
Tags = new[] { "pool", "air conditioning", "concierge" },
ParkingIncluded = false,
LastRenovationDate = new DateTimeOffset(1970, 1, 18, 0, 0, 0, TimeSpan.Zero),
Rating = 3.6,
Address = new Address()
{
StreetAddress = "677 5th Ave",
City = "New York",
StateProvince = "NY",
PostalCode = "10022",
Country = "USA"
}
}),
IndexDocumentsAction.Upload(
new Hotel()
{
HotelId = "2",
HotelName = "Old Century Hotel",
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.",
DescriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
Category = "Boutique",
Tags = new[] { "pool", "free wifi", "concierge" },
ParkingIncluded = false,
LastRenovationDate = new DateTimeOffset(1979, 2, 18, 0, 0, 0, TimeSpan.Zero),
Rating = 3.60,
Address = new Address()
{
StreetAddress = "140 University Town Center Dr",
City = "Sarasota",
StateProvince = "FL",
PostalCode = "34243",
Country = "USA"
}
}),
IndexDocumentsAction.Upload(
new Hotel()
{
HotelId = "3",
HotelName = "Gastronomic Landscape Hotel",
Description = "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.",
DescriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
Category = "Resort and Spa",
Tags = new[] { "air conditioning", "bar", "continental breakfast" },
ParkingIncluded = true,
LastRenovationDate = new DateTimeOffset(2015, 9, 20, 0, 0, 0, TimeSpan.Zero),
Rating = 4.80,
Address = new Address()
{
StreetAddress = "3393 Peachtree Rd",
City = "Atlanta",
StateProvince = "GA",
PostalCode = "30326",
Country = "USA"
}
}),
IndexDocumentsAction.Upload(
new Hotel()
{
HotelId = "4",
HotelName = "Sublime Palace Hotel",
Description = "Sublime Palace Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Palace is part of a lovingly restored 1800 palace.",
DescriptionFr = "Le Sublime Palace Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Palace fait partie d'un Palace 1800 restauré avec amour.",
Category = "Boutique",
Tags = new[] { "concierge", "view", "24-hour front desk service" },
ParkingIncluded = true,
LastRenovationDate = new DateTimeOffset(1960, 2, 06, 0, 0, 0, TimeSpan.Zero),
Rating = 4.60,
Address = new Address()
{
StreetAddress = "7400 San Pedro Ave",
City = "San Antonio",
StateProvince = "TX",
PostalCode = "78216",
Country = "USA"
}
})
);
try
{
IndexDocumentsResult result = searchClient.IndexDocuments(batch);
}
catch (Exception)
{
// If for some reason any documents are dropped during indexing, you can compensate by delaying and
// retrying. This simple demo just logs the failed document keys and continues.
Console.WriteLine("Failed to index some of the documents: {0}");
}
}
Создав экземпляр объекта IndexDocumentsBatch, вы сможете отправить его в индекс, вызвав IndexDocuments из объекта SearchClient.
Добавьте в раздел Main()
следующие строки. Загрузка документов выполняется с помощью SearchClient, однако для операции также понадобятся права администратора для службы, которая обычно связана с SearchIndexClient. Один из способов настройки этой операции — получить SearchClient через SearchIndexClient
(adminClient
в этом примере).
SearchClient ingesterClient = adminClient.GetSearchClient(indexName);
// Load documents
Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(ingesterClient);
Так как это консольное приложение выполняет все команды последовательно, включите 2-секундные паузы между операциями индексирования и запросов.
// Wait 2 seconds for indexing to complete before starting queries (for demo and console-app purposes only)
Console.WriteLine("Waiting for indexing...\n");
System.Threading.Thread.Sleep(2000);
2-секундная задержка дает достаточно времени для асинхронного индексирования, чтобы все документы уже были проиндексированы перед выполнением запросов. Задержки в коде обычно используются только в демонстрациях, тестах и примерах приложений.
Поиск в индексе
Результаты запросов можно получить сразу по завершении индексирования первого документа, но для полноценного тестирования индекса придется подождать, пока закончится индексирование всех документов.
В этом разделе мы добавим две новые функции: логику запроса и результаты. Для запросов примените метод Search. Этот метод принимает текст поиска (строку запроса) и другие параметры.
Класс SearchResults представляет результаты запроса.
В Program.cs создайте WriteDocuments
метод, который выводит результаты поиска в консоль.
// Write search results to console
private static void WriteDocuments(SearchResults<Hotel> searchResults)
{
foreach (SearchResult<Hotel> result in searchResults.GetResults())
{
Console.WriteLine(result.Document);
}
Console.WriteLine();
}
private static void WriteDocuments(AutocompleteResults autoResults)
{
foreach (AutocompleteItem result in autoResults.Results)
{
Console.WriteLine(result.Text);
}
Console.WriteLine();
}
RunQueries
Создайте метод для выполнения запросов и возврата результатов. Результаты имеют формат объектов Hotel. В этом примере показана сигнатура метода и первый запрос. В этом запросе представлен параметр Select, позволяющий составить результат, используя выбранные поля документа.
// Run queries, use WriteDocuments to print output
private static void RunQueries(SearchClient srchclient)
{
SearchOptions options;
SearchResults<Hotel> response;
// Query 1
Console.WriteLine("Query #1: Search on empty term '*' to return all documents, showing a subset of fields...\n");
options = new SearchOptions()
{
IncludeTotalCount = true,
Filter = "",
OrderBy = { "" }
};
options.Select.Add("HotelId");
options.Select.Add("HotelName");
options.Select.Add("Address/City");
response = srchclient.Search<Hotel>("*", options);
WriteDocuments(response);
Во втором запросе выполните поиск по термину, добавьте фильтр, который выбирает документы, где рейтинг больше 4, а затем сортируйте по рейтингу в порядке убывания. Фильтром называется логическое выражение, которое оценивается для всех полей в индексе с атрибутом IsFilterable. Фильтрующие запросы могут включать или исключать указанные значения. Таким образом, оценка релевантности не связана с запросом фильтра.
// Query 2
Console.WriteLine("Query #2: Search on 'hotels', filter on 'Rating gt 4', sort by Rating in descending order...\n");
options = new SearchOptions()
{
Filter = "Rating gt 4",
OrderBy = { "Rating desc" }
};
options.Select.Add("HotelId");
options.Select.Add("HotelName");
options.Select.Add("Rating");
response = srchclient.Search<Hotel>("hotels", options);
WriteDocuments(response);
Третий запрос демонстрирует searchFields
область действия полнотекстового поиска для определенных полей.
// Query 3
Console.WriteLine("Query #3: Limit search to specific fields (pool in Tags field)...\n");
options = new SearchOptions()
{
SearchFields = { "Tags" }
};
options.Select.Add("HotelId");
options.Select.Add("HotelName");
options.Select.Add("Tags");
response = srchclient.Search<Hotel>("pool", options);
WriteDocuments(response);
Четвертый запрос демонстрирует facets
, который можно использовать для структуры фасетной структуры навигации.
// Query 4
Console.WriteLine("Query #4: Facet on 'Category'...\n");
options = new SearchOptions()
{
Filter = ""
};
options.Facets.Add("Category");
options.Select.Add("HotelId");
options.Select.Add("HotelName");
options.Select.Add("Category");
response = srchclient.Search<Hotel>("*", options);
WriteDocuments(response);
В пятом запросе возвращается конкретный документ. Поиск документов — это типичный ответ на OnClick
событие в результирующем наборе.
// Query 5
Console.WriteLine("Query #5: Look up a specific document...\n");
Response<Hotel> lookupResponse;
lookupResponse = srchclient.GetDocument<Hotel>("3");
Console.WriteLine(lookupResponse.Value.HotelId);
В последнем запросе показан синтаксис автозавершения, имитация частичного пользовательского ввода sa , разрешающего два возможных совпадения в sourceFields, связанных с предложением, определенным в индексе.
// Query 6
Console.WriteLine("Query #6: Call Autocomplete on HotelName that starts with 'sa'...\n");
var autoresponse = srchclient.Autocomplete("sa", "sg");
WriteDocuments(autoresponse);
Добавлен RunQueries
в Main()
.
// Call the RunQueries method to invoke a series of queries
Console.WriteLine("Starting queries...\n");
RunQueries(srchclient);
// End the program
Console.WriteLine("{0}", "Complete. Press any key to end this program...\n");
Console.ReadKey();
В предыдущих запросах показано несколько способов сопоставления условий в запросе: полнотекстовый поиск, фильтры и автозавершение.
Полнотекстовый поиск и фильтрация выполняются с помощью метода SearchClient.Search. Строку поиска можно передать в параметре searchText
, а выражение фильтра — свойстве Filter класса SearchOptions. Чтобы выполнить фильтрацию без поиска, передайте "*"
в качестве значения параметра searchText
в метод Search. Чтобы выполнить поиск без фильтрации, оставьте Filter
свойство неустановленным или не передайте в SearchOptions
экземпляре вообще.
Запуск программы
Нажмите клавишу F5 , чтобы перестроить приложение и запустить программу в целом.
Выходные данные содержат сообщения из Console.WriteLine, а также сведения о запросе и результаты.
Используйте записную книжку Jupyter и библиотеку документов azure-search-documents в пакете SDK Azure для Python для создания, загрузки и запроса индекса поиска.
Кроме того, можно скачать и запустить завершенную записную книжку.
Настройка среды
Используйте Visual Studio Code с расширением Python или эквивалентной интегрированной среды разработки с Python 3.10 или более поздней версии.
Для этого краткого руководства рекомендуется использовать виртуальную среду:
Запустите Visual Studio Code.
Откройте палитру команд (CTRL+SHIFT+P).
Поиск Python : создание среды.
Выберите Venv.
Выбор интерпретатора Python Выберите версию 3.10 или более позднюю.
Для настройки может потребоваться несколько минут. Если возникнут проблемы, ознакомьтесь со средами Python в VS Code.
Установка пакетов и установка переменных
Установите пакеты, включая документы azure-search-documents.
! pip install azure-search-documents==11.6.0b1 --quiet
! pip install azure-identity --quiet
! pip install python-dotenv --quiet
Укажите конечную точку и ключ API для службы:
search_endpoint: str = "PUT-YOUR-SEARCH-SERVICE-ENDPOINT-HERE"
search_api_key: str = "PUT-YOUR-SEARCH-SERVICE-ADMIN-API-KEY-HERE"
index_name: str = "hotels-quickstart"
Создание индекса
from azure.core.credentials import AzureKeyCredential
credential = AzureKeyCredential(search_api_key)
from azure.search.documents.indexes import SearchIndexClient
from azure.search.documents import SearchClient
from azure.search.documents.indexes.models import (
ComplexField,
SimpleField,
SearchFieldDataType,
SearchableField,
SearchIndex
)
# Create a search schema
index_client = SearchIndexClient(
endpoint=search_endpoint, credential=credential)
fields = [
SimpleField(name="HotelId", type=SearchFieldDataType.String, key=True),
SearchableField(name="HotelName", type=SearchFieldDataType.String, sortable=True),
SearchableField(name="Description", type=SearchFieldDataType.String, analyzer_name="en.lucene"),
SearchableField(name="Description_fr", type=SearchFieldDataType.String, analyzer_name="fr.lucene"),
SearchableField(name="Category", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
SearchableField(name="Tags", collection=True, type=SearchFieldDataType.String, facetable=True, filterable=True),
SimpleField(name="ParkingIncluded", type=SearchFieldDataType.Boolean, facetable=True, filterable=True, sortable=True),
SimpleField(name="LastRenovationDate", type=SearchFieldDataType.DateTimeOffset, facetable=True, filterable=True, sortable=True),
SimpleField(name="Rating", type=SearchFieldDataType.Double, facetable=True, filterable=True, sortable=True),
ComplexField(name="Address", fields=[
SearchableField(name="StreetAddress", type=SearchFieldDataType.String),
SearchableField(name="City", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
SearchableField(name="StateProvince", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
SearchableField(name="PostalCode", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
SearchableField(name="Country", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
])
]
scoring_profiles = []
suggester = [{'name': 'sg', 'source_fields': ['Tags', 'Address/City', 'Address/Country']}]
# Create the search index
index = SearchIndex(name=index_name, fields=fields, suggesters=suggester, scoring_profiles=scoring_profiles)
result = index_client.create_or_update_index(index)
print(f' {result.name} created')
Создание полезных данных документов
Используйте действие индекса для типа операции, например отправку или слияние и отправку. Документы, полученные из примера HotelsData на GitHub.
# Create a documents payload
documents = [
{
"@search.action": "upload",
"HotelId": "1",
"HotelName": "Stay-Kay City Hotel",
"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 Time's 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.",
"Description_fr": "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.",
"Category": "Boutique",
"Tags": [ "pool", "air conditioning", "concierge" ],
"ParkingIncluded": "false",
"LastRenovationDate": "1970-01-18T00:00:00Z",
"Rating": 3.60,
"Address": {
"StreetAddress": "677 5th Ave",
"City": "New York",
"StateProvince": "NY",
"PostalCode": "10022",
"Country": "USA"
}
},
{
"@search.action": "upload",
"HotelId": "2",
"HotelName": "Old Century Hotel",
"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.",
"Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
"Category": "Boutique",
"Tags": [ "pool", "free wifi", "concierge" ],
"ParkingIncluded": "false",
"LastRenovationDate": "1979-02-18T00:00:00Z",
"Rating": 3.60,
"Address": {
"StreetAddress": "140 University Town Center Dr",
"City": "Sarasota",
"StateProvince": "FL",
"PostalCode": "34243",
"Country": "USA"
}
},
{
"@search.action": "upload",
"HotelId": "3",
"HotelName": "Gastronomic Landscape Hotel",
"Description": "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel's restaurant services.",
"Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
"Category": "Resort and Spa",
"Tags": [ "air conditioning", "bar", "continental breakfast" ],
"ParkingIncluded": "true",
"LastRenovationDate": "2015-09-20T00:00:00Z",
"Rating": 4.80,
"Address": {
"StreetAddress": "3393 Peachtree Rd",
"City": "Atlanta",
"StateProvince": "GA",
"PostalCode": "30326",
"Country": "USA"
}
},
{
"@search.action": "upload",
"HotelId": "4",
"HotelName": "Sublime Palace Hotel",
"Description": "Sublime Palace Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Palace is part of a lovingly restored 1800 palace.",
"Description_fr": "Le Sublime Palace Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Palace fait partie d'un Palace 1800 restauré avec amour.",
"Category": "Boutique",
"Tags": [ "concierge", "view", "24-hour front desk service" ],
"ParkingIncluded": "true",
"LastRenovationDate": "1960-02-06T00:00:00Z",
"Rating": 4.60,
"Address": {
"StreetAddress": "7400 San Pedro Ave",
"City": "San Antonio",
"StateProvince": "TX",
"PostalCode": "78216",
"Country": "USA"
}
}
]
Отправить документы
# Upload documents to the index
search_client = SearchClient(endpoint=search_endpoint,
index_name=index_name,
credential=credential)
try:
result = search_client.upload_documents(documents=documents)
print("Upload of new document succeeded: {}".format(result[0].succeeded))
except Exception as ex:
print (ex.message)
index_client = SearchIndexClient(
endpoint=search_endpoint, credential=credential)
Выполните первый запрос
Используйте метод поиска класса search.client.
В этом примере выполняется пустой поиск (search=*
), возвращающий неподделанный список (оценка поиска = 1.0) произвольных документов. Поскольку условия не заданы, в результаты включаются все документы.
# Run an empty query (returns selected fields, all documents)
results = search_client.search(query_type='simple',
search_text="*" ,
select='HotelName,Description',
include_total_count=True)
print ('Total Documents Matching Query:', results.get_count())
for result in results:
print(result["@search.score"])
print(result["HotelName"])
print(f"Description: {result['Description']}")
Выполнение запроса терминов
Следующий запрос добавляет в выражение поиска целые термины ("wifi"). Этот запрос указывает, что результаты содержат только поля в операторе select
. Ограничение числа полей, которые возвращаются обратно, минимизирует объем данных, передаваемых по сети, и сокращает задержку поиска.
results = search_client.search(query_type='simple',
search_text="wifi" ,
select='HotelName,Description,Tags',
include_total_count=True)
print ('Total Documents Matching Query:', results.get_count())
for result in results:
print(result["@search.score"])
print(result["HotelName"])
print(f"Description: {result['Description']}")
Добавить фильтр
Добавьте выражение фильтра, возвращая только те отели с рейтингом, превышающим четыре, отсортированные в порядке убывания.
# Add a filter
results = search_client.search(
search_text="hotels",
select='HotelId,HotelName,Rating',
filter='Rating gt 4',
order_by='Rating desc')
for result in results:
print("{}: {} - {} rating".format(result["HotelId"], result["HotelName"], result["Rating"]))
Добавление области полей
Добавьте search_fields
в область выполнения запроса к определенным полям.
# Add search_fields to scope query matching to the HotelName field
results = search_client.search(
search_text="sublime",
search_fields=['HotelName'],
select='HotelId,HotelName')
for result in results:
print("{}: {}".format(result["HotelId"], result["HotelName"]))
Добавление аспектов
Аспекты создаются для положительных совпадений, найденных в результатах поиска. Нет нулевых совпадений. Если результаты поиска не включают термин Wifi, то wifi не отображается в фасетной структуре навигации.
# Return facets
results = search_client.search(search_text="*", facets=["Category"])
facets = results.get_facets()
for facet in facets["Category"]:
print(" {}".format(facet))
Поиск документа
Возвращает документ на основе ключа. Эта операция полезна, если требуется выполнить детализацию, когда пользователь выбирает элемент в результатах поиска.
# Look up a specific document by ID
result = search_client.get_document(key="3")
print("Details for hotel '3' are:")
print("Name: {}".format(result["HotelName"]))
print("Rating: {}".format(result["Rating"]))
print("Category: {}".format(result["Category"]))
Добавление автозаполнения
Автозавершение может обеспечить возможные совпадения в поле поиска в качестве типов пользователей.
Автозавершение использует средство предложения (sg
), чтобы узнать, какие поля содержат потенциальные совпадения с запросами предложителя. В этом кратком руководстве эти поля: Tags
, Address/Country
Address/City
.
Чтобы имитировать автозавершение, передайте буквы sa в виде частичной строки. Метод автозаполнения SearchClient возвращает подходящие термины.
# Autocomplete a query
search_suggestion = 'sa'
results = search_client.autocomplete(
search_text=search_suggestion,
suggester_name="sg",
mode='twoTerms')
print("Autocomplete for:", search_suggestion)
for result in results:
print (result['text'])
Создайте консольное приложение Java с помощью библиотеки Azure.Search.Documents для создания, загрузки и запроса индекса поиска.
Кроме того, можно скачать исходный код , чтобы начать с готового проекта или выполнить следующие действия, чтобы создать собственный.
Настройка среды
Используйте следующие средства для создания этого краткого руководства.
Создание проекта
Запустите Visual Studio Code.
Откройте палитру команд с помощью клавиш CTRL+SHIFT+P. Выполните поиск проекта Java.
Выберите Maven.
Выберите maven-archetype-quickstart.
Выберите последнюю версию, в настоящее время — 1.4.
Введите azure.search.sample в качестве идентификатора группы.
Введите azuresearchquickstart в качестве идентификатора артефакта.
Выберите папку для создания проекта.
Завершите создание проекта в интегрированном терминале. Нажмите клавишу ВВОД, чтобы принять значение по умолчанию для "1.0-SNAPSHOT", а затем введите "y", чтобы подтвердить свойства проекта.
Откройте папку, в которую вы создали проект.
Указание зависимостей Maven
Откройте файл pom.xml и добавьте следующие зависимости.
<dependencies>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-search-documents</artifactId>
<version>11.7.3</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core</artifactId>
<version>1.53.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
Измените версию компилятора Java на 11.
<maven.compiler.source>1.11</maven.compiler.source>
<maven.compiler.target>1.11</maven.compiler.target>
Создание клиента для поиска
Откройте класс в App
разделе src, main, java, azure, search, sample. Добавьте следующие директивы импорта.
import java.util.Arrays;
import java.util.ArrayList;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.LocalDateTime;
import java.time.LocalDate;
import java.time.LocalTime;
import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.util.Context;
import com.azure.search.documents.SearchClient;
import com.azure.search.documents.SearchClientBuilder;
import com.azure.search.documents.models.SearchOptions;
import com.azure.search.documents.indexes.SearchIndexClient;
import com.azure.search.documents.indexes.SearchIndexClientBuilder;
import com.azure.search.documents.indexes.models.IndexDocumentsBatch;
import com.azure.search.documents.indexes.models.SearchIndex;
import com.azure.search.documents.indexes.models.SearchSuggester;
import com.azure.search.documents.util.AutocompletePagedIterable;
import com.azure.search.documents.util.SearchPagedIterable;
В следующем примере содержатся заполнители для имени службы поиска, ключа API администрирования, который предоставляет разрешения на создание и удаление, а также имя индекса. Замените допустимые значения для всех трех заполнителей. Создайте два клиента: SearchIndexClient создает индекс, а SearchClient загружает и запрашивает существующий индекс. Требуется конечная точка службы и ключ API администратора для проверки подлинности с правами на создание и удаление.
public static void main(String[] args) {
var searchServiceEndpoint = "<YOUR-SEARCH-SERVICE-URL>";
var adminKey = new AzureKeyCredential("<YOUR-SEARCH-SERVICE-ADMIN-KEY>");
String indexName = "<YOUR-SEARCH-INDEX-NAME>";
SearchIndexClient searchIndexClient = new SearchIndexClientBuilder()
.endpoint(searchServiceEndpoint)
.credential(adminKey)
.buildClient();
SearchClient searchClient = new SearchClientBuilder()
.endpoint(searchServiceEndpoint)
.credential(adminKey)
.indexName(indexName)
.buildClient();
}
Создание индекса
В этом кратком руководстве показано, как создать индекс Hotels, в который вы позже загрузите данные об отелях и к которому будете выполнять запросы. На этом шаге следует определить поля в этом индексе. Каждое определение поля содержит имя, тип данных и атрибуты, которые определяют способ использования этого поля.
В этом примере синхронные методы библиотеки документов azure-search-documents используются для простоты и удобства чтения. Но для рабочих сценариев лучше использовать асинхронные методы, чтобы приложение было масштабируемым и отзывчивым. Например, вместо SearchClient используется SearchAsyncClient .
Добавьте в проект пустое определение класса: Hotel.java
Скопируйте следующий код в Hotel.java
структуру документа отеля. Атрибуты в поле определяют, как оно используется в приложении. Например, заметка IsFilterable должна быть назначена каждому полю, поддерживающему выражение фильтра.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package azure.search.sample;
import com.azure.search.documents.indexes.SearchableField;
import com.azure.search.documents.indexes.SimpleField;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import java.time.OffsetDateTime;
/**
* Model class representing a hotel.
*/
@JsonInclude(Include.NON_NULL)
public class Hotel {
/**
* Hotel ID
*/
@JsonProperty("HotelId")
@SimpleField(isKey = true)
public String hotelId;
/**
* Hotel name
*/
@JsonProperty("HotelName")
@SearchableField(isSortable = true)
public String hotelName;
/**
* Description
*/
@JsonProperty("Description")
@SearchableField(analyzerName = "en.microsoft")
public String description;
/**
* French description
*/
@JsonProperty("DescriptionFr")
@SearchableField(analyzerName = "fr.lucene")
public String descriptionFr;
/**
* Category
*/
@JsonProperty("Category")
@SearchableField(isFilterable = true, isSortable = true, isFacetable = true)
public String category;
/**
* Tags
*/
@JsonProperty("Tags")
@SearchableField(isFilterable = true, isFacetable = true)
public String[] tags;
/**
* Whether parking is included
*/
@JsonProperty("ParkingIncluded")
@SimpleField(isFilterable = true, isSortable = true, isFacetable = true)
public Boolean parkingIncluded;
/**
* Last renovation time
*/
@JsonProperty("LastRenovationDate")
@SimpleField(isFilterable = true, isSortable = true, isFacetable = true)
public OffsetDateTime lastRenovationDate;
/**
* Rating
*/
@JsonProperty("Rating")
@SimpleField(isFilterable = true, isSortable = true, isFacetable = true)
public Double rating;
/**
* Address
*/
@JsonProperty("Address")
public Address address;
@Override
public String toString()
{
try
{
return new ObjectMapper().writeValueAsString(this);
}
catch (JsonProcessingException e)
{
e.printStackTrace();
return "";
}
}
}
В клиентской библиотеке Azure.Search.Documents можно использовать SearchableField и SimpleField для упрощения определения полей.
SimpleField
может быть любым типом данных, он всегда недоступен для поиска (он игнорируется для запросов полнотекстового поиска) и его можно извлечь (он не скрыт). Другие атрибуты отключены по умолчанию, но их можно включить. Вы можете использовать SimpleField для идентификаторов документов или полей, используемых только в фильтрах, аспектах или профилях оценки. Если это так, обязательно примените все атрибуты, необходимые для сценария, например IsKey = true для идентификатора документа.
SearchableField
должен быть строкой и всегда доступен для поиска и извлечения. Другие атрибуты отключены по умолчанию, но их можно включить. Так как этот тип поля доступен для поиска, он поддерживает синонимы и полное дополнение свойств анализатора.
Независимо от того, используется ли базовый API SearchField
или одна из вспомогательных моделей, необходимо явно включить атрибуты фильтров, аспектов и сортировки. Например, isFilterable
и isSortable
isFacetable
должен быть явным образом атрибутом, как показано в предыдущем примере.
Добавьте в проект второе пустое определение класса: Address.java
Скопируйте приведенный ниже код и вставьте его в класс.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package azure.search.sample;
import com.azure.search.documents.indexes.SearchableField;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
/**
* Model class representing an address.
*/
@JsonInclude(Include.NON_NULL)
public class Address {
/**
* Street address
*/
@JsonProperty("StreetAddress")
@SearchableField
public String streetAddress;
/**
* City
*/
@JsonProperty("City")
@SearchableField(isFilterable = true, isSortable = true, isFacetable = true)
public String city;
/**
* State or province
*/
@JsonProperty("StateProvince")
@SearchableField(isFilterable = true, isSortable = true, isFacetable = true)
public String stateProvince;
/**
* Postal code
*/
@JsonProperty("PostalCode")
@SearchableField(isFilterable = true, isSortable = true, isFacetable = true)
public String postalCode;
/**
* Country
*/
@JsonProperty("Country")
@SearchableField(isFilterable = true, isSortable = true, isFacetable = true)
public String country;
}
Создайте App.java
SearchIndex
объект в методеmain
, а затем вызовите createOrUpdateIndex
метод для создания индекса в службе поиска. Индекс также включает SearchSuggester
функцию автозаполнения для указанных полей.
// Create Search Index for Hotel model
searchIndexClient.createOrUpdateIndex(
new SearchIndex(indexName, SearchIndexClient.buildSearchFields(Hotel.class, null))
.setSuggesters(new SearchSuggester("sg", Arrays.asList("HotelName"))));
Загрузка документов
Поиск azure AI выполняет поиск по содержимому, хранящемуся в службе. На этом шаге вы отправите документы JSON, которые соответствуют формату созданного индекса отелей.
В поиске ИИ Azure документы поиска — это структуры данных, которые являются входными данными для индексирования и выходных данных из запросов. Полученные из внешнего источника данных входные документы могут содержать строки базы данных, большие двоичные объекты из хранилища BLOB-объектов или сохраненные на диске документы JSON. В нашем примере мы выбрали самый простой путь, внедрив прямо в код документы JSON с информацией о четырех отелях.
При отправке документов необходимо использовать объект IndexDocumentsBatch. Объект IndexDocumentsBatch
содержит коллекцию IndexActions, каждая из которых содержит документ и свойство, указывающее службе "Поиск ИИ Azure", какие действия необходимо выполнить (отправка, слияние, удаление и слияниеOrUpload).
Создайте App.java
документы и действия индекса, а затем передайте их IndexDocumentsBatch
в . Следующие документы соответствуют индексу hotels-quickstart, как определено классом отеля.
// Upload documents in a single Upload request.
private static void uploadDocuments(SearchClient searchClient)
{
var hotelList = new ArrayList<Hotel>();
var hotel = new Hotel();
hotel.hotelId = "1";
hotel.hotelName = "Stay-Kay City Hotel";
hotel.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 Time's 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.";
hotel.descriptionFr = "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.";
hotel.category = "Boutique";
hotel.tags = new String[] { "pool", "air conditioning", "concierge" };
hotel.parkingIncluded = false;
hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(1970, 1, 18), LocalTime.of(0, 0)), ZoneOffset.UTC);
hotel.rating = 3.6;
hotel.address = new Address();
hotel.address.streetAddress = "677 5th Ave";
hotel.address.city = "New York";
hotel.address.stateProvince = "NY";
hotel.address.postalCode = "10022";
hotel.address.country = "USA";
hotelList.add(hotel);
hotel = new Hotel();
hotel.hotelId = "2";
hotel.hotelName = "Old Century Hotel";
hotel.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.";
hotel.descriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.";
hotel.category = "Boutique";
hotel.tags = new String[] { "pool", "free wifi", "concierge" };
hotel.parkingIncluded = false;
hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(1979, 2, 18), LocalTime.of(0, 0)), ZoneOffset.UTC);
hotel.rating = 3.60;
hotel.address = new Address();
hotel.address.streetAddress = "140 University Town Center Dr";
hotel.address.city = "Sarasota";
hotel.address.stateProvince = "FL";
hotel.address.postalCode = "34243";
hotel.address.country = "USA";
hotelList.add(hotel);
hotel = new Hotel();
hotel.hotelId = "3";
hotel.hotelName = "Gastronomic Landscape Hotel";
hotel.description = "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.";
hotel.descriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.";
hotel.category = "Resort and Spa";
hotel.tags = new String[] { "air conditioning", "bar", "continental breakfast" };
hotel.parkingIncluded = true;
hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(2015, 9, 20), LocalTime.of(0, 0)), ZoneOffset.UTC);
hotel.rating = 4.80;
hotel.address = new Address();
hotel.address.streetAddress = "3393 Peachtree Rd";
hotel.address.city = "Atlanta";
hotel.address.stateProvince = "GA";
hotel.address.postalCode = "30326";
hotel.address.country = "USA";
hotelList.add(hotel);
hotel = new Hotel();
hotel.hotelId = "4";
hotel.hotelName = "Sublime Palace Hotel";
hotel.description = "Sublime Palace Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Palace is part of a lovingly restored 1800 palace.";
hotel.descriptionFr = "Le Sublime Palace Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Palace fait partie d'un Palace 1800 restauré avec amour.";
hotel.category = "Boutique";
hotel.tags = new String[] { "concierge", "view", "24-hour front desk service" };
hotel.parkingIncluded = true;
hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(1960, 2, 06), LocalTime.of(0, 0)), ZoneOffset.UTC);
hotel.rating = 4.60;
hotel.address = new Address();
hotel.address.streetAddress = "7400 San Pedro Ave";
hotel.address.city = "San Antonio";
hotel.address.stateProvince = "TX";
hotel.address.postalCode = "78216";
hotel.address.country = "USA";
hotelList.add(hotel);
var batch = new IndexDocumentsBatch<Hotel>();
batch.addMergeOrUploadActions(hotelList);
try
{
searchClient.indexDocuments(batch);
}
catch (Exception e)
{
e.printStackTrace();
// If for some reason any documents are dropped during indexing, you can compensate by delaying and
// retrying. This simple demo just logs failure and continues
System.err.println("Failed to index some of the documents");
}
}
После инициализации IndexDocumentsBatch
объекта его можно отправить в индекс, вызвав indexDocuments в объекте SearchClient
.
Добавьте в раздел Main()
следующие строки. Загрузка документов выполняется с помощью SearchClient
.
// Upload sample hotel documents to the Search Index
uploadDocuments(searchClient);
Так как это консольное приложение выполняет все команды последовательно, включите 2-секундные паузы между операциями индексирования и запросов.
// Wait 2 seconds for indexing to complete before starting queries (for demo and console-app purposes only)
System.out.println("Waiting for indexing...\n");
try
{
Thread.sleep(2000);
}
catch (InterruptedException e)
{
}
2-секундная задержка дает достаточно времени для асинхронного индексирования, чтобы все документы уже были проиндексированы перед выполнением запросов. Задержки в коде обычно используются только в демонстрациях, тестах и примерах приложений.
Поиск в индексе
Результаты запросов можно получить сразу по завершении индексирования первого документа, но для полноценного тестирования индекса придется подождать, пока закончится индексирование всех документов.
В этом разделе мы добавим две новые функции: логику запроса и результаты. Для запросов примените метод Search. Этот метод принимает текст поиска (строку запроса) и другие параметры.
Создайте App.java
WriteDocuments
метод, который выводит результаты поиска в консоль.
// Write search results to console
private static void WriteSearchResults(SearchPagedIterable searchResults)
{
searchResults.iterator().forEachRemaining(result ->
{
Hotel hotel = result.getDocument(Hotel.class);
System.out.println(hotel);
});
System.out.println();
}
// Write autocomplete results to console
private static void WriteAutocompleteResults(AutocompletePagedIterable autocompleteResults)
{
autocompleteResults.iterator().forEachRemaining(result ->
{
String text = result.getText();
System.out.println(text);
});
System.out.println();
}
RunQueries
Создайте метод для выполнения запросов и возврата результатов. Результаты — это Hotel
объекты. В этом примере показана сигнатура метода и первый запрос. Этот запрос демонстрирует Select
параметр, позволяющий создавать результат с помощью выбранных полей из документа.
// Run queries, use WriteDocuments to print output
private static void RunQueries(SearchClient searchClient)
{
// Query 1
System.out.println("Query #1: Search on empty term '*' to return all documents, showing a subset of fields...\n");
SearchOptions options = new SearchOptions();
options.setIncludeTotalCount(true);
options.setFilter("");
options.setOrderBy("");
options.setSelect("HotelId", "HotelName", "Address/City");
WriteSearchResults(searchClient.search("*", options, Context.NONE));
}
Во втором запросе выполните поиск по термину, добавьте фильтр, который выбирает документы, где рейтинг больше 4, а затем сортируйте по рейтингу в порядке убывания. Фильтр — это логическое выражение, вычисляемое по isFilterable
полям в индексе. Фильтрующие запросы могут включать или исключать указанные значения. Таким образом, оценка релевантности не связана с запросом фильтра.
// Query 2
System.out.println("Query #2: Search on 'hotels', filter on 'Rating gt 4', sort by Rating in descending order...\n");
options = new SearchOptions();
options.setFilter("Rating gt 4");
options.setOrderBy("Rating desc");
options.setSelect("HotelId", "HotelName", "Rating");
WriteSearchResults(searchClient.search("hotels", options, Context.NONE));
Третий запрос демонстрирует searchFields
область действия полнотекстового поиска для определенных полей.
// Query 3
System.out.println("Query #3: Limit search to specific fields (pool in Tags field)...\n");
options = new SearchOptions();
options.setSearchFields("Tags");
options.setSelect("HotelId", "HotelName", "Tags");
WriteSearchResults(searchClient.search("pool", options, Context.NONE));
Четвертый запрос демонстрирует facets
, который можно использовать для структуры фасетной структуры навигации.
// Query 4
System.out.println("Query #4: Facet on 'Category'...\n");
options = new SearchOptions();
options.setFilter("");
options.setFacets("Category");
options.setSelect("HotelId", "HotelName", "Category");
WriteSearchResults(searchClient.search("*", options, Context.NONE));
В пятом запросе возвращается конкретный документ.
// Query 5
System.out.println("Query #5: Look up a specific document...\n");
Hotel lookupResponse = searchClient.getDocument("3", Hotel.class);
System.out.println(lookupResponse.hotelId);
System.out.println();
Последний запрос показывает синтаксис автозавершения, имитируя частичные входные данные пользователя s , разрешающие два возможных совпадения в sourceFields
связанном с предложением, определенном в индексе.
// Query 6
System.out.println("Query #6: Call Autocomplete on HotelName that starts with 's'...\n");
WriteAutocompleteResults(searchClient.autocomplete("s", "sg"));
Добавлен RunQueries
в Main()
.
// Call the RunQueries method to invoke a series of queries
System.out.println("Starting queries...\n");
RunQueries(searchClient);
// End the program
System.out.println("Complete.\n");
В предыдущих запросах показано несколько способов сопоставления условий в запросе: полнотекстовый поиск, фильтры и автозавершение.
Полнотекстовый поиск и фильтры выполняются с помощью метода SearchClient.search . Запрос поиска можно передать в строке searchText
, а выражение фильтра можно передать в filter
свойстве класса SearchOptions . Чтобы отфильтровать без поиска, просто передайте "*" для searchText
параметра search
метода. Чтобы выполнить поиск без фильтрации, оставьте filter
свойство неустановленным или не передайте в SearchOptions
экземпляре вообще.
Запуск программы
Нажмите клавишу F5, чтобы перестроить приложение и запустить полнофункциональную программу.
Выходные данные включают сообщения из System.out.println
, с добавлением сведений о запросах и результатов.
Создайте приложение Node.js с помощью библиотеки @azure/search-documents для создания, загрузки и запроса индекса поиска.
Кроме того, можно скачать исходный код , чтобы начать с готового проекта или выполнить следующие действия, чтобы создать собственный.
Настройка среды
Для создания этого краткого руководства мы использовали следующие средства.
Создание проекта
Запустите Visual Studio Code.
Откройте палитру команд с помощью клавиш CTRL+SHIFT+P и откройте интегрированный терминал.
Создайте каталог разработки, предоставив ему краткое руководство по имени:
mkdir quickstart
cd quickstart
Инициализировать пустой проект с помощью npm, выполнив следующую команду. Чтобы полностью инициализировать проект, нажмите клавишу ВВОД несколько раз, чтобы принять значения по умолчанию, за исключением лицензии, которую необходимо задать для MIT.
npm init
Установите @azure/search-documents
пакет SDK JavaScript/TypeScript для поиска ИИ Azure.
npm install @azure/search-documents
Установка dotenv
, которая используется для импорта переменных среды, таких как имя службы поиска и ключ API.
npm install dotenv
Перейдите в каталог быстрого запуска , а затем убедитесь, что проект и его зависимости настроены, проверив, что файл package.json выглядит следующим образом:
{
"name": "quickstart",
"version": "1.0.0",
"description": "Azure AI Search Quickstart",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"Azure",
"Search"
],
"author": "Your Name",
"license": "MIT",
"dependencies": {
"@azure/search-documents": "^11.3.0",
"dotenv": "^16.0.2"
}
}
Создайте файл ENV для хранения параметров службы поиска:
SEARCH_API_KEY=<YOUR-SEARCH-ADMIN-API-KEY>
SEARCH_API_ENDPOINT=<YOUR-SEARCH-SERVICE-URL>
Замените YOUR-SEARCH-SERVICE-URL
значение именем URL-адреса конечной точки службы поиска. Замените <YOUR-SEARCH-ADMIN-API-KEY>
ключ администратора, записанный ранее.
Создание файла index.js
Затем мы создадим файл index.js , который является основным файлом, в котором размещен наш код.
В верхней части этого файла мы импортируем библиотеку @azure/search-documents
:
const { SearchIndexClient, SearchClient, AzureKeyCredential, odata } = require("@azure/search-documents");
Далее необходимо, чтобы пакет dotenv
считал параметры из файла ENV следующим образом:
// Load the .env file if it exists
require("dotenv").config();
// Getting endpoint and apiKey from .env file
const endpoint = process.env.SEARCH_API_ENDPOINT || "";
const apiKey = process.env.SEARCH_API_KEY || "";
После импорта данных и переменных среды мы готовы определить функцию main.
Большинство функций в пакете SDK являются асинхронными, поэтому мы делаем функцию main async
. Мы также включаем main().catch()
ниже функции main для перехвата и записи в журнал всех обнаруженных ошибок:
async function main() {
console.log(`Running Azure AI Search JavaScript quickstart...`);
if (!endpoint || !apiKey) {
console.log("Make sure to set valid values for endpoint and apiKey with proper authorization.");
return;
}
// remaining quickstart code will go here
}
main().catch((err) => {
console.error("The sample encountered an error:", err);
});
После этого можно создать индекс.
Создание индекса
Создайте файл hotels_quickstart_index.json. Этот файл определяет, как служба поиска ИИ Azure работает с документами, которые вы будете загружать на следующем шаге. Каждое поле идентифицируется по свойству name
, и для каждого поля указано значение type
. Каждое поле также содержит ряд атрибутов индекса, определяющих, может ли поиск, фильтрация, сортировка и аспекты в поле поиска и поиска azure. Большинство полей имеют простой тип данных, но некоторые (например, AddressType
) являются сложными типами, что позволяет создавать сложные структуры данных в индексе. Вы можете подробнее изучить поддерживаемые типы данных и атрибуты индекса, описанные в статье Создание индекса (REST API службы "Когнитивный поиск Azure").
Добавьте следующее содержимое, чтобы hotels_quickstart_index.json или скачать файл.
{
"name": "hotels-quickstart",
"fields": [
{
"name": "HotelId",
"type": "Edm.String",
"key": true,
"filterable": true
},
{
"name": "HotelName",
"type": "Edm.String",
"searchable": true,
"filterable": false,
"sortable": true,
"facetable": false
},
{
"name": "Description",
"type": "Edm.String",
"searchable": true,
"filterable": false,
"sortable": false,
"facetable": false,
"analyzerName": "en.lucene"
},
{
"name": "Description_fr",
"type": "Edm.String",
"searchable": true,
"filterable": false,
"sortable": false,
"facetable": false,
"analyzerName": "fr.lucene"
},
{
"name": "Category",
"type": "Edm.String",
"searchable": true,
"filterable": true,
"sortable": true,
"facetable": true
},
{
"name": "Tags",
"type": "Collection(Edm.String)",
"searchable": true,
"filterable": true,
"sortable": false,
"facetable": true
},
{
"name": "ParkingIncluded",
"type": "Edm.Boolean",
"filterable": true,
"sortable": true,
"facetable": true
},
{
"name": "LastRenovationDate",
"type": "Edm.DateTimeOffset",
"filterable": true,
"sortable": true,
"facetable": true
},
{
"name": "Rating",
"type": "Edm.Double",
"filterable": true,
"sortable": true,
"facetable": true
},
{
"name": "Address",
"type": "Edm.ComplexType",
"fields": [
{
"name": "StreetAddress",
"type": "Edm.String",
"filterable": false,
"sortable": false,
"facetable": false,
"searchable": true
},
{
"name": "City",
"type": "Edm.String",
"searchable": true,
"filterable": true,
"sortable": true,
"facetable": true
},
{
"name": "StateProvince",
"type": "Edm.String",
"searchable": true,
"filterable": true,
"sortable": true,
"facetable": true
},
{
"name": "PostalCode",
"type": "Edm.String",
"searchable": true,
"filterable": true,
"sortable": true,
"facetable": true
},
{
"name": "Country",
"type": "Edm.String",
"searchable": true,
"filterable": true,
"sortable": true,
"facetable": true
}
]
}
],
"suggesters": [
{
"name": "sg",
"searchMode": "analyzingInfixMatching",
"sourceFields": [
"HotelName"
]
}
]
}
После определения индекса мы импортируем hotels_quickstart_index.json в верхней части файла index.js, чтобы функция main могла получить доступ к определению индекса.
const indexDefinition = require('./hotels_quickstart_index.json');
В основной функции мы создадим SearchIndexClient
, который используется для создания индексов и управления ими для поиска ИИ Azure.
const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));
Затем нужно удалить индекс, если он уже существует. Эта операция является распространенной практикой для тестового и демонстрационного кода.
Для этого нужно определить простую функцию, которая будет удалять индекс.
async function deleteIndexIfExists(indexClient, indexName) {
try {
await indexClient.deleteIndex(indexName);
console.log('Deleting index...');
} catch {
console.log('Index does not exist yet.');
}
}
Чтобы запустить функцию, извлеките имя индекса из определения индекса и передайте indexName
вместе с indexClient
в функцию deleteIndexIfExists()
.
const indexName = indexDefinition["name"];
console.log('Checking if index exists...');
await deleteIndexIfExists(indexClient, indexName);
После этого можно создать индекс с помощью метода createIndex()
.
console.log('Creating index...');
let index = await indexClient.createIndex(indexDefinition);
console.log(`Index named ${index.name} has been created.`);
Запуск примера
Теперь все готово к запуску примера. Выполните следующие команды в окне терминала.
node index.js
Если вы скачали исходный код и еще не установили необходимые пакеты, сначала запустите npm install
.
Вы увидите ряд сообщений с описанием действий, которые выполняются программой.
Откройте обзорные сведения о службе Поиска Azure на портале Azure. Выберите вкладку "Индексы ". Вы должны увидеть примерно следующий пример:
На следующем шаге вы добавите данные в индекс.
Загрузка документов
В службе "Поиск ИИ Azure" документы — это структуры данных, которые являются входными и выходными данными для индексирования и выходных данных из запросов. Эти данные можно отправить в индекс или использовать индексатор. В этом случае мы будем программно отправлять документы в индекс.
Входные документы могут содержать строки базы данных, большие двоичные объекты из хранилища BLOB-объектов (как в этом примере) или сохраненные на диске документы JSON. Вы можете скачать файл hotels.json или создать собственный hotels.json со следующим содержимым:
{
"value": [
{
"HotelId": "1",
"HotelName": "Stay-Kay City Hotel",
"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 Time's 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.",
"Description_fr": "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.",
"Category": "Boutique",
"Tags": ["pool", "air conditioning", "concierge"],
"ParkingIncluded": false,
"LastRenovationDate": "1970-01-18T00:00:00Z",
"Rating": 3.6,
"Address": {
"StreetAddress": "677 5th Ave",
"City": "New York",
"StateProvince": "NY",
"PostalCode": "10022"
}
},
{
"HotelId": "2",
"HotelName": "Old Century Hotel",
"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.",
"Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
"Category": "Boutique",
"Tags": ["pool", "free wifi", "concierge"],
"ParkingIncluded": "false",
"LastRenovationDate": "1979-02-18T00:00:00Z",
"Rating": 3.6,
"Address": {
"StreetAddress": "140 University Town Center Dr",
"City": "Sarasota",
"StateProvince": "FL",
"PostalCode": "34243"
}
},
{
"HotelId": "3",
"HotelName": "Gastronomic Landscape Hotel",
"Description": "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.",
"Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
"Category": "Resort and Spa",
"Tags": ["air conditioning", "bar", "continental breakfast"],
"ParkingIncluded": "true",
"LastRenovationDate": "2015-09-20T00:00:00Z",
"Rating": 4.8,
"Address": {
"StreetAddress": "3393 Peachtree Rd",
"City": "Atlanta",
"StateProvince": "GA",
"PostalCode": "30326"
}
},
{
"HotelId": "4",
"HotelName": "Sublime Palace Hotel",
"Description": "Sublime Palace Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Palace is part of a lovingly restored 1800 palace.",
"Description_fr": "Le Sublime Palace Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Palace fait partie d'un Palace 1800 restauré avec amour.",
"Category": "Boutique",
"Tags": ["concierge", "view", "24-hour front desk service"],
"ParkingIncluded": true,
"LastRenovationDate": "1960-02-06T00:00:00Z",
"Rating": 4.6,
"Address": {
"StreetAddress": "7400 San Pedro Ave",
"City": "San Antonio",
"StateProvince": "TX",
"PostalCode": "78216"
}
}
]
}
Аналогично тому, что мы сделали с indexDefinition
этим, мы также должны импортировать hotels.json
в верхней части index.js , чтобы получить доступ к данным в основной функции.
const hotelData = require('./hotels.json');
Чтобы индексировать данные в индексе поиска, нужно создать SearchClient
. Хотя SearchIndexClient
используется для создания индекса и управления им, для отправки документов и запроса индекса используется SearchClient
.
Есть два способа создания представления данных SearchClient
. Первый вариант — создать SearchClient
с нуля:
const searchClient = new SearchClient(endpoint, indexName, new AzureKeyCredential(apiKey));
Кроме того, можно использовать метод getSearchClient()
из SearchIndexClient
для создания SearchClient
:
const searchClient = indexClient.getSearchClient(indexName);
Теперь, когда клиент определен, отправьте документы в индекс поиска. В этом случае мы используем метод, который отправляет документы или объединяет их с существующим документом mergeOrUploadDocuments()
, если документ с тем же ключом уже существует.
console.log('Uploading documents...');
let indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotelData['value']);
console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);
Снова запустите программу командой node index.js
. Теперь набор сообщений будет немного отличаться от тех, которые вы видели на шаге 1. На этот раз индекс уже существует, а вы получите сообщение о его удалении перед тем, как приложение создаст новый индекс и поместит в него данные.
Прежде чем выполнять запросы на следующем шаге, определите функцию, чтобы программа ожидала в течение одной секунды. Это делается только для целей тестирования и демонстрации, чтобы обеспечить завершение индексирования и доступность документов в индексе для запросов.
function sleep(ms) {
var d = new Date();
var d2 = null;
do {
d2 = new Date();
} while (d2 - d < ms);
}
Чтобы программа ожидала в течение одной секунды, вызовите функцию sleep
, как показано ниже:
sleep(1000);
Поиск в индексе
С помощью созданного индекса и отправленных документов можно отправлять запросы в индекс. В этом разделе мы отправим пять разных запросов в индекс поиска, чтобы продемонстрировать различные функциональность запросов, доступные для вас.
Запросы записываются в функцию, которую мы вызываем в главной sendQueries()
функции следующим образом:
await sendQueries(searchClient);
Запросы отправляются с помощью метода search()
объекта searchClient
. Первый параметр — это текст поиска, а второй параметр задает параметры поиска.
Первый запрос выполняет поиск *
, что эквивалентно поиску по всем данным, и выбирает три поля в индексе. Рекомендуется использовать select
только для нужных вам полей, так как извлечение ненужных данных приведет к задержкам в запросах.
Для searchOptions
этого запроса также задано includeTotalCount
значение true
, которое возвращает количество найденных результатов сопоставления.
async function sendQueries(searchClient) {
console.log('Query #1 - search everything:');
let searchOptions = {
includeTotalCount: true,
select: ["HotelId", "HotelName", "Rating"]
};
let searchResults = await searchClient.search("*", searchOptions);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
console.log(`Result count: ${searchResults.count}`);
// remaining queries go here
}
Оставшиеся запросы, описанные ниже, также нужно добавить в функцию sendQueries()
. Они разделены здесь для удобства чтения.
В следующем запросе укажите условие поиска "wifi"
, а также включите фильтр, который возвращает результаты только в том случае, если состояние равно 'FL'
. Результаты также упорядочиваются по данным отелей Rating
.
console.log('Query #2 - Search with filter, orderBy, and select:');
let state = 'FL';
searchOptions = {
filter: odata`Address/StateProvince eq ${state}`,
orderBy: ["Rating desc"],
select: ["HotelId", "HotelName", "Rating"]
};
searchResults = await searchClient.search("wifi", searchOptions);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
Далее поиск ограничивается одним полем, поддерживающим поиск, с помощью параметра searchFields
. Этот подход является отличным вариантом, чтобы сделать запрос более эффективным, если вы знаете, что вы заинтересованы только в совпадениях в определенных полях.
console.log('Query #3 - Limit searchFields:');
searchOptions = {
select: ["HotelId", "HotelName", "Rating"],
searchFields: ["HotelName"]
};
searchResults = await searchClient.search("Sublime Palace", searchOptions);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
console.log();
Еще один распространенный параметр, включаемый в запрос, — facets
. С помощью аспектов можно создавать фильтры в пользовательском интерфейсе, чтобы пользователям было проще узнать, какие значения они могут фильтровать.
console.log('Query #4 - Use facets:');
searchOptions = {
facets: ["Category"],
select: ["HotelId", "HotelName", "Rating"],
searchFields: ["HotelName"]
};
searchResults = await searchClient.search("*", searchOptions);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
В последнем запросе используется метод getDocument()
объекта searchClient
. Это позволяет эффективно получать документ по ключу.
console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument(key='3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)
Запуск примера
Запустите программу с помощью node index.js
. Теперь, помимо предыдущих шагов, запросы отправляются и результаты, записанные в консоль.
Создайте приложение Node.js с помощью библиотеки @azure/search-documents для создания, загрузки и запроса индекса поиска.
Кроме того, можно скачать исходный код , чтобы начать с готового проекта или выполнить следующие действия, чтобы создать собственный.
Настройка среды
Для создания этого краткого руководства мы использовали следующие средства.
Создание проекта
Запустите Visual Studio Code.
Откройте палитру команд с помощью клавиш CTRL+SHIFT+P и откройте интегрированный терминал.
Создайте каталог разработки, предоставив ему краткое руководство по имени:
mkdir quickstart
cd quickstart
Инициализировать пустой проект с помощью npm, выполнив следующую команду. Чтобы полностью инициализировать проект, нажмите клавишу ВВОД несколько раз, чтобы принять значения по умолчанию, за исключением лицензии, которую необходимо задать для MIT.
npm init
Установите @azure/search-documents
пакет SDK JavaScript/TypeScript для поиска ИИ Azure.
npm install @azure/search-documents
Установка dotenv
, которая используется для импорта переменных среды, таких как имя службы поиска и ключ API.
npm install dotenv
Перейдите в каталог быстрого запуска , а затем убедитесь, что проект и его зависимости настроены, проверив, что файл package.json выглядит следующим образом:
{
"name": "quickstart",
"version": "1.0.0",
"description": "Azure AI Search Quickstart",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"Azure",
"Search"
],
"author": "Your Name",
"license": "MIT",
"dependencies": {
"@azure/search-documents": "^12.1.0",
"dotenv": "^16.4.5"
}
}
Создайте файл ENV для хранения параметров службы поиска:
SEARCH_API_KEY=<YOUR-SEARCH-ADMIN-API-KEY>
SEARCH_API_ENDPOINT=<YOUR-SEARCH-SERVICE-URL>
Замените YOUR-SEARCH-SERVICE-URL
значение именем URL-адреса конечной точки службы поиска. Замените <YOUR-SEARCH-ADMIN-API-KEY>
ключ администратора, записанный ранее.
Создание файла index.ts
Затем мы создадим файл index.ts, который является основным файлом, в котором размещается наш код.
В верхней части этого файла мы импортируем библиотеку @azure/search-documents
:
import {
AzureKeyCredential,
ComplexField,
odata,
SearchClient,
SearchFieldArray,
SearchIndex,
SearchIndexClient,
SearchSuggester,
SimpleField
} from "@azure/search-documents";
Далее необходимо, чтобы пакет dotenv
считал параметры из файла ENV следующим образом:
// Load the .env file if it exists
import dotenv from 'dotenv';
dotenv.config();
// Getting endpoint and apiKey from .env file
const endpoint = process.env.SEARCH_API_ENDPOINT || "";
const apiKey = process.env.SEARCH_API_KEY || "";
После импорта данных и переменных среды мы готовы определить функцию main.
Большинство функций в пакете SDK являются асинхронными, поэтому мы делаем функцию main async
. Мы также включаем main().catch()
ниже функции main для перехвата и записи в журнал всех обнаруженных ошибок:
async function main() {
console.log(`Running Azure AI Search JavaScript quickstart...`);
if (!endpoint || !apiKey) {
console.log("Make sure to set valid values for endpoint and apiKey with proper authorization.");
return;
}
// remaining quickstart code will go here
}
main().catch((err) => {
console.error("The sample encountered an error:", err);
});
После этого можно создать индекс.
Создание индекса
Создайте файл hotels_quickstart_index.json. Этот файл определяет, как служба поиска ИИ Azure работает с документами, которые вы будете загружать на следующем шаге. Каждое поле идентифицируется по свойству name
, и для каждого поля указано значение type
. Каждое поле также содержит ряд атрибутов индекса, определяющих, может ли поиск, фильтрация, сортировка и аспекты в поле поиска и поиска azure. Большинство полей имеют простой тип данных, но некоторые (например, AddressType
) являются сложными типами, что позволяет создавать сложные структуры данных в индексе. Вы можете подробнее изучить поддерживаемые типы данных и атрибуты индекса, описанные в статье Создание индекса (REST API службы "Когнитивный поиск Azure").
Добавьте следующее содержимое, чтобы hotels_quickstart_index.json или скачать файл.
{
"name": "hotels-quickstart",
"fields": [
{
"name": "HotelId",
"type": "Edm.String",
"key": true,
"filterable": true
},
{
"name": "HotelName",
"type": "Edm.String",
"searchable": true,
"filterable": false,
"sortable": true,
"facetable": false
},
{
"name": "Description",
"type": "Edm.String",
"searchable": true,
"filterable": false,
"sortable": false,
"facetable": false,
"analyzerName": "en.lucene"
},
{
"name": "Description_fr",
"type": "Edm.String",
"searchable": true,
"filterable": false,
"sortable": false,
"facetable": false,
"analyzerName": "fr.lucene"
},
{
"name": "Category",
"type": "Edm.String",
"searchable": true,
"filterable": true,
"sortable": true,
"facetable": true
},
{
"name": "Tags",
"type": "Collection(Edm.String)",
"searchable": true,
"filterable": true,
"sortable": false,
"facetable": true
},
{
"name": "ParkingIncluded",
"type": "Edm.Boolean",
"filterable": true,
"sortable": true,
"facetable": true
},
{
"name": "LastRenovationDate",
"type": "Edm.DateTimeOffset",
"filterable": true,
"sortable": true,
"facetable": true
},
{
"name": "Rating",
"type": "Edm.Double",
"filterable": true,
"sortable": true,
"facetable": true
},
{
"name": "Address",
"type": "Edm.ComplexType",
"fields": [
{
"name": "StreetAddress",
"type": "Edm.String",
"filterable": false,
"sortable": false,
"facetable": false,
"searchable": true
},
{
"name": "City",
"type": "Edm.String",
"searchable": true,
"filterable": true,
"sortable": true,
"facetable": true
},
{
"name": "StateProvince",
"type": "Edm.String",
"searchable": true,
"filterable": true,
"sortable": true,
"facetable": true
},
{
"name": "PostalCode",
"type": "Edm.String",
"searchable": true,
"filterable": true,
"sortable": true,
"facetable": true
},
{
"name": "Country",
"type": "Edm.String",
"searchable": true,
"filterable": true,
"sortable": true,
"facetable": true
}
]
}
],
"suggesters": [
{
"name": "sg",
"searchMode": "analyzingInfixMatching",
"sourceFields": [
"HotelName"
]
}
]
}
С помощью определения индекса мы хотим импортировать hotels_quickstart_index.json в верхней части index.ts , чтобы основная функция может получить доступ к определению индекса.
// Importing the index definition and sample data
import indexDefinition from './hotels_quickstart_index.json';
interface HotelIndexDefinition {
name: string;
fields: SimpleField[] | ComplexField[];
suggesters: SearchSuggester[];
};
const hotelIndexDefinition: HotelIndexDefinition = indexDefinition as HotelIndexDefinition;
В основной функции мы создадим SearchIndexClient
, который используется для создания индексов и управления ими для поиска ИИ Azure.
const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));
Затем нужно удалить индекс, если он уже существует. Эта операция является распространенной практикой для тестового и демонстрационного кода.
Для этого нужно определить простую функцию, которая будет удалять индекс.
async function deleteIndexIfExists(indexClient: SearchIndexClient, indexName: string): Promise<void> {
try {
await indexClient.deleteIndex(indexName);
console.log('Deleting index...');
} catch {
console.log('Index does not exist yet.');
}
}
Чтобы запустить функцию, извлеките имя индекса из определения индекса и передайте indexName
вместе с indexClient
в функцию deleteIndexIfExists()
.
// Getting the name of the index from the index definition
const indexName: string = hotelIndexDefinition.name;
console.log('Checking if index exists...');
await deleteIndexIfExists(indexClient, indexName);
После этого можно создать индекс с помощью метода createIndex()
.
console.log('Creating index...');
let index = await indexClient.createIndex(hotelIndexDefinition);
console.log(`Index named ${index.name} has been created.`);
Запуск примера
На этом этапе вы готовы к сборке и запуску примера. Используйте окно терминала для выполнения следующих команд для сборки источника с tsc
помощью следующего запуска источника:node
tsc
node index.ts
Если вы скачали исходный код и еще не установили необходимые пакеты, сначала запустите npm install
.
Вы увидите ряд сообщений с описанием действий, которые выполняются программой.
Откройте обзорные сведения о службе Поиска Azure на портале Azure. Выберите вкладку "Индексы ". Вы должны увидеть примерно следующий пример:
На следующем шаге вы добавите данные в индекс.
Загрузка документов
В службе "Поиск ИИ Azure" документы — это структуры данных, которые являются входными и выходными данными для индексирования и выходных данных из запросов. Эти данные можно отправить в индекс или использовать индексатор. В этом случае мы будем программно отправлять документы в индекс.
Входные документы могут содержать строки базы данных, большие двоичные объекты из хранилища BLOB-объектов (как в этом примере) или сохраненные на диске документы JSON. Вы можете скачать файл hotels.json или создать собственный hotels.json со следующим содержимым:
{
"value": [
{
"HotelId": "1",
"HotelName": "Stay-Kay City Hotel",
"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 Time's 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.",
"Description_fr": "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.",
"Category": "Boutique",
"Tags": ["pool", "air conditioning", "concierge"],
"ParkingIncluded": false,
"LastRenovationDate": "1970-01-18T00:00:00Z",
"Rating": 3.6,
"Address": {
"StreetAddress": "677 5th Ave",
"City": "New York",
"StateProvince": "NY",
"PostalCode": "10022"
}
},
{
"HotelId": "2",
"HotelName": "Old Century Hotel",
"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.",
"Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
"Category": "Boutique",
"Tags": ["pool", "free wifi", "concierge"],
"ParkingIncluded": "false",
"LastRenovationDate": "1979-02-18T00:00:00Z",
"Rating": 3.6,
"Address": {
"StreetAddress": "140 University Town Center Dr",
"City": "Sarasota",
"StateProvince": "FL",
"PostalCode": "34243"
}
},
{
"HotelId": "3",
"HotelName": "Gastronomic Landscape Hotel",
"Description": "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.",
"Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
"Category": "Resort and Spa",
"Tags": ["air conditioning", "bar", "continental breakfast"],
"ParkingIncluded": "true",
"LastRenovationDate": "2015-09-20T00:00:00Z",
"Rating": 4.8,
"Address": {
"StreetAddress": "3393 Peachtree Rd",
"City": "Atlanta",
"StateProvince": "GA",
"PostalCode": "30326"
}
},
{
"HotelId": "4",
"HotelName": "Sublime Palace Hotel",
"Description": "Sublime Palace Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Palace is part of a lovingly restored 1800 palace.",
"Description_fr": "Le Sublime Palace Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Palace fait partie d'un Palace 1800 restauré avec amour.",
"Category": "Boutique",
"Tags": ["concierge", "view", "24-hour front desk service"],
"ParkingIncluded": true,
"LastRenovationDate": "1960-02-06T00:00:00Z",
"Rating": 4.6,
"Address": {
"StreetAddress": "7400 San Pedro Ave",
"City": "San Antonio",
"StateProvince": "TX",
"PostalCode": "78216"
}
}
]
}
Аналогично тому, что мы сделали с индексомDefinition, мы также должны импортировать hotels.json
в верхней части index.ts , чтобы получить доступ к данным в нашей главной функции.
import hotelData from './hotels.json';
interface Hotel {
HotelId: string;
HotelName: string;
Description: string;
Description_fr: string;
Category: string;
Tags: string[];
ParkingIncluded: string | boolean;
LastRenovationDate: string;
Rating: number;
Address: {
StreetAddress: string;
City: string;
StateProvince: string;
PostalCode: string;
};
};
const hotels: Hotel[] = hotelData["value"];
Чтобы индексировать данные в индексе поиска, нужно создать SearchClient
. Хотя SearchIndexClient
используется для создания индекса и управления им, для отправки документов и запроса индекса используется SearchClient
.
Есть два способа создания представления данных SearchClient
. Первый вариант — создать SearchClient
с нуля:
const searchClient = new SearchClient<Hotel>(endpoint, indexName, new AzureKeyCredential(apiKey));
Кроме того, можно использовать метод getSearchClient()
из SearchIndexClient
для создания SearchClient
:
const searchClient = indexClient.getSearchClient<Hotel>(indexName);
Теперь, когда клиент определен, отправьте документы в индекс поиска. В этом случае мы используем метод, который отправляет документы или объединяет их с существующим документом mergeOrUploadDocuments()
, если документ с тем же ключом уже существует. Затем убедитесь, что операция выполнена успешно, так как по крайней мере первый документ существует.
console.log("Uploading documents...");
const indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotels);
console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);
Снова запустите программу командой tsc && node index.ts
. Теперь набор сообщений будет немного отличаться от тех, которые вы видели на шаге 1. На этот раз индекс уже существует, а вы получите сообщение о его удалении перед тем, как приложение создаст новый индекс и поместит в него данные.
Прежде чем выполнять запросы на следующем шаге, определите функцию, чтобы программа ожидала в течение одной секунды. Это делается только для целей тестирования и демонстрации, чтобы обеспечить завершение индексирования и доступность документов в индексе для запросов.
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
Чтобы программа ждала одну секунду, вызовите функцию sleep
:
sleep(1000);
Поиск в индексе
С помощью созданного индекса и отправленных документов можно отправлять запросы в индекс. В этом разделе мы отправим пять разных запросов в индекс поиска, чтобы продемонстрировать различные функциональность запросов, доступные для вас.
Запросы записываются в функцию, которую мы вызываем в главной sendQueries()
функции следующим образом:
await sendQueries(searchClient);
Запросы отправляются с помощью метода search()
объекта searchClient
. Первый параметр — это текст поиска, а второй параметр задает параметры поиска.
Первый запрос выполняет поиск *
, что эквивалентно поиску по всем данным, и выбирает три поля в индексе. Рекомендуется использовать select
только для нужных вам полей, так как извлечение ненужных данных приведет к задержкам в запросах.
searchOptions
в этом запросе также имеет значение true
для параметра includeTotalCount
, возвращающего количество найденных результатов поиска.
async function sendQueries(
searchClient: SearchClient<Hotel>
): Promise<void> {
// Query 1
console.log('Query #1 - search everything:');
const selectFields: SearchFieldArray<Hotel> = [
"HotelId",
"HotelName",
"Rating",
];
const searchOptions1 = {
includeTotalCount: true,
select: selectFields
};
let searchResults = await searchClient.search("*", searchOptions1);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
console.log(`Result count: ${searchResults.count}`);
// remaining queries go here
}
Оставшиеся запросы, описанные ниже, также нужно добавить в функцию sendQueries()
. Они разделены здесь для удобства чтения.
В следующем запросе укажите условие поиска "wifi"
, а также включите фильтр, который возвращает результаты только в том случае, если состояние равно 'FL'
. Результаты также упорядочиваются по данным отелей Rating
.
console.log('Query #2 - search with filter, orderBy, and select:');
let state = 'FL';
const searchOptions2 = {
filter: odata`Address/StateProvince eq ${state}`,
orderBy: ["Rating desc"],
select: selectFields
};
searchResults = await searchClient.search("wifi", searchOptions2);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
Далее поиск ограничивается одним полем, поддерживающим поиск, с помощью параметра searchFields
. Этот подход является отличным вариантом, чтобы сделать запрос более эффективным, если вы знаете, что вы заинтересованы только в совпадениях в определенных полях.
console.log('Query #3 - limit searchFields:');
const searchOptions3 = {
select: selectFields,
searchFields: ["HotelName"] as const
};
searchResults = await searchClient.search("Sublime Palace", searchOptions3);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
Еще один распространенный параметр, включаемый в запрос, — facets
. Аспекты позволяют предоставлять самонаправленную детализацию из результатов в пользовательском интерфейсе. Результаты аспектов можно превратить в флажки в области результатов.
console.log('Query #4 - limit searchFields and use facets:');
const searchOptions4 = {
facets: ["Category"],
select: selectFields,
searchFields: ["HotelName"] as const
};
searchResults = await searchClient.search("*", searchOptions4);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
В последнем запросе используется метод getDocument()
объекта searchClient
. Это позволяет эффективно получать документ по ключу.
console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument('3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)
Повторное выполнение примера
Выполните сборку и запуск программы с tsc && node index.ts
помощью . Теперь, помимо предыдущих шагов, запросы отправляются и результаты, записанные в консоль.
Если вы работаете в собственной подписке, в конце проекта следует решить, нужны ли вам созданные ресурсы. Ресурсы, которые продолжат работать, могут быть платными. Вы можете удалить ресурсы по отдельности либо удалить всю группу ресурсов.
Если вы используете бесплатную службу, помните, что вы ограничены тремя индексами, индексаторами и источниками данных. Вы можете удалить отдельные элементы на портале, чтобы не превысить лимит.
В этом кратком руководстве описан набор задач для создания индекса, загрузки его с документами и выполнения запросов. Мы несколько упростили решение, чтобы код было проще читать и понимать. Теперь, когда вы знакомы с основными понятиями, ознакомьтесь с руководством, которое вызывает API поиска ИИ Azure в веб-приложении.