Java
JavaScript
Clojure
ClojureScript
clara-rules

前回

clara-rulesは異るエンティティ間のジョイン操作に使えるという知見を講演の動画から得たので、自分で簡単な例を実装してみた。

問題

  • TypeA
    • 属性attr-aとjoin-keyを持つ
  • TypeB
    • 属性attr-bとjoin-keyを持つ
  • TypeC
    • 共通のjoin-keyを持つTypeA.attr-aとTypeB.attr-bを属性に持つ

コード

(ns  clara-rules.join
  (:require [clara.rules :refer [defrule fire-rules insert insert! mk-session]]))

(defrecord TypeA [attr-a join-key])

(defrecord TypeB [attr-b join-key])

(defrecord TypeC [attr-a attr-b join-key])

(defrule Join
  [TypeA (= ?jk-a join-key) (= ?a attr-a)]
  [TypeB (= join-key ?jk-a) (= ?b attr-b)]
  =>
  (insert! (->TypeC ?a ?b ?jk-a)))

(defrule Print
  [?result <- TypeC];; Will always match
  =>
  (println ?result))

(-> (mk-session)
    (insert (->TypeA "a1" :foo)
            (->TypeA "a2" :bar)
            (->TypeA "a3" :baz)
            (->TypeB "b1" :foo)
            (->TypeB "b2" :bar)
            (->TypeB "b3" :baz)
            (->TypeB "b4" :qux))
    (fire-rules))
;;#clara_rules.join.TypeC{:attr-a a1, :attr-b b1, :join-key :foo}
;;#clara_rules.join.TypeC{:attr-a a2, :attr-b b2, :join-key :bar}
;;#clara_rules.join.TypeC{:attr-a a3, :attr-b b3, :join-key :baz}
  • 狙いの通り共通の:join-keyを持つTypeAとTypeBのペアに対してTypeCがプリントされている。やったね!
  • (->TypeB "b4" :qux) は同じ:join-key :quxを持つTypeAのファクトが無いため結果に表れない。なるほど。

defrule左辺<-の説明

ファクトの属性ではなく、ファクトそのものを変数に束縛する。
[?result <- TypeC]を例にとると、マッチしたTypeCのインスタンス全体が?resultに束縛される

感想

  • インメモリのジョインのロジックを、SQLとは異るアプローチで宣言的に書くことができる
  • 例えばDB上のジョイン操作が水平スケーラビリティのボトルネックになっている場合に、ジョイン操作をアプリケーションの方でインメモリで行うように変更するのに使えるかもしれない。
    • あるいはデータはあるけどそれクエリをかけることができない環境とかで役に立つかもしれない

続編