本文說明如何使用 C# 和舊版用戶端連結庫,在適用於 .NET 的 Azure SDK 中建立和管理搜尋物件, Microsoft.Azure.Search (第 10 版)。
第 10 版是 Microsoft.Azure.Search 套件的最後一個版本。 接下來,Azure SDK 小組將在 Azure.Search.Documents 中推出新功能。
備註
如果您有現有的或內建開發專案,則可以繼續使用第10版。 對於新專案,或者使用新功能,您應該轉換至 新的函式庫。
關於第 10 版
SDK 包含一些用戶端連結庫,可讓您管理索引、數據源、索引器和同義字對應,以及上傳和管理檔,以及執行查詢,而不需要處理 HTTP 和 JSON 的詳細數據。 這些用戶端程式庫全都以 NuGet 套件的形式散發。
主要的 NuGet 套件是 Microsoft.Azure.Search,這是中繼套件,其中包含所有其他套件作為相依性。 如果您剛開始使用,或知道您的應用程式需要 Azure 認知搜尋的所有功能,請使用此套件。
SDK 中的其他 NuGet 套件如下:
-
Microsoft.Azure.Search.Data:如果您要使用 Azure 認知搜尋開發 .NET 應用程式,而且只需要查詢或更新索引中的檔案,請使用此套件。 如果您也需要建立或更新索引、同義字對應或其他服務層級資源,請改用Microsoft.Azure.Search套件。 -
Microsoft.Azure.Search.Service:如果您要在 .NET 中開發自動化,以管理 Azure 認知搜尋索引、同義字對應、索引器、數據源或其他服務層級資源,請使用此套件。 如果您只需要查詢或更新索引中的檔,請改用Microsoft.Azure.Search.Data套件。 如果您需要 Azure 認知搜尋的所有功能,請改用套件Microsoft.Azure.Search。 -
Microsoft.Azure.Search.Common:Azure 認知搜尋 .NET 連結庫所需的常見類型。 您不需要直接在應用程式中使用此套件。 它僅用作依賴項。
各種客戶端庫會定義類別,例如Index、Field和Document,以及對Indexes.Create和Documents.Search類別的SearchServiceClient和SearchIndexClient操作。 這些類別可編成以下命名空間:
如果您想要提供 SDK 未來更新的意見反應,請參閱我們的 意見反應頁面 ,或在 GitHub 上建立問題,並在問題標題中提及「Azure 認知搜尋」。
.NET SDK 的目標是 Azure 認知搜尋 REST API 的版本2019-05-06。 此版本在索引 Azure Blobs 時,包含對 複雜類型、AI 擴充、自動完成 和 JsonLines 剖析模式 的支援。
此 SDK 不支援 管理作業 ,例如建立和調整搜尋服務和管理 API 金鑰。 如果您需要從 .NET 應用程式管理搜尋資源,您可以使用 Azure 認知搜尋 .NET 管理 SDK。
升級至 v10
如果您已經使用舊版的 Azure 認知搜尋 .NET SDK,而且想要升級至最新的正式推出版本, 本文 將說明如何。
SDK 需求
- Visual Studio 2017 或更新版本。
- 您自己的 Azure 認知搜尋服務。 若要使用 SDK,您需要服務的名稱和一或多個 API 金鑰。 在入口網站中建立服務 可協助您完成這些步驟。
- 使用 Visual Studio 中的「管理 NuGet 套件」,下載 Azure 認知搜尋 .NET SDK NuGet 套件 。 只要在 NuGet.org 上搜尋套件名稱
Microsoft.Azure.Search(或上述其中一個套件名稱,如果您只需要功能的子集)。
Azure 認知搜尋 .NET SDK 支援以 .NET Framework 4.5.2 和更新版本為目標的應用程式,以及 .NET Core 2.0 和更新版本。
核心案例
在您的搜尋應用程式中,您需要執行幾項事項。 在本教學課程中,我們將討論下列核心案例:
- 建立索引
- 使用文件填充索引
- 使用全文搜索和篩選來搜尋檔
下列範例程式代碼說明上述每個案例。 請隨意在您自己的應用程式中使用代碼段。
概觀
我們將探索的範例應用程式會建立名為 「hotels」 的新索引,並填入一些檔,然後執行一些搜尋查詢。 以下是主要程式,其中顯示整體流程:
// This sample shows how to delete, create, upload documents and query an index
static void Main(string[] args)
{
IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
IConfigurationRoot configuration = builder.Build();
SearchServiceClient serviceClient = CreateSearchServiceClient(configuration);
string indexName = configuration["SearchIndexName"];
Console.WriteLine("{0}", "Deleting index...\n");
DeleteIndexIfExists(indexName, serviceClient);
Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, serviceClient);
ISearchIndexClient indexClient = serviceClient.Indexes.GetClient(indexName);
Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(indexClient);
ISearchIndexClient indexClientForQueries = CreateSearchIndexClient(configuration);
RunQueries(indexClientForQueries);
Console.WriteLine("{0}", "Complete. Press any key to end application...\n");
Console.ReadKey();
}
備註
您可以在 GitHub 上找到範例應用程式的完整原始程式碼。
我們將逐步說明這個過程。 首先,我們需要建立新的 SearchServiceClient。 此物件可讓您管理索引。 若要建構一個,您必須提供 Azure 認知搜尋服務名稱和系統管理 API 金鑰。 您可以在appsettings.json檔案中輸入這項資訊。
private static SearchServiceClient CreateSearchServiceClient(IConfigurationRoot configuration)
{
string searchServiceName = configuration["SearchServiceName"];
string adminApiKey = configuration["SearchServiceAdminApiKey"];
SearchServiceClient serviceClient = new SearchServiceClient(searchServiceName, new SearchCredentials(adminApiKey));
return serviceClient;
}
備註
如果您提供了一個不正確的金鑰(例如,在需要管理金鑰的情況下提供了一個查詢金鑰),那麼在您第一次呼叫其作業方法時,SearchServiceClient 將會擲回 CloudException,並顯示「禁止存取」的錯誤訊息,例如 Indexes.Create。 如果發生這種情況,請仔細檢查我們的 API 金鑰。
接下來幾行會呼叫方法來建立名為 「hotels」 的索引,如果索引已經存在,請先將其刪除。 稍後我們將逐步解說這些方法。
Console.WriteLine("{0}", "Deleting index...\n");
DeleteIndexIfExists(indexName, serviceClient);
Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, serviceClient);
接下來,必須填入索引。 若要填充索引,我們需要SearchIndexClient。 有兩種方式可以取得一個:一是透過在Indexes.GetClient上呼叫SearchServiceClient,二是建構它。 為了方便起見,我們會使用後者。
ISearchIndexClient indexClient = serviceClient.Indexes.GetClient(indexName);
備註
在一般搜尋應用程式中,索引管理和母體可能會由與搜尋查詢不同的元件來處理。
Indexes.GetClient 方便填入索引,因為它可節省您提供其他 SearchCredentials的麻煩。 其方式是將您用來建立 SearchServiceClient 的管理金鑰傳遞至新的 SearchIndexClient。 不過,在執行查詢的應用程式部分,最好直接建立 SearchIndexClient ,讓您可以傳入查詢密鑰,這隻允許您讀取數據,而不是系統管理密鑰。 這與最低許可權原則一致,有助於讓您的應用程式更安全。 您可以在 這裏深入瞭解系統管理金鑰和查詢金鑰。
現在我們有 SearchIndexClient,我們可以填入索引。 索引生成是通過另一種方法完成的,我們稍後會逐步解說。
Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(indexClient);
最後,我們會執行一些搜尋查詢並顯示結果。 這次我們使用不同的 SearchIndexClient:
ISearchIndexClient indexClientForQueries = CreateSearchIndexClient(indexName, configuration);
RunQueries(indexClientForQueries);
我們稍後會進一步瞭解 RunQueries 方法。 以下是要建立新 SearchIndexClient的程式代碼:
private static SearchIndexClient CreateSearchIndexClient(string indexName, IConfigurationRoot configuration)
{
string searchServiceName = configuration["SearchServiceName"];
string queryApiKey = configuration["SearchServiceQueryApiKey"];
SearchIndexClient indexClient = new SearchIndexClient(searchServiceName, indexName, new SearchCredentials(queryApiKey));
return indexClient;
}
這次我們使用查詢索引鍵,因為我們不需要索引的寫入許可權。 您可以在appsettings.json檔案中輸入這項資訊。
如果您使用有效的服務名稱和 API 金鑰執行此應用程式,輸出看起來應該像下列範例:(某些控制台輸出已取代為 “...”為了說明目的。)
Deleting index...
Creating index...
Uploading documents...
Waiting for documents to be indexed...
Search the entire index for the term 'motel' and return only the HotelName field:
Name: Secret Point Motel
Name: Twin Dome Motel
Apply a filter to the index to find hotels with a room cheaper than $100 per night, and return the hotelId and description:
HotelId: 1
Description: The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Times Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.
HotelId: 2
Description: The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts.
Search the entire index, order by a specific field (lastRenovationDate) in descending order, take the top two results, and show only hotelName and lastRenovationDate:
Name: Triple Landscape Hotel
Last renovated on: 9/20/2015 12:00:00 AM +00:00
Name: Twin Dome Motel
Last renovated on: 2/18/1979 12:00:00 AM +00:00
Search the hotel names for the term 'hotel':
HotelId: 3
Name: Triple Landscape Hotel
...
Complete. Press any key to end application...
本文結尾會提供應用程式的完整原始程式碼。
接下來,我們將仔細查看由Main呼叫的每個方法。
建立索引
建立 SearchServiceClient之後, Main 如果已經存在,就會刪除 「hotels」 索引。 該刪除是由下列方法完成:
private static void DeleteIndexIfExists(string indexName, SearchServiceClient serviceClient)
{
if (serviceClient.Indexes.Exists(indexName))
{
serviceClient.Indexes.Delete(indexName);
}
}
這個方法會使用指定的 SearchServiceClient 來檢查索引是否存在,如果是的話,請將其刪除。
備註
本文中的範例程式代碼會使用 Azure 認知搜尋 .NET SDK 的同步方法來簡化。 建議您在自己的應用程式中使用異步方法,使其保持可調整且回應。 例如,在上述方法中,您可以使用 ExistsAsync 和 DeleteAsync ,而不是 Exists 和 Delete。
接下來, Main 呼叫這個方法,以建立新的「旅館」索引:
private static void CreateIndex(string indexName, SearchServiceClient serviceClient)
{
var definition = new Index()
{
Name = indexName,
Fields = FieldBuilder.BuildForType<Hotel>()
};
serviceClient.Indexes.Create(definition);
}
這個方法會建立新的 Index 物件,其中包含定義新索引架構的物件清單 Field 。 每個欄位均有一個名稱、資料類型和一些屬性,以用於定義欄位的搜尋行為。
FieldBuilder 類別使用反射來檢查指定的 Field 模型類別的公用屬性和屬性,並據此建立索引的 Hotel 物件清單。 我們稍後會仔細查看 Hotel 類別。
備註
如有需要,您永遠可以直接建立 Field 物件清單,而不是使用FieldBuilder。 例如,您可能不想使用模型類別,或者您可能需要使用您不想透過新增屬性來修改的現有模型類別。
除了欄位之外,您也可以將評分配置檔、建議工具或 CORS 選項新增至索引(這些參數會從範例中省略,以求簡潔)。 您可以在 SDK 參考中,以及 Azure 認知搜尋 REST API 參考中找到 Index 物件及其組成部分的詳細資訊。
填充索引
中的下一個步驟 Main 會填入新建立的索引。 此索引母體擴展是在下列方法中完成的:(某些程序代碼已取代為 “...”為了說明目的。如需完整的數據母體擴展程序代碼,請參閱完整的範例解決方案。
private static void UploadDocuments(ISearchIndexClient indexClient)
{
var hotels = new Hotel[]
{
new Hotel()
{
HotelId = "1",
HotelName = "Secret Point Motel",
...
Address = new Address()
{
StreetAddress = "677 5th Ave",
...
},
Rooms = new Room[]
{
new Room()
{
Description = "Budget Room, 1 Queen Bed (Cityside)",
...
},
new Room()
{
Description = "Budget Room, 1 King Bed (Mountain View)",
...
},
new Room()
{
Description = "Deluxe Room, 2 Double Beds (City View)",
...
}
}
},
new Hotel()
{
HotelId = "2",
HotelName = "Twin Dome Motel",
...
{
StreetAddress = "140 University Town Center Dr",
...
},
Rooms = new Room[]
{
new Room()
{
Description = "Suite, 2 Double Beds (Mountain View)",
...
},
new Room()
{
Description = "Standard Room, 1 Queen Bed (City View)",
...
},
new Room()
{
Description = "Budget Room, 1 King Bed (Waterfront View)",
...
}
}
},
new Hotel()
{
HotelId = "3",
HotelName = "Triple Landscape Hotel",
...
Address = new Address()
{
StreetAddress = "3393 Peachtree Rd",
...
},
Rooms = new Room[]
{
new Room()
{
Description = "Standard Room, 2 Queen Beds (Amenities)",
...
},
new Room ()
{
Description = "Standard Room, 2 Double Beds (Waterfront View)",
...
},
new Room()
{
Description = "Deluxe Room, 2 Double Beds (Cityside)",
...
}
}
}
};
var batch = IndexBatch.Upload(hotels);
try
{
indexClient.Documents.Index(batch);
}
catch (IndexBatchException e)
{
// Sometimes when your Search service is under load, indexing will fail for some of the documents in
// the batch. Depending on your application, you can take compensating actions like delaying and
// retrying. For this simple demo, we just log the failed document keys and continue.
Console.WriteLine(
"Failed to index some of the documents: {0}",
String.Join(", ", e.IndexingResults.Where(r => !r.Succeeded).Select(r => r.Key)));
}
Console.WriteLine("Waiting for documents to be indexed...\n");
Thread.Sleep(2000);
}
此方法分四個部分。 第一個會建立一個陣列,裡面包含 3 個 Hotel 物件,每個都有 3 個 Room 物件,作為要上傳至索引的輸入數據。 為簡單起見,此資料採硬式編碼。 在您自己的應用程式中,您的資料可能來自外部數據源,例如 SQL 資料庫。
第二個部分會建立一個包含文件的IndexBatch容器。 您在建立批次時 (在此案例中,是藉由呼叫 IndexBatch.Upload),指定要套用至該批次的作業。 然後,使用方法 Documents.Index 將批次上傳至 Azure 認知搜尋索引。
備註
在此範例中,我們只是上傳檔。 如果您想要將變更合併至現有的文件,或是刪除文件,您可以改為呼叫 IndexBatch.Merge、IndexBatch.MergeOrUpload 或 IndexBatch.Delete 來建立批次。 您也可以藉由呼叫 IndexBatch.New來混合單一批次中的不同作業,這會採用 物件的集合 IndexAction ,每個作業都會告訴 Azure 認知搜尋在文件上執行特定作業。 您可以建立每個具有自身操作功能的 IndexAction,方法是呼叫對應的方法,例如 IndexAction.Merge、IndexAction.Upload 等等。
此方法的第三部分是 catch 區塊,用來處理索引的重要的錯誤情況。 如果您的 Azure 認知搜尋服務無法為批次中的某些檔案編制索引,IndexBatchException 會由 Documents.Index 拋出。 如果您在服務負載過重時編製檔索引,就會發生此例外狀況。 我們強烈建議您在程式碼中明確處理此情況。 您可以延遲,然後重新嘗試將失敗的文件編制索引,或像範例一樣加以記錄並繼續,或是根據您應用程式的資料一致性需求執行其他操作。
備註
您可以使用 FindFailedActionsToRetry 方法來建構新的批次,只包含先前呼叫 Index中失敗的動作。 有一個討論如何在 StackOverflow 上正確使用它。
最後,UploadDocuments 方法會延遲兩秒。 Azure 認知搜尋服務中會以異步方式編製索引,因此範例應用程式需要等候一小段時間,以確保檔可供搜尋。 通常只有在示範、測試和範例應用程式中,才需要這類延遲。
.NET SDK 如何處理檔
您可能想知道 Azure 認知搜尋 .NET SDK 如何能夠將使用者定義類別的實例上傳 Hotel 至索引。 為了協助回答這個問題,讓我們看看 類別 Hotel :
using System;
using Microsoft.Azure.Search;
using Microsoft.Azure.Search.Models;
using Microsoft.Spatial;
using Newtonsoft.Json;
public partial class Hotel
{
[System.ComponentModel.DataAnnotations.Key]
[IsFilterable]
public string HotelId { get; set; }
[IsSearchable, IsSortable]
public string HotelName { get; set; }
[IsSearchable]
[Analyzer(AnalyzerName.AsString.EnLucene)]
public string Description { get; set; }
[IsSearchable]
[Analyzer(AnalyzerName.AsString.FrLucene)]
[JsonProperty("Description_fr")]
public string DescriptionFr { get; set; }
[IsSearchable, IsFilterable, IsSortable, IsFacetable]
public string Category { get; set; }
[IsSearchable, IsFilterable, IsFacetable]
public string[] Tags { get; set; }
[IsFilterable, IsSortable, IsFacetable]
public bool? ParkingIncluded { get; set; }
// SmokingAllowed reflects whether any room in the hotel allows smoking.
// The JsonIgnore attribute indicates that a field should not be created
// in the index for this property and it will only be used by code in the client.
[JsonIgnore]
public bool? SmokingAllowed => (Rooms != null) ? Array.Exists(Rooms, element => element.SmokingAllowed == true) : (bool?)null;
[IsFilterable, IsSortable, IsFacetable]
public DateTimeOffset? LastRenovationDate { get; set; }
[IsFilterable, IsSortable, IsFacetable]
public double? Rating { get; set; }
public Address Address { get; set; }
[IsFilterable, IsSortable]
public GeographyPoint Location { get; set; }
public Room[] Rooms { get; set; }
}
首先要注意的是,類別中 Hotel 每個公用屬性的名稱都會對應至索引定義中具有相同名稱的欄位。 如果您希望每個欄位的首字母小寫(即 "camelCase"),可以在類別上使用 [SerializePropertyNamesAsCamelCase] 屬性,讓 SDK 自動將屬性名稱對應為 camelCase。 在 .NET 應用程式中,這種情況很常見,尤其是在執行數據綁定時,當目標架構不在開發者的控制範圍內時,不需要違反 .NET 的「Pascal 命名法」指導方針。
備註
Azure 認知搜尋 .NET SDK 會使用 NewtonSoft JSON.NET 程式庫,將自定義模型物件序列化和反序列化至 JSON。 您可以視需要自定義此串行化。 如需詳細資訊,請參閱 使用 JSON.NET 進行自定義串行化。
要注意的第二件事是,每個屬性都以IsFilterable、IsSearchable、Key和Analyzer等屬性裝飾。 這些屬性會直接對應至 Azure 認知搜尋索引中的對應欄位屬性。
FieldBuilder 類別會使用這些屬性來建構索引的欄位定義。
類別的第三個重要事項 Hotel 是公用屬性的數據類型。 這些屬性的 .NET 類型會對應至索引定義中的對等字段類型。 例如,Category 字串屬性會對應至 category 欄位 (此欄位屬於 Edm.String 類型)。
bool? 與 Edm.Boolean、DateTimeOffset? 與 Edm.DateTimeOffset 等屬性之間也有類似的類型對應。 類型映射的特定規則記載於 Documents.Get 方法,並在 Azure 認知搜尋 .NET SDK 參考中。 類別FieldBuilder會為您處理這個對應,但了解它的運作原理仍然是有幫助的,以防您需要針對任何序列化問題進行疑難排解。
您是否有注意到 SmokingAllowed 屬性?
[JsonIgnore]
public bool? SmokingAllowed => (Rooms != null) ? Array.Exists(Rooms, element => element.SmokingAllowed == true) : (bool?)null;
此屬性上的 JsonIgnore 屬性會指導 FieldBuilder 不要將它序列化成索引中的欄位。 這是一個建立用戶端計算屬性的絕佳方式,您可以在應用程式中作為輔助工具使用。 在此情況下,SmokingAllowed 屬性會反映 Room 集合中的任何 Rooms 是否允許吸煙。 如果都是假的,則表示整個酒店不允許吸煙。
某些屬性,例如 Address 和 Rooms 是 .NET 類別的實例。 這些屬性代表更複雜的數據結構,因此需要索引中具有 複雜數據類型 的欄位。
屬性 Address 代表 類別中的 Address 一組多個值,定義如下:
using System;
using Microsoft.Azure.Search;
using Microsoft.Azure.Search.Models;
using Newtonsoft.Json;
namespace AzureSearch.SDKHowTo
{
public partial class Address
{
[IsSearchable]
public string StreetAddress { get; set; }
[IsSearchable, IsFilterable, IsSortable, IsFacetable]
public string City { get; set; }
[IsSearchable, IsFilterable, IsSortable, IsFacetable]
public string StateProvince { get; set; }
[IsSearchable, IsFilterable, IsSortable, IsFacetable]
public string PostalCode { get; set; }
[IsSearchable, IsFilterable, IsSortable, IsFacetable]
public string Country { get; set; }
}
}
這個類別包含用來描述美國或加拿大地址的標準值。 您可以使用這類類型,將索引中的邏輯欄位群組在一起。
屬性 Rooms 代表 物件的陣列 Room :
using System;
using Microsoft.Azure.Search;
using Microsoft.Azure.Search.Models;
using Newtonsoft.Json;
namespace AzureSearch.SDKHowTo
{
public partial class Room
{
[IsSearchable]
[Analyzer(AnalyzerName.AsString.EnMicrosoft)]
public string Description { get; set; }
[IsSearchable]
[Analyzer(AnalyzerName.AsString.FrMicrosoft)]
[JsonProperty("Description_fr")]
public string DescriptionFr { get; set; }
[IsSearchable, IsFilterable, IsFacetable]
public string Type { get; set; }
[IsFilterable, IsFacetable]
public double? BaseRate { get; set; }
[IsSearchable, IsFilterable, IsFacetable]
public string BedOptions { get; set; }
[IsFilterable, IsFacetable]
public int SleepsCount { get; set; }
[IsFilterable, IsFacetable]
public bool? SmokingAllowed { get; set; }
[IsSearchable, IsFilterable, IsFacetable]
public string[] Tags { get; set; }
}
}
.NET 中的數據模型及其對應的索引架構應該設計成支援您想要提供給使用者的搜尋體驗。 .NET 中的每個最上層物件,即索引中的檔,會對應至您在使用者介面中出現的搜尋結果。 例如,在旅館搜尋應用程式中,使用者可能想要依旅館名稱、旅館功能或特定房間的特性來搜尋。 稍後我們將討論一些查詢範例。
這種能夠使用您自己的類別來與索引互動的能力具有雙向運作的效果;您也可以擷取搜尋結果,並讓SDK自動將其反序列化為您選擇的類型,如我們將在下一節所見。
備註
Azure 認知搜尋 .NET SDK 也支援使用 Document 類別的動態類型文件,這是一個從欄位名稱到欄位值的鍵/值對應。 在設計階段無法預知索引架構的情況下,或會不方便系結至特定模型類別的情境中,這是很有用的。 SDK 中所有處理文件的方法都有多載,這些多載可以使用 Document 類別類別進行運作,並且還有採用泛型型別參數的強型別多載。 本教學課程中的範例程式代碼中只會使用後者。
類別Document繼承自 Dictionary<string, object>。
為何您應該使用可為 Null 的數據類型
設計您自己的模型類別以對應至 Azure 認知搜尋索引時,建議您宣告實值型別的屬性,例如將 bool 和 int 設為可為 Null(例如使用 bool? 而不是 bool)。 如果您使用不可為 Null 的屬性,則必須 保證 索引中沒有任何檔包含對應欄位的 Null 值。 SDK 和 Azure 認知搜尋服務都無法協助您強制執行這項功能。
這不僅僅是假設問題:想像一個情境,您在現有索引中加入一個新欄位,而該索引的類型是Edm.Int32。 更新索引定義之後,所有文件都會有該新欄位的 Null 值(因為所有類型在 Azure 認知搜尋中都是可為 Null 的)。 如果您接著使用具有非可為 Null int 屬性的模型類別來處理該欄位,當您嘗試擷取檔時,將會收到類似的 JsonSerializationException:
Error converting value {null} to type 'System.Int32'. Path 'IntValue'.
基於這個理由,我們建議您在模型類別中使用可為 Null 的類型作為最佳做法。
使用 JSON.NET 進行自定義串行化
SDK 會使用 JSON.NET 來序列化和反序列化文件。 您可以藉由定義自己的 JsonConverter 或 IContractResolver,視需要自訂串行化和還原串行化。 如需詳細資訊,請參閱 JSON.NET 檔。 當您想要從應用程式調整現有的模型類別以搭配 Azure 認知搜尋和其他更進階的案例使用時,這非常有用。 例如,使用自訂串行化,您可以:
- 包含或排除模型類別的特定屬性,使其無法儲存為檔欄位。
- 在程式代碼中的屬性名稱與索引中的欄位名稱之間對應。
- 建立可用於將屬性對應至檔欄位的自訂屬性。
您可以在 GitHub 上的 Azure 認知搜尋 .NET SDK 單元測試中找到實作自定義串行化的範例。 這個 資料夾是不錯的起點。 它包含自定義串行化測試所使用的類別。
在索引中搜尋檔
範例應用程式中的最後一個步驟是搜尋索引中的某些檔:
private static void RunQueries(ISearchIndexClient indexClient)
{
SearchParameters parameters;
DocumentSearchResult<Hotel> results;
Console.WriteLine("Search the entire index for the term 'motel' and return only the HotelName field:\n");
parameters =
new SearchParameters()
{
Select = new[] { "HotelName" }
};
results = indexClient.Documents.Search<Hotel>("motel", parameters);
WriteDocuments(results);
Console.Write("Apply a filter to the index to find hotels with a room cheaper than $100 per night, ");
Console.WriteLine("and return the hotelId and description:\n");
parameters =
new SearchParameters()
{
Filter = "Rooms/any(r: r/BaseRate lt 100)",
Select = new[] { "HotelId", "Description" }
};
results = indexClient.Documents.Search<Hotel>("*", parameters);
WriteDocuments(results);
Console.Write("Search the entire index, order by a specific field (lastRenovationDate) ");
Console.Write("in descending order, take the top two results, and show only hotelName and ");
Console.WriteLine("lastRenovationDate:\n");
parameters =
new SearchParameters()
{
OrderBy = new[] { "LastRenovationDate desc" },
Select = new[] { "HotelName", "LastRenovationDate" },
Top = 2
};
results = indexClient.Documents.Search<Hotel>("*", parameters);
WriteDocuments(results);
Console.WriteLine("Search the entire index for the term 'hotel':\n");
parameters = new SearchParameters();
results = indexClient.Documents.Search<Hotel>("hotel", parameters);
WriteDocuments(results);
}
每次執行查詢時,這個方法都會先建立新的 SearchParameters 物件。 此物件用於指定查詢的額外選項,例如排序、篩選、分頁和分面。 在此方法中,我們會為不同的查詢設定 Filter、 Select、 OrderBy和 Top 屬性。 這裏記載了SearchParameters所有屬性。
下一個步驟是實際執行搜尋查詢。 執行搜尋將會使用 Documents.Search 方法。 針對每個查詢,我們會將搜尋文字當作字串來使用(若沒有搜尋文字,則使用 "*"),再加上稍早建立的搜尋參數。 我們也指定了 Hotel 做為 Documents.Search 的類型參數,藉此告訴 SDK 將搜尋結果中的文件還原序列化為 Hotel 類型的物件。
備註
您可以 在這裡找到搜尋查詢表達式語法的詳細資訊。
最後,在每次查詢之後,此方法會逐一遍歷搜尋結果中的所有匹配項,將每個文件輸出到控制台:
private static void WriteDocuments(DocumentSearchResult<Hotel> searchResults)
{
foreach (SearchResult<Hotel> result in searchResults.Results)
{
Console.WriteLine(result.Document);
}
Console.WriteLine();
}
讓我們依序仔細查看每個查詢。 以下是執行第一個查詢的程式代碼:
parameters =
new SearchParameters()
{
Select = new[] { "HotelName" }
};
results = indexClient.Documents.Search<Hotel>("motel", parameters);
WriteDocuments(results);
在此情況下,我們會在任何可搜尋的欄位中搜尋 「hotel」 這個字的整個索引,而我們只想要擷取參數所 Select 指定的旅館名稱。 以下是結果:
Name: Secret Point Motel
Name: Twin Dome Motel
下一個查詢更有趣一點。 我們想要尋找任何具有夜間費率低於 $100 的旅館,並只傳回旅館標識碼和描述:
parameters =
new SearchParameters()
{
Filter = "Rooms/any(r: r/BaseRate lt 100)",
Select = new[] { "HotelId", "Description" }
};
results = indexClient.Documents.Search<Hotel>("*", parameters);
WriteDocuments(results);
此查詢會使用 OData $filter 運算式 Rooms/any(r: r/BaseRate lt 100)來篩選索引中的檔。 這會使用 any 運算符,將 "BaseRate lt 100" 套用至 Rooms 集合中的每個項目。 您可以 在這裡深入瞭解 Azure 認知搜尋支援的 OData 語法。
以下是查詢的結果:
HotelId: 1
Description: The hotel is ideally located on the main commercial artery of the city in the heart of New York...
HotelId: 2
Description: The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to...
接下來,我們想要尋找最近裝修的前兩家酒店,並顯示酒店名稱和上次裝修日期。 程式碼如下:
parameters =
new SearchParameters()
{
OrderBy = new[] { "LastRenovationDate desc" },
Select = new[] { "HotelName", "LastRenovationDate" },
Top = 2
};
results = indexClient.Documents.Search<Hotel>("*", parameters);
WriteDocuments(results);
在此情況下,我們再次使用 OData 語法將 參數指定 OrderBy 為 lastRenovationDate desc。 我們也設定 Top 為 2,以確保我們只取得前兩份檔。 和之前一樣,我們設定 Select 為指定應該傳回哪些欄位。
以下是結果:
Name: Fancy Stay Last renovated on: 6/27/2010 12:00:00 AM +00:00
Name: Roach Motel Last renovated on: 4/28/1982 12:00:00 AM +00:00
最後,我們想要尋找符合 「hotel」 一字的所有旅館名稱:
parameters = new SearchParameters()
{
SearchFields = new[] { "HotelName" }
};
results = indexClient.Documents.Search<Hotel>("hotel", parameters);
WriteDocuments(results);
以下是結果,其中包含所有欄位,因為我們未指定 Select 屬性:
HotelId: 3
Name: Triple Landscape Hotel
...
此步驟會完成本教學課程,但不會在此停止。 **後續步驟提供其他資源,以深入瞭解 Azure 認知搜尋。