Skip to content

add env support to expression/accumulator join nodes #489

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/clojure/clara/rules/compiler.clj
Original file line number Diff line number Diff line change
Expand Up @@ -1632,6 +1632,7 @@
;; Create an accumulator structure for use when examining the node or the tokens
;; it produces.
{:accumulator (:accumulator beta-node)
:env (:env beta-node)
;; Include the original filter expressions in the constraints for inspection tooling.
:from (update-in condition [:constraints]
into (-> beta-node :join-filter-expressions :constraints))}
Expand Down
32 changes: 19 additions & 13 deletions src/main/clojure/clara/rules/engine.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@

(defn- join-node-matches
[node join-filter-fn token fact fact-bindings env]
(let [beta-bindings (try (join-filter-fn token fact fact-bindings {})
(let [beta-bindings (try (join-filter-fn token fact fact-bindings env)
(catch #?(:clj Exception :cljs :default) e
(throw-condition-exception {:cause e
:node node
Expand All @@ -695,7 +695,7 @@
token tokens
:let [fact (:fact element)
fact-binding (:bindings element)
beta-bindings (join-node-matches node join-filter-fn token fact fact-binding {})]
beta-bindings (join-node-matches node join-filter-fn token fact fact-binding (:env condition))]
:when beta-bindings]
(->Token (conj (:matches token) [fact id])
(conj fact-binding (:bindings token) beta-bindings)))))
Expand All @@ -711,7 +711,7 @@
element (mem/get-elements memory node join-bindings)
:let [fact (:fact element)
fact-bindings (:bindings element)
beta-bindings (join-node-matches node join-filter-fn token fact fact-bindings {})]
beta-bindings (join-node-matches node join-filter-fn token fact fact-bindings (:env condition))]
:when beta-bindings]
(->Token (conj (:matches token) [fact id])
(conj fact-bindings (:bindings token) beta-bindings)))))
Expand All @@ -731,7 +731,7 @@
children
(platform/eager-for [token (mem/get-tokens memory node join-bindings)
{:keys [fact bindings] :as element} elements
:let [beta-bindings (join-node-matches node join-filter-fn token fact bindings {})]
:let [beta-bindings (join-node-matches node join-filter-fn token fact bindings (:env condition))]
:when beta-bindings]
(->Token (conj (:matches token) [fact id])
(conj (:bindings token) bindings beta-bindings)))))
Expand All @@ -745,7 +745,7 @@
children
(platform/eager-for [{:keys [fact bindings] :as element} (mem/remove-elements! memory node join-bindings elements)
token (mem/get-tokens memory node join-bindings)
:let [beta-bindings (join-node-matches node join-filter-fn token fact bindings {})]
:let [beta-bindings (join-node-matches node join-filter-fn token fact bindings (:env condition))]
:when beta-bindings]
(->Token (conj (:matches token) [fact id])
(conj (:bindings token) bindings beta-bindings)))))
Expand Down Expand Up @@ -1378,8 +1378,8 @@

(defn- filter-accum-facts
"Run a filter on elements against a given token for constraints that are not simple hash joins."
[node join-filter-fn token candidate-facts bindings]
(filter #(join-node-matches node join-filter-fn token % bindings {}) candidate-facts))
[node join-filter-fn token candidate-facts bindings condition]
(filter #(join-node-matches node join-filter-fn token % bindings (:env condition)) candidate-facts))

;; A specialization of the AccumulateNode that supports additional tests
;; that have to occur on the beta side of the network. The key difference between this and the simple
Expand All @@ -1406,7 +1406,8 @@
[fact-bindings candidate-facts] grouped-candidate-facts

;; Filter to items that match the incoming token, then apply the accumulator.
:let [filtered-facts (filter-accum-facts node join-filter-fn token candidate-facts fact-bindings)]
:let [filtered-facts (filter-accum-facts node join-filter-fn token candidate-facts
fact-bindings accum-condition)]

:when (or (seq filtered-facts)
;; Even if there no filtered facts, if there are no new bindings we may
Expand Down Expand Up @@ -1463,7 +1464,8 @@
(doseq [token tokens
[fact-bindings candidate-facts] grouped-candidate-facts

:let [filtered-facts (filter-accum-facts node join-filter-fn token candidate-facts fact-bindings)]
:let [filtered-facts (filter-accum-facts node join-filter-fn token candidate-facts
fact-bindings accum-condition)]

:when (or (seq filtered-facts)
;; Even if there no filtered facts, if there are no new bindings an initial value
Expand Down Expand Up @@ -1531,13 +1533,15 @@

(doseq [token matched-tokens

:let [new-filtered-facts (filter-accum-facts node join-filter-fn token candidates bindings)]
:let [new-filtered-facts (filter-accum-facts node join-filter-fn token candidates
bindings accum-condition)]

;; If no new elements matched the token, we don't need to do anything for this token
;; since the final result is guaranteed to be the same.
:when (seq new-filtered-facts)

:let [previous-filtered-facts (filter-accum-facts node join-filter-fn token previous-candidates bindings)
:let [previous-filtered-facts (filter-accum-facts node join-filter-fn token previous-candidates
bindings accum-condition)

previous-accum-result-init (cond
(seq previous-filtered-facts)
Expand Down Expand Up @@ -1633,9 +1637,11 @@
(doseq [;; Get all of the previously matched tokens so we can retract and re-send them.
token matched-tokens

:let [previous-facts (filter-accum-facts node join-filter-fn token previous-candidates bindings)
:let [previous-facts (filter-accum-facts node join-filter-fn token previous-candidates
bindings accum-condition)

new-facts (filter-accum-facts node join-filter-fn token new-candidates bindings)]
new-facts (filter-accum-facts node join-filter-fn token new-candidates
bindings accum-condition)]

;; The previous matching elements are a superset of the matching elements after retraction.
;; Therefore, if the counts before and after are equal nothing retracted actually matched
Expand Down
21 changes: 21 additions & 0 deletions src/test/clojure/clara/test_rules.clj
Original file line number Diff line number Diff line change
Expand Up @@ -1348,6 +1348,27 @@

(is (= 42 @rule-output-env))))

(deftest test-destructured-join-node-env-binding
(let [rule-output-env (atom #{})
rule {:name "clara.test-destructured-binding/test-destructured-test-env-binding"
:env {:rule-output rule-output-env} ; Rule environment so we can check its output.
:lhs '[{:args [[e a v]]
:type :foo
:constraints [(= e ?entity) (= v ?foo-value) (swap! rule-output conj ?foo-value)]}
{:accumulator (clara.rules.accumulators/all),
:from
{:args [[e a v]]
:type :bar
:constraints [(= e ?entity) (= v ?bar-value) (swap! rule-output conj ?bar-value)]},
:result-binding :?resp}]
:rhs '(inc 1)}]
(-> (mk-session [rule] :fact-type-fn second)
(insert [1 :foo 42])
(insert [1 :bar 43])
(fire-rules))

(is (= #{42 43} @rule-output-env))))

(def locals-shadowing-tester
"Used to demonstrate local shadowing works in `test-explicit-rhs-map-can-use-ns-name-for-unqualified-symbols` below."
:bad)
Expand Down