clara-rulesのaccumulator機能を使うとsqlで言うところの集約関数のような事が実現できるので今回はそれを試してみることにする。
概要
-
clara.rules.accumulators
のネームスペースにあるものを利用する - デフォルトでmin/max/sum等が提供されている
- 自分で任意のaccumulatorを作成する事もできる(accum関数)
-
defrule
の左辺、defquery
で使用することができる
文法
[<束縛する変数名> <- <accumulator> :from [<ファクト>+<constraint>]]
動かしてみる
以下のようなコードを試してみた。
(ns clara-rules.accumulator
(:require [clara.rules :refer [fire-rules insert mk-session query defquery]]
[clara.rules.accumulators :as acc]))
(defrecord SomeAmount [id amt])
(defquery test-query
[]
[?largest-amt <- (acc/max :amt) :from [SomeAmount]]
[?total <- (acc/sum :amt) :from [SomeAmount]]
[?grouped <- (acc/grouping-by :id) :from [SomeAmount]]
;;Total by groups
[?grouped-and-summed
<- (acc/grouping-by :id (fn [m]
;;Accepts map key=field for grouping
;;value=seq of maps of records that were grouped
(map (fn [[k seq-of-records]]
{k (reduce + (map :amt seq-of-records))})
m)))
:from [SomeAmount]])
(-> (mk-session)
(insert (->SomeAmount :a 4)
(->SomeAmount :a 8)
(->SomeAmount :b 32)
(->SomeAmount :b 21)
(->SomeAmount :c 1)
(->SomeAmount :c 9))
(fire-rules)
(query test-query))
;; => ({:?largest-amt 32,
;; :?total 75,
;; :?grouped {:a [#clara_rules.accumulator.SomeAmount{:id :a, :amt 4}
;; #clara_rules.accumulator.SomeAmount{:id :a, :amt 8}],
;; :b [#clara_rules.accumulator.SomeAmount{:id :b, :amt 32}
;; #clara_rules.accumulator.SomeAmount{:id :b, :amt 21}],
;; :c [#clara_rules.accumulator.SomeAmount{:id :c, :amt 1}
;; #clara_rules.accumulator.SomeAmount{:id :c, :amt 9}]},
;; :?grouped-and-summed ({:a 12} {:b 53} {:c 10})})