筆者相信:好的可觀測性,能把「不確定」變成「可驗證」
Explain 讓讀者看見 matcher tree 的結構與責任分工,Trace 讓讀者看見每筆資料在每個節點的命中與否
這一篇把兩者的使用方式、閱讀心法與常見踩雷整理成一份可實戰的手冊
條件愈複雜,心智模型愈容易與實際落差(特別是巢狀 $or
、$elemMatch
、$in/$nin
對陣列欄位)
Explain 幫讀者驗證「樹長得對不對」
Trace 幫讀者驗證「資料在樹上怎麼走」
require 'mongory'
Mongory.enable_symbol_snippets!
Mongory.register(Array)
records = [
{ 'name' => 'Jack', 'age' => 18, 'tags' => [{ 'name' => 'ruby', 'priority' => 6 }] },
{ 'name' => 'Jill', 'age' => 15, 'tags' => [{ 'name' => 'rails', 'priority' => 3 }] },
{ 'name' => 'Bob', 'age' => 21, 'tags' => [{ 'name' => 'ruby', 'priority' => 8 }] }
]
q = records.mongory
.where(:age.gte => 18)
.any_of({ :name.regex => /^J/ }, { :tags.elem_match => { :name => 'ruby' } })
q.explain
輸出:
閱讀要點:
And/Or 節點之下是子 matcher 清單,順序代表執行優先度,足以協助閱讀語意
Field 只負責取值與轉交,真正的比較在其子節點(Eq/Gte/Regex/...)
單子條件被解包時,Explain 會直接顯示被解包後的節點(層級更淺)
小提醒:explain
前其實會先觸發一次 match?(@records.first)
以促成建樹或初始化
或是觸發 Array 欄位的 elemMatch 轉包
若資料集合為空,輸出可能較簡略,建議準備一筆樣本資料
records = [
{ 'name' => 'Ann', 'age' => 19 },
{ 'name' => 'Ben', 'age' => 17 }
]
q = records.mongory.where(:age.gte => 18)
q.trace { gets } # 執行一次,使匹配過程被紀錄,並在每筆紀錄間 gets 暫停,直到你按 enter
輸出:
閱讀要點:
每個節點都會標示 Matched/Dismatch 以及目前觀察到的值
先觀察失敗的最底層 matcher(例如 Gte),再往上看 Field/And,能快速定位條件不符的原因
Trace 僅用於除錯,請勿在大量資料或生產環境長時間開啟
條件對不對?先 explain
看樹結構
資料為什麼不符合?再 trace
看每層節點的命中與否
有了這兩個工具,讀者能以「先結構、後動態」的方式縮短問題定位時間
鍵名(symbol/string/dot notation)不一致 → 使用 KeyConverter 統一
陣列欄位單值條件未命中 → 留意自動包裝 $elemMatch
的語意
$in/$nin
對陣列欄位是「交集」而非 include?
→ 請以 explain/trace 驗證期望
資料集為空時 explain 輸出過於簡略 → 先準備樣本資料或用 limit(1)
取樣
Trace 輸出過長 → 以小資料集重現,或在測試層級驗證
records = [
{ 'name' => 'Ann', 'age' => 19, 'tags' => [{ 'name' => 'ruby', 'priority' => 6 }] },
{ 'name' => 'Ben', 'age' => 17, 'tags' => [{ 'name' => 'rails', 'priority' => 3 }] },
{ 'name' => 'Cody', 'age' => 22, 'tags' => [{ 'name' => 'ruby', 'priority' => 8 }] }
]
q = records.mongory
.where(:age.gte => 18)
.where(:tags.elem_match => { :name => 'ruby', :priority.gt => 5 })
q.explain
q.trace { |_| }
筆者在實務上會以 explain
驗證語意、用 trace
驗證資料流,兩者搭配能在迭代條件與轉換器(Converters)時保持信心
下一篇開始進入 Week 2:Day 8〈為什麼 Ruby 不夠快〉,談 benchmark 與方法學,以及開始 C 語言的世界!