多層分類 view 裡的 GetAllDocumentsByKey:為什麼你的 count 悄悄就錯了

多層分類 view 裡的 GetAllDocumentsByKey:為什麼你的 count 悄悄就錯了

在一個以 Country 分類的 view 裡,GetAllDocumentsByKey("Belgien", True) 回傳 2 — Maison Dewey 與 Suprêmes délices。完全正確。單層分類加上 by-key 查詢,運作得一如預期。

現在加上第二層分類。一個 view “cc”,先以 Form 分類、再以 Country 分類:

▼ Customer ← 第一層分類 (Form)
▼ Argentinien ← 第二層分類 (Country)
Cactus Comidas para llevar (CACTU)
Océano Atlántico Ltda. (OCEAN)
Rancho grande (RANCH)
▼ Belgien
Maison Dewey (MAISD)
Suprêmes délices (SUPRD)
▼ Brasilien
Comércio Mineiro (COMMI)
…9 筆以上
▼ Dänemark
Simons bistro (SIMOB)

呼叫 GetAllDocumentsByKey("Customer", True)。你期望拿到 “Customer” 分類下的每一筆文件 — 所有國家、所有文件。答案回來:3。不是全部的數字。只有 Argentinien(第一個子分類)底下那 3 筆。

沒有錯誤。沒有警告。一個看起來合理的數字。你就這樣上線了。


重點摘要

  • 單層分類 view + by-key = 正確運作。 傳分類值、拿到該分類下所有文件。
  • 多層分類 view + 單一 key = 悄悄被截斷。 查詢在第一個子分類邊界就停了。你拿到的是一個看起來對的部分結果(不是零、不是錯誤、是一個合理的 count),但後面的子分類全部都漏掉了。
  • GetAllDocumentsByKey 文件說 key 是「由左至右比對已排序欄位」— 但它沒警告在多層分類 view 裡單一 key 只會回傳到第一個子分類的文件。
  • 繞法: 用一個平面的(非分類)查詢 view;或用不依賴 view 分類結構的 db.Search / DQL;或用 GetAllEntriesByKey 明確地走子分類。

單層分類做對了什麼

先把基線講清楚:只有一個分類欄位的 view 運作如預期。五個 pitfall 的那篇講的是正常的 by-key 機制 — key 由左至右比對已排序欄位、exactMatch 預設 False 等等。在一個只以 Country 分類的 Customers view 裡,GetAllDocumentsByKey("Belgien", True) 正確回傳 Belgien 底下的 2 筆文件。沒什麼意外。

陷阱只在 view 有分類再分類時才出現。

實測證明

View “cc” — 兩個分類欄位:

欄位角色已排序分類
Form第一層分類
Country第二層分類(巢狀在 Form 底下)
Subject顯示

程式:

Sub Initialize
On Error GoTo errH
Dim ss As New NotesSession
Dim db As NotesDatabase
Set db = ss.CurrentDatabase
Dim v As NotesView
Set v = db.GetView("cc")
Dim dc As NotesDocumentCollection
Set dc = v.GetAllDocumentsByKey("Customer", True)
MsgBox dc.Count
Exit Sub
errH:
MsgBox "get error: " + Error + " on line: " + CStr(Erl)
End Sub

結果:3 — 只有 Argentinien(第一個子分類)底下的文件。不是 “Customer” 底下橫跨所有國家的 20 幾筆。

查詢找到了第一層分類 “Customer”、下降到它的第一個子分類 “Argentinien”、回傳那 3 筆文件、然後就停了。Belgien 的 2 筆、Brasilien 的 9 筆、Dänemark 的 2 筆、以及其他每個國家的文件,全部悄悄不見了。

為什麼這是地雷

三個原因讓這個比多數 API 怪癖更會咬人:

  1. 結果看起來合理、不是零。 你拿到 count = 3 — 一個真實的數字、來自真實的文件。如果答案是 0 你會馬上調查。但 3 看起來搞不好就是對的,尤其你不知道正確的總數該是多少。

  2. 沒有錯誤、沒有警告。 方法不丟錯、不回 Nothing、不設旗標。它回傳一個合法的 NotesDocumentCollection,裝著合法的文件 — 只是不是全部。

  3. 官方文件沒描述這個行為。 它說「陣列第一個元素比對第一個已排序欄位」、警告了 \\ backslash 子分類 bug,但它沒提到在多層分類 view 裡用單一 key 會在第一個子分類就截斷。你可以把方法頁從頭讀到尾、仍然不會預見這個。

繞法

方法一:用平面的查詢 view。 做一個專用 view,你需要的欄位有排序但不分類(或只有單層分類)。一個 Form 有排序(ascending、不勾 categorize)的「lookup」view,用單一 key 就能回傳完整的結果集。

方法二:用 db.Search 或 DQL。 這些根本不走 view 索引,所以分類結構影響不了它們:

Set dc = db.Search({Form = "Customer"}, Nothing, 0)
Print dc.Count ' 所有 Form = "Customer" 的文件

方法三:用 GetAllEntriesByKey 走子分類。 如果你一定要用那個分類 view,用 GetAllEntriesByKey 搭配最上層的 key。view-entry 集合包含分類 entry 本身,讓你能走完整棵樹。但到了這步,做一個平面查詢 view 通常更簡單。

最乾淨的答案 — 也是完全避開陷阱的那個 — 是一個沒有多層分類的查詢 view。如 Stephan Wissel 的 back-to-basics 指南所強調的,分類再分類是給人類讀者展開/折疊群組用的 UI 功能;它不是拿來做程式化查詢的可靠結構。

同類別在其他語言

語言對應
LotusScriptview.GetAllDocumentsByKey(...) / view.GetDocumentByKey(...)
JavaView.getAllDocumentsByKey(...)
SSJS (XPages)同 Java

這個行為是 view 索引的性質、不是語言的 — 所以同樣的截斷在 Java 與 SSJS 也會發生。繞法也一樣:平面查詢 view、或 db.search / DQL。

參考來源

← 回到文章列表