使用 Azure.Search.Documents 用戶端程式庫建置主控台應用程式,以建立、載入及查詢搜尋索引。
或者,您可以下載原始程式碼以使用已完成的專案來開始,或遵循這些步驟來建立您自己的程式碼。
設定您的環境
啟動 Visual Studio 並建立主控台應用程式的新專案。
在 [工具]>[NuGet 套件管理員] 中,選取 [管理解決方案的 NuGet 套件] 中。
選取瀏覽。
搜尋 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 金鑰,以使用 create/delete 權限進行驗證。
因為程式代碼會為您建置 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 ,以定義飯店文件的結構。 欄位上的屬性決定它如何在應用程式中使用。 例如,必須將 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
可以是任何資料類型、一律不可搜尋 (全文檢索搜尋查詢會將其忽略),以及可擷取 (不會隱藏)。 其他屬性預設為關閉,但可以啟用。 您可以針對僅用於篩選、Facet 或評分設定檔的文件識別碼或欄位使用 SimpleField
。 若是如此,請務必套用案例所需的任何屬性,例如文件識別碼的 IsKey = true
。 如需詳細資訊,請參閱原始程式碼中的 SimpleFieldAttribute.cs。
SearchableField
必須是字串,而且一律可搜尋並可擷取。 其他屬性預設為關閉,但可以啟用。 因為此欄位類型是可搜尋,所以其支援同義字和分析器屬性的完整補語。 如需詳細資訊,請參閱原始程式碼中的 SearchableFieldAttribute.cs。
無論您使用的是基本 SearchField
API 或其中一個 協助程式模型,都必須明確啟用篩選、Facet 和排序屬性。 例如,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 中,建立 SearchIndex 物件,然後呼叫 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 AI 搜尋服務中,搜尋文件是同時屬於索引輸入與查詢輸出的資料結構。 如同從外部資料來源所取得的一樣,文件輸入可能是資料庫中的資料列,Blob 儲存體中的 Blob 或磁碟上的 JSON 文件。 在此範例中,我們採用捷徑,並針對程式碼本身中的四家旅館內嵌 JSON 文件。
上傳文件時,您必須使用 IndexDocumentsBatch 物件。 IndexDocumentsBatch
物件包含動作集合,每個物件都包含文件與屬性,後者會告知 Azure AI 搜尋服務所應執行的動作 (上傳、合併、刪除及 mergeOrUpload)。
在 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 物件初始化之後,您就可以將其傳送至索引,方法是在 SearchClient 物件上呼叫 IndexDocuments。
將下列數行新增至 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 秒延的遲是針對索引編製進行補償,這是非同步作業,因此可以在執行查詢之前針對所有文件編製索引。 設計延遲程式碼只有在示範、測試與範例應用程式中才有必要。
搜尋索引
您可以在系統為第一個文件編製索引之後立即取得結果,但索引的實際測試應該等到系統為所有文件編製索引之後才進行。
此節新增兩個功能:查詢邏輯,以及結果。 針對查詢,請使用搜尋方法。 這個方法會接受搜尋文字 (查詢字串) 和其他選項。
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
字串中傳遞,而篩選運算式則可以在 SearchOptions 類別的篩選屬性中傳遞。 若要篩選而不進行搜尋,只要針對搜尋方法的 searchText
參數傳遞 "*"
即可。 若要在不進行篩選的情況下搜尋,則請將 Filter
屬性保留在未設定狀態,或完全不要傳入 SearchOptions
執行個體。
執行程式
按 F5 重建應用程式,並完整執行程式。
輸出中包含來自 Console.WriteLine 的訊息,並且會加上查詢資訊和結果。
使用 Azure SDK for Python 中的 Jupyter Notebook 和 azure-search-documents 程式庫來建立、載入及查詢搜尋索引。
或者,您可以下載並執行 已完成的筆記本。
設定您的環境
使用 Visual Studio Code 搭配 Python 延伸模組或對等的 IDE 搭配 Python 3.10 或更新版本。
建議本快速入門的虛擬環境:
啟動 Visual Studio Code。
開啟命令選擇區 (Ctrl+Shift+P)。
搜尋 Python:建立環境。
選取 Venv.
選取 Python 解譯器。 選擇 3.10 版或更新版本。
設定可能需要一分鐘的時間。 如果您遇到問題,請參閱 VS Code 中的 Python 環境。
安裝套件並設定變數
安裝套件,包括 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')
建立文件承載
針對作業類型使用索引動作,例如上傳或合併和上傳。 檔源自 GitHub 上的 HotelsData 範例。
# 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 class 類別的 search 方法。
此範例會執行空的搜尋 (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"]))
新增 Facet
針對在搜尋結果中找到的正面相符項目,會產生 Facet。 沒有零相符項目。 如果搜尋結果不包含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/City
、Address/Country
。
若要模擬自動完成,請將字母 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'])
使用 Azure.Search.Documents 程式庫建置 JAVA 控制台應用程式,以建立、載入及查詢搜尋索引。
或者,您可以下載原始程式碼以使用已完成的專案來開始,或遵循這些步驟來建立您自己的程式碼。
設定您的環境
使用下列工具來建立本快速入門。
建立專案
啟動 Visual Studio Code。
使用 Ctrl+Shift+P 開啟命令選擇區。 搜尋建立 JAVA 專案。
選取 [Maven]。
選取 maven-archetype-quickstart。
選取最新版本,目前為 1.4。
輸入 azure.search.sample 作為群組標識碼。
輸入 azuresearchquickstart 作為成品標識碼。
選取要用來建立專案的資料夾。
在整合式終端機中完成專案建立。 按 Enter 以接受「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>
建立搜尋用戶端
在 src、main、java、azure、search、sample 下開啟 App
類別。 新增下列匯入指示詞。
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 程式庫的同步方法。 不過,在生產案例中,則請使用非同步方法,以讓應用程式保持可調整和能回應的狀態。 例如,您會使用 SearchAsyncClient,而不是 SearchClient。
將空類別定義新增至您的專案: 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
可以是任何資料類型、一律不可搜尋 (全文檢索搜尋查詢會將其忽略),以及可擷取 (不會隱藏)。 其他屬性預設為關閉,但可以啟用。 您可以針對僅用於篩選、Facet 或評分設定檔的文件識別碼或欄位使用 SimpleField。 若是如此,請務必套用案例所需的任何屬性,例如文件識別碼的 IsKey = true。
SearchableField
必須是字串,而且一律可搜尋並可擷取。 其他屬性預設為關閉,但可以啟用。 因為此欄位類型是可搜尋,所以其支援同義字和分析器屬性的完整補語。
無論您使用的是基本 SearchField
API 或其中一個 協助程式模型,都必須明確啟用篩選、Facet 和排序屬性。 例如, 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
中,在方法中main
建立 SearchIndex
對象,然後呼叫 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 AI 搜尋服務中,搜尋文件是同時屬於索引輸入與查詢輸出的資料結構。 如同從外部資料來源所取得的一樣,文件輸入可能是資料庫中的資料列,Blob 儲存體中的 Blob 或磁碟上的 JSON 文件。 在此範例中,我們採用捷徑,並針對程式碼本身中的四家旅館內嵌 JSON 文件。
上傳文件時,您必須使用 IndexDocumentsBatch 物件。 IndexDocumentsBatch
物件包含 IndexActions 集合,每個物件都包含文件與屬性,後者會告知 Azure 認知搜尋所應執行的動作 (上傳、合併、刪除及 mergeOrUpload)。
在 中 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
物件之後,您可以將它傳送到索引,方式是呼叫您 SearchClient
物件上的 indexDocuments。
將下列數行新增至 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 秒延的遲是針對索引編製進行補償,這是非同步作業,因此可以在執行查詢之前針對所有文件編製索引。 設計延遲程式碼只有在示範、測試與範例應用程式中才有必要。
搜尋索引
您可以在系統為第一個文件編製索引之後立即取得結果,但索引的實際測試應該等到系統為所有文件編製索引之後才進行。
此節新增兩個功能:查詢邏輯,以及結果。 針對查詢,請使用搜尋方法。 這個方法會接受搜尋文字 (查詢字串) 以及其他選項。
在 中 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();
最後一個查詢會顯示自動完成的 語法,模擬 的部分使用者輸入 , 其解析為與您在索引中定義之建議工具相關聯的兩個可能相符 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
字串中傳遞,而篩選運算式則可以在 SearchOptions 類別的 filter
屬性中傳遞。 若要篩選而不進行搜尋,只要針對 search
方法的 searchText
參數傳遞「*」即可。 若要在不進行篩選的情況下搜尋,則請將 filter
屬性保留在未設定狀態,或完全不要傳入 SearchOptions
執行個體。
執行程式
按下 F5 以重建應用程式並完整執行程式。
輸出包含 來自 System.out.println
的訊息,並新增查詢資訊和結果。
使用 @azure/search-documents 程式庫建置 Node.js 應用程式,以建立、載入及查詢搜尋索引。
或者,您可以下載原始程式碼以使用已完成的專案來開始,或遵循這些步驟來建立您自己的程式碼。
設定您的環境
我們已使用下列工具來建立此快速入門。
建立專案
啟動 Visual Studio Code。
使用 Ctrl+Shift+P 開啟命令選擇區,然後開啟整合式終端機。
建立開發目錄,並提供名稱 快速入門:
mkdir quickstart
cd quickstart
執行下列命令,以 npm 初始化空的專案。 若要完整初始化專案,請按 Enter 多次以接受預設值,但 [授權] 除外,您應該將它設定為 MIT。
npm init
安裝 @azure/search-documents
,適用於 Azure AI 搜尋服務的 JavaScript/TypeScript SDK。
npm install @azure/search-documents
安裝用來匯入環境變數的 dotenv
,例如您的搜尋服務名稱和 API 金鑰。
npm install dotenv
瀏覽至 快速入門 目錄,然後確認您已藉由檢查您的 package.json 檔案看起來類似下列 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 中的大部分功能都是非同步,因此我們會將主要函式 async
。 我們也會在 main 函式下包含 main().catch()
,以攔截並記錄遇到的任何錯誤:
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 AI 搜尋服務如何使用您要在下一個步驟中載入的文件。 每個欄位都會由 name
識別,並具有指定的 type
。 每個欄位也有一系列的索引屬性,可指定 Azure AI 搜尋服務是否可在欄位上進行搜尋、篩選、排序和 Facet 處理。 大部分的欄位都是簡單的資料類型,但有些 (像是 AddressType
) 則為複雜類型,其可讓您在索引中建立豐富的資料結構。 您可以深入了解建立索引 (REST) 中所述的支援資料類型和索引屬性。
將下列內容新增至 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');
在 main 函式中,我們接著建立一個 SearchIndexClient
,用來建立和管理 Azure AI 搜尋服務的索引。
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 AI 搜尋服務中,文件是同時屬於索引輸入與查詢輸出的資料結構。 您可以將這類資料推送至索引,或使用索引子。 在此情況下,我們會以程式設計方式將文件推送至索引。
文件輸入可能是資料庫中的資料列、Blob 儲存體中的 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
處理方式,我們也需要在index.js頂端匯hotels.json
入,以便在主要函式中存取數據。
const hotelData = require('./hotels.json');
若要在搜尋索引中編製資料索引,現在需要建立 SearchClient
。 當 SearchIndexClient
用來建立和管理索引時,SearchClient
用來上傳文件和查詢索引。
建立 SearchClient
的方法有兩種。 第一個選項是從頭開始建立 SearchClient
:
const searchClient = new SearchClient(endpoint, indexName, new AzureKeyCredential(apiKey));
或者,您可以使用 SearchIndexClient
的 getSearchClient()
方法來建立 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()
我們在main函式中呼叫的函式撰寫,如下所示:
await sendQueries(searchClient);
系統會使用 searchClient
的 search()
方法來傳送查詢。 第一個參數是搜尋文字,而第二個參數則指定搜尋選項。
第一個查詢會搜尋 *
,這相當於搜尋所有項目,並選取索引中的三個欄位。 最佳做法是只針對所需的欄位執行 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
。 Facet 可讓您在 UI 上建立篩選,讓使用者能夠輕鬆地知道可以篩選的值。
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)}`);
}
最後一個查詢會使用 searchClient
的 getDocument()
方法。 這可讓您依其索引鍵有效率地擷取文件。
console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument(key='3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)
執行範例
使用 node index.js
執行程式。 現在,除了先前的步驟之外,查詢也會傳送,並將結果寫入主控台。
使用 @azure/search-documents 程式庫建置 Node.js 應用程式,以建立、載入及查詢搜尋索引。
或者,您可以下載原始程式碼以使用已完成的專案來開始,或遵循這些步驟來建立您自己的程式碼。
設定您的環境
我們已使用下列工具來建立此快速入門。
建立專案
啟動 Visual Studio Code。
使用 Ctrl+Shift+P 開啟命令選擇區,然後開啟整合式終端機。
建立開發目錄,並提供名稱 快速入門:
mkdir quickstart
cd quickstart
執行下列命令,以 npm 初始化空的專案。 若要完整初始化專案,請按 Enter 多次以接受預設值,但 [授權] 除外,您應該將它設定為 MIT。
npm init
安裝 @azure/search-documents
,適用於 Azure AI 搜尋服務的 JavaScript/TypeScript SDK。
npm install @azure/search-documents
安裝用來匯入環境變數的 dotenv
,例如您的搜尋服務名稱和 API 金鑰。
npm install dotenv
瀏覽至 快速入門 目錄,然後確認您已藉由檢查您的 package.json 檔案看起來類似下列 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 中的大部分功能都是非同步,因此我們會將主要函式 async
。 我們也會在 main 函式下包含 main().catch()
,以攔截並記錄遇到的任何錯誤:
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 AI 搜尋服務如何使用您要在下一個步驟中載入的文件。 每個欄位都會由 name
識別,並具有指定的 type
。 每個欄位也有一系列的索引屬性,可指定 Azure AI 搜尋服務是否可在欄位上進行搜尋、篩選、排序和 Facet 處理。 大部分的欄位都是簡單的資料類型,但有些 (像是 AddressType
) 則為複雜類型,其可讓您在索引中建立豐富的資料結構。 您可以深入了解建立索引 (REST) 中所述的支援資料類型和索引屬性。
將下列內容新增至 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 的頂端,讓 main 函式可以存取索引定義。
// 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;
在 main 函式中,我們接著建立一個 SearchIndexClient
,用來建立和管理 Azure AI 搜尋服務的索引。
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 AI 搜尋服務中,文件是同時屬於索引輸入與查詢輸出的資料結構。 您可以將這類資料推送至索引,或使用索引子。 在此情況下,我們會以程式設計方式將文件推送至索引。
文件輸入可能是資料庫中的資料列、Blob 儲存體中的 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.ts 頂端,以便在 main 函數中存取資料。
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));
或者,您可以使用 SearchIndexClient
的 getSearchClient()
方法來建立 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()
我們在main函式中呼叫的函式撰寫,如下所示:
await sendQueries(searchClient);
系統會使用 searchClient
的 search()
方法來傳送查詢。 第一個參數是搜尋文字,而第二個參數則指定搜尋選項。
第一個查詢會搜尋 *
,這相當於搜尋所有項目,並選取索引中的三個欄位。 最佳做法是只針對所需的欄位執行 select
,因為提取不必要的資料可能會讓查詢延遲。
此查詢的 searchOptions
也將 includeTotalCount
設定為 true
,這會傳回找到的相符結果數目。
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
。 Facet 可讓您從 UI 中的結果提供自我導向的向下鑽研。 Facet 結果可以轉換成結果窗格中的核取方塊。
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)}`);
}
最後一個查詢會使用 searchClient
的 getDocument()
方法。 這可讓您依其索引鍵有效率地擷取文件。
console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument('3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)
重新執行範例
以 tsc && node index.ts
建置並執行程式。 現在,除了先前的步驟之外,查詢也會傳送,並將結果寫入主控台。
如果您是在自己的訂用帳戶中進行,建議您在專案結束時判斷自己是否仍需要先前所建立的資源。 資源若繼續執行,將需付費。 您可以個別刪除資源,或刪除資源群組以刪除整組資源。
在此快速入門中,您已執行一組工作來建立索引、使用文件來載入索引,以及執行查詢。 在不同的階段中,我們採用捷徑,以簡化程式碼,讓程式碼更容易閱讀及傳達概念。 現在您已熟悉基本概念,請嘗試在 Web 應用程式中呼叫 Azure AI 搜尋服務 API 的教學課程。