iT邦幫忙

2024 iThome 鐵人賽

DAY 16
0
Software Development

Datomic,內建事件溯源的資料庫。系列 第 16

先從 Datalog 談起 -- part 11 (rules)

  • 分享至 

  • xImage
  •  

這篇與 Day11 類似,又是 Datalog 特有的功能。我們要來談論 Datalog 特有抽象機制: 規則 (rules)。

什麼是規則呢?像下方的 Datalog 查詢,它要找出『魔鬼終結者 (Terminator) 這部電影的演員名字』

[:find ?name
 :where
 [?p :person/name ?name]
 [?m :movie/cast ?p]
 [?m :movie/title "The Terminator"]]

查詢之中的一小部分,用來『從電影名查演員名』的,總是反復地在不同的查詢使用,是否可以有什麼抽象機制可以讓我們不需要一次又一次地重寫下方這段程式碼呢?

[?p :person/name ?name]
[?m :movie/cast ?p]
[?m :movie/title ?title]

有的,這個抽象機制叫做規則。先看一個範例吧:

;; 我們定義的規則
[(actor-movie ?name ?title)
 [?p :person/name ?name]
 [?m :movie/cast ?p]
 [?m :movie/title ?title]]
 
;; 改寫後的 Datalog 查詢
 [:find ?name
  :in $ %
  :where (actor-movie ?name "The Terminator")]

既然稱之為抽象化機制了,其實規則就很像一般程式語言裡的函數。函數可以看成三個部分:「函數名稱、輸入引數、函數實作」。

(defn add-2     ;; add-2 是『函數名稱』
  [x]        ;; [x] 是輸入引數
  (+ x 2))     ;;  (+ x 2) 是函數實作

Datalog 的規則也差不多,它的組成是:[規則名稱 規則變數 對應子句]actor-movie規則名稱?name?title規則變數,最後是對應子句。其中,規則名稱與規則變數又可以合稱為規則頭部 (rule head)

如何使用 Datalog 規則

參考下方的程式碼:

  1. 首先,我們定義一個叫 rules 的外部變數,它是一組規則的集合。
  2. 在 Datalog 查詢裡,我們引用 rules 這個外部變數,並且在 :in 子句使用 % 去對應外部傳入的規則。
  3. :where 子句裡,呼叫規則名稱,即 (actor-movie ?name ?movie-title)
(def rules
  '[[(actor-movie ?name ?title)
    [?p :person/name ?name]
    [?m :movie/cast ?p]
    [?m :movie/title ?title]]]])

(d/q '[:find ?name
       :in $ % ?movie-title
       :where (actor-movie ?name ?movie-title)]
     db rules "The Terminator")

規則的特性

讀者可能會問,「咦,這個規則的部分,SQL 查詢有對應的語法嗎?」

我認為沒有。儘管我們可以利用類似 dbt/jinja 之類的工具,把 SQL 查詢之中區段的 join 語句包裝起來,並且在不同的 SQL 查詢裡重複使用。但是,如果讀者仔細去了解 Datalog 規則的種種特性之後,想必會認同我的結論:『沒有』。

這個結論其實不太意外,畢竟,規則算是 Datalog 語言的宗廟之美、百官之富了。

這邊讓我們來看看規則的特性吧:

邏輯編程

[(actor-movie ?name ?title)
 [?p :person/name ?name]
 [?m :movie/cast ?p]
 [?m :movie/title ?title]]

由於 Datalog 語言是邏輯編程 (logic programming),所以它支援不同方向的運算,即可以正的方向算過去,也可以逆的方向算回來。也就是說,每一個規則變數既可以用來當輸入變數,也可以用來當輸出變數

這到底是什麼意思呢?

actor-movie 這個規則來舉例,我們有以下幾種用法:

  1. 綁定 ?title 的值,透過電影名找演員名。
  2. 綁定 ?name 的值,透過演員名找電影名。
  3. 上述兩個變數都綁定,於是這條規則就會去查詢資料庫是否有這個組合。
  4. 上述兩個變數都不要綁定,於是這條規則就會去比對所有可能的組合,並且傳回。

規則頭部可複用

規則也可以用作編寫邏輯或 (or) 查詢的另一種工具,因為相同的規則頭部可以使用多次。

[[(associated-with ?person ?movie)
  [?movie :movie/cast ?person]]
 [(associated-with ?person ?movie)
  [?movie :movie/director ?person]]]


當使用 associated-with? 這個規則時,它既會去比對演員與電影、也會去比對導演與電影,所以下方的查詢,它可以找出終極戰士:掠奪者 (Predator) 這部電影的導演與演員。

[:find ?name
 :in $ %
 :where
 [?m :movie/title "Predator"]
 (associated-with ?p ?m)
 [?p :person/name ?name]]

遞迴呼叫

規則有對應子句,而這些對應子句可以是任何一種子句。在 Day 13 時,我們已經有提到過,Datalog 有五種子句:

  1. not 子句
  2. not-join 子句
  3. or 子句
  4. or-join 子句
  5. 表達式子句 (expression clause),包含:資料模式、斷言表達式、函數表達式、規則表達式。

這邊就產生了一個有趣的問題:「如果某個規則 A 的對應子句部分,我們不僅使用規則來寫這些對應子句,而且還在子句裡呼叫了 A 規則的自身呢?」

上述的寫法是可行的,而且如果我們這樣子寫,就等於寫出了遞迴的規則。沒錯,規則讓 Datalog 查詢可以遞迴。換言之,即使樹走訪、圖走訪之類的查詢,對於 Datalog 來說,一樣是小菜一碟。(註1)

練習題

註:

  1. 現在的 SQL 已經有定義了 Recursive CTE 的語法,要做樹走訪、圖走訪也算是可以寫得出來。然而,還是沒有 Datalog 的語法直觀易懂。

參考資料

  1. Datomic 官方文件的規則

其它資源

  1. 歡迎訂閱 PruningSuccess 電子報,主要談論軟體開發、資料處理、資料分析等議題。
  2. 歡迎加入 Clojure 社群

上一篇
先從 Datalog 談起 -- part 10 (find specs)
下一篇
先從 Datalog 談起 -- part 12 (cross database join)
系列文
Datomic,內建事件溯源的資料庫。25
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言