在 Azure 搜尋服務中模型化複雜資料類型

用來填入 Azure AI 搜尋服務索引的外部資料集可以有許多形狀。 有時候,其會包括階層式或巢狀子結構。 範例可能會包括單一客戶的多個地址、單一 SKU 的多種色彩和大小、單一書籍的多位作者等。 就模型化而論,您可能會看到這些稱為「複雜」、「複合」、「合成」或「彙總」資料類型的結構。 Azure AI 搜尋服務用於此概念的字詞是「複雜類型」。 在 Azure AI 搜尋服務中,會使用「複雜欄位」來模型化複雜類型。 複雜欄位是一個欄位,其中所包含的子系 (子欄位) 可以是任何資料類型,包括其他複雜類型。 其運作方式與程式設計語言中的結構化資料類型類似。

根據資料類型,複雜欄位會代表文件中的單一物件或物件陣列。 Edm.ComplexType 類型的欄位代表單一物件,而 Collection(Edm.ComplexType) 類型的欄位代表物件陣列。

Azure AI 搜尋服務原本就支援複雜類型和集合。 這些類型幾乎可讓您模型化 Azure AI 搜尋服務索引中的任何 JSON 結構。 在舊版 Azure AI 搜尋服務 API 中,只能匯入扁平化資料列集。 在最新版本中,您的索引現在可以更緊密地對應至來源資料。 換句話說,如果來源資料具有複雜類型,則索引也可以有複雜類型。

若要開始使用,建議使用 Hotels 資料集,而您可以在 Azure 入口網站的 [匯入資料] 精靈中載入此資料集。 此精靈會偵測來源中的複雜類型,並根據偵測到的結構來建議索引結構描述。

注意

api-version=2019-05-06 開始,已正式推出複雜類型支援。

如果您的搜尋解決方案是根據集合中扁平化資料集的先前因應措施所建置,則您應該變更索引,以包括最新 API 版本中所支援的複雜類型。 如需升級 API 版本的詳細資訊,請參閱升級至最新的 REST API 版本升級至最新的 .NET SDK 版本

複雜結構範例

下列 JSON 文件是由簡單欄位和複雜欄位所組成。 AddressRooms 這類複雜欄位具有子欄位。 Address 具有這些子欄位的單一值集,因為其為文件中的單一物件。 相反地,Rooms 具有其子欄位的多個值集,而集合中的每個物件都有一個。

{
  "HotelId": "1",
  "HotelName": "Secret Point Motel",
  "Description": "Ideally located on the main commercial artery of the city in the heart of New York.",
  "Tags": ["Free wifi", "on-site parking", "indoor pool", "continental breakfast"],
  "Address": {
    "StreetAddress": "677 5th Ave",
    "City": "New York",
    "StateProvince": "NY"
  },
  "Rooms": [
    {
      "Description": "Budget Room, 1 Queen Bed (Cityside)",
      "RoomNumber": 1105,
      "BaseRate": 96.99,
    },
    {
      "Description": "Deluxe Room, 2 Double Beds (City View)",
      "Type": "Deluxe Room",
      "BaseRate": 150.99,
    }
    . . .
  ]
}

編製複雜類型索引

在編製索引期間,您最多在單一文件的所有複雜集合中可以有 3000 個元素。 複雜集合的元素是該集合的成員,因此在 Rooms (Hotel 範例中的唯一複雜集合) 的情況下,每個房間都是一個元素。 在上面的範例中,如果 "Secret Point Motel" 有 500 間房間,則飯店文件會有 500 個房間元素。 針對巢狀複雜集合,除了外部 (父代) 元素之外,也會計算每個巢狀元素。

此限制僅適用於複雜集合,並不適用於複雜類型 (例如 Address) 或字串集合 (例如 Tags)。

建立複雜欄位

與任何索引定義一樣,您可以使用入口網站、REST API.NET SDK 來建立包含複雜類型的結構描述。

其他 Azure SDK 提供 PythonJavaJavaScript 中的範例。

  1. 登入 Azure 入口網站

  2. 在搜尋服務 [概觀] 頁面上,選取 [索引] 索引標籤。

  3. 開啟現有的索引,或建立新的索引。

  4. 選取 [欄位] 索引標籤,然後選取 [新增欄位]。 新增空白欄位。 如果您要使用現有的欄位集合,則請向下捲動以設定欄位。

  5. 指定欄位的名稱,並將類型設定為 Edm.ComplexTypeCollection(Edm.ComplexType)

  6. 選取最右邊的省略符號,並選取 [新增欄位] 或 [新增子欄位],然後指派屬性。

更新複雜欄位

一般套用至欄位的所有重新編製規則索引都仍然適用於複雜欄位。 在這裡重述一些主要規則,將欄位新增至複雜類型並不需要重建索引,但大部分的修改需要。

定義的結構化更新

您隨時可以將新的子欄位新增至複雜欄位,而不需要重建索引。 例如,允許將 "ZipCode" 新增至 Address 或將 "Amenities" 新增至 Rooms,就像將最上層欄位新增至索引一樣。 除非您更新資料來明確填入新欄位,否則這些欄位的現有文件為 Null 值。

請注意,在複雜類型內,每個子欄位都有一種類型,而且可有多個屬性,就像最上層欄位一樣

資料更新

使用 upload 動作來更新索引中的現有文件,其運作方式與複雜和簡單欄位相同:會取代所有欄位。 不過,所有欄位的 merge (套用至現有文件時為 mergeOrUpload) 的運作方式都不同。 具體來說,merge 不支援合併集合內的元素。 基本類型和複雜集合的集合具有這項限制。 若要更新集合,您需要擷取完整集合值,並進行變更,然後在索引 API 要求中包括新的集合。

搜尋複雜欄位

自由格式搜尋運算式預期會與複雜類型搭配運作。 如果文件中的任何位置有任何可搜尋欄位或子欄位相符,則文件本身是相符項目。

如果您有多個字詞和運算子,而某些字詞已指定欄位名稱,則查詢會更為細微,如同使用 Lucene 語法一樣。 例如,此查詢會針對 Address 欄位的兩個子欄位,嘗試比對兩個字詞:"Portland" 和 "OR":

search=Address/City:Portland AND Address/State:OR

與篩選不同,全文檢索搜尋的這類查詢「無關」。 在篩選中,複雜集合子欄位的查詢會使用 anyall 中的範圍變數而相互關聯。 上面的 Lucene 查詢會傳回文件,其中包含 "Portland, Maine" 和 "Portland, Oregon",以及 Oregon 中的其他城市。 發生此情況的原因是每個子句都會套用至整個文件中其欄位的所有值,因此沒有「目前子文件」的概念。 如需此作業的詳細資訊,請參閱了解 Azure AI 搜尋服務中的 OData 集合篩選

選取複雜欄位

$select 參數用來選擇搜尋結果中所傳回的欄位。 若要使用此參數來選取複雜欄位的特定子欄位,請包括以斜線 (/) 區隔的父欄位和子欄位。

$select=HotelName, Address/City, Rooms/BaseRate

如果您想要搜尋結果中有欄位,則欄位在索引中必須標示為 [可擷取]。 只有標示為 [可擷取] 的欄位才能用於 $select 陳述式中。

篩選、Facet 化和排序複雜欄位

用於篩選和欄位式搜尋的相同 OData 路徑語法也可以用於 Facet 化、排序和選取搜尋要求中的欄位。 針對複雜類型,會套用規則,以治理哪些子欄位可以標示為可排序或可 Facet 化。 如需這些規則的詳細資訊,請參閱建立索引 API 參考

Facet 化子欄位

除非任何子欄位的類型為 Edm.GeographyPointCollection(Edm.GeographyPoint),否則可以將其標示為可 Facet 化。

Facet 結果中所傳回的文件計數是針對父文件 (飯店) 計算而來,而不是複雜集合中的子文件 (房間)。 例如,假設飯店有 20 間類型為「套房」的房間。 如果有此 Facet 參數 facet=Rooms/Type,則 Facet 計數會是一間飯店,而不是 20 間房間。

排序複雜欄位

排序作業會套用至文件 (Hotels),而不是子文件 (Rooms)。 當您有複雜類型集合 (例如 Rooms) 時,請務必瞭解您根本無法根據 Rooms 進行排序。 事實上,您無法根據任何集合進行排序。

欄位的每份文件都有單一值時,排序作業會運作,無論欄位是簡單欄位,還是複雜類型中的子欄位。 例如,Address/City 允許可進行排序,因為每間飯店只會有一個地址,因此 $orderby=Address/City 會依縣/市來排序飯店。

根據複雜欄位進行篩選

您可以在篩選運算式中參照複雜欄位的子欄位。 僅使用用於 Facet 化、排序和選取欄位的相同 OData 路徑語法。 例如,下列篩選條件會傳回加拿大的所有飯店:

$filter=Address/Country eq 'Canada'

若要根據複雜集合欄位進行篩選,您可以搭配使用 Lambda 運算式anyall 運算子。 在此情況下,Lambda 運算式的 [範圍變數] 是具有子欄位的物件。 您可以使用標準 OData 路徑語法來參照這些子欄位。 例如,下列篩選條件會傳回所有至少有一間豪華房和所有非吸煙房的飯店:

$filter=Rooms/any(room: room/Type eq 'Deluxe Room') and Rooms/all(room: not room/SmokingAllowed)

與最上層簡單欄位一樣,如果索引定義中複雜欄位的簡單子欄位將 filterable 屬性設定為 true 時,則只能包括在篩選條件中。 如需詳細資訊,請參閱建立索引 API 參考

下一步

在 [匯入資料] 精靈中嘗試 Hotels 資料集。 您需要讀我檔案中所提供的 Azure Cosmos DB 連線資訊,才能存取資料。

有了該資訊之後,精靈中的第一個步驟就是建立新的 Azure Cosmos DB 資料來源。 進一步來說,在精靈中,當您進入目標索引頁面時,會看到具有複雜類型的索引。 建立並載入此索引,然後執行查詢以了解新的結構。