Fork me on GitHub
#datomic
<
2021-06-20
>
joshkh15:06:41

the other day i asked about how to audit which query rules are satisfied and favila had a nice suggestion to use ground to return some known value. that works when my "top level" rule returns a grounded value, however i'd like to also audit nested rules as well. any idea how i can aggregate some grounded values from each rule? here is a non-working example that returns an empty result because i think the bound value of ?rule in the parent rule fails to unify on the different bound values in the nested rules.

(let [rules '[[(is-blue ?item ?rule)
               [(ground :is-blue) ?rule]
               [?item :item/color "blue"]]

              [(is-in-stock ?item ?rule)
               [(ground :is-in-stock) ?rule]
               [?item :item/inStock? true]]

              [(blue-items-in-stock ?item ?rule)
               [(ground :blue-items-in-stock) ?rule]
               (is-blue ?item ?rule)
               (is-in-stock ?item ?rule)]]]

  (d/q '{:find  [?item (distinct ?rule)]
         :in    [$ %]
         :where [(blue-items-in-stock ?item ?rule)]}
       (d/db conn) rules))
=> []
ideally i would end up with something like this:
=> [[92358976734084 #{:blue-items-in-stock :is-blue :is-in-stock}]]

refset16:06:39

> [?item :item/inStock? true ?rule] is that 4th element intended?

joshkh16:06:19

oops, that was just a typo in the example. thanks for pointing it out.

👌 3
Joe Lane18:06:48

@joshkh it's your lucky day 🙂 tl;dr, use clojure.core/tap> and add however much tracing you want, whether it's just a single value or a map that you construct in your rule in order to capture the inputs. The below snippet should be added to the siderail (with whatever filename you want) of https://github.com/Datomic/ion-starter .

;; This assumes you're using dev-local, and have dev-local as a dependency.

;; Edit resources/datomic/ion/starter/config.edn to match your system
(require
 '[clojure.data.json :as json]
 '[clojure.edn :as edn]
 '[ :as io]
 '[clojure.pprint :as pp]
 '[datomic.client.api :as d]
 '[datomic.dev-local :as dl]
 '[datomic.ion.starter :as starter]
 '[datomic.ion.starter.attributes :as attrs]
 '[datomic.ion.starter.edn :as s-edn]
 '[datomic.ion.starter.lambdas :as lambdas]
 '[datomic.ion.starter.http :as http]
 '[datomic.ion.starter.inventory :as inventory]
 '[datomic.ion.starter.utils :as utils])

(if-let [r (io/resource "datomic/ion/starter/config.edn")]
  (dl/divert-system (edn/read-string (slurp r)))
  (throw (RuntimeException. "You need to add a resource datomic/ion/starter/config.edn with your connection config")))

;; test that config works
(def client (starter/get-client))

;; create database and load sample data:
(starter/ensure-sample-dataset)

(def conn (starter/get-connection))
@(def db (d/db conn))


;; Does tap work in queries?

(def rules
  '[[(trace> [?tracer])
     [(java.util.Date.) ?nt]
     [(assoc ?tracer :at ?nt) ?t]
     [(tap> ?t) _]]
    [(by-type [?type] ?e)
     ;pre
     [(hash-map :phase :pre :rule 'by-type :type ?type :e ?e) ?pre]
     (trace> ?pre)
     ;rule
     [?e :inv/type ?type]
     ;post
     [(hash-map :phase :post :rule 'by-type :type ?type :e ?e) ?post]
     (trace> ?post)]
    [(by-size [?size] ?e)
     ;pre
     [(hash-map :phase :pre :rule 'by-size :size ?size :e ?e) ?pre]
     (trace> ?pre)
     ;rule
     [?e :inv/size ?size]
     ;post
     [(hash-map :phase :post :rule 'by-size :size ?size :e ?e) ?post]
     (trace> ?post)]
    [(by-type-and-size [?type ?size] ?e)
     ;pre
     [(hash-map :phase :pre :rule 'by-type-and-size :type ?type :size ?size) ?pre]
     (trace> ?pre)
     ;rule
     (by-type ?type ?e)
     (by-size ?size ?e)
     ; post
     [(hash-map :phase :post :rule 'by-type-and-size :type ?type :size ?size :e ?e) ?post]
     (trace> ?post)
     ]])

(defn get-items-by-type-and-size
  "Returns pull maps describing all items matching type"
  [db type size pull-expr]
  (d/q '[:find (pull ?e pull-expr)
         :in $ % ?type ?size pull-expr
         :where
         (by-type-and-size ?type ?size ?e)]
       db rules type size pull-expr))

(get-items-by-type-and-size db :shirt :small '[:inv/sku :inv/color :inv/size])
Attached is a screenshot showing the output in REBL after executing get-items-by-type-and-size then browsing the tapped values as a collection of maps.

🆒 12
joshkh17:06:28

well this is exactly what i was looking for. thank you @U0CJ19XAM!

Joe Lane17:06:58

NP, I just wish I would have thought of it sooner 😂 . Definitely could have used this over the years.

Joe Lane18:06:48
replied to a thread:the other day i asked about how to audit which query rules are satisfied and favila had a nice suggestion to use `ground` to return some known value. that works when my "top level" rule returns a grounded value, however i'd like to also audit nested rules as well. any idea how i can aggregate some grounded values from each rule? here is a non-working example that returns an empty result because i think the bound value of `?rule` in the parent rule fails to unify on the different bound values in the nested rules. (let [rules '[[(is-blue ?item ?rule) [(ground :is-blue) ?rule] [?item :item/color "blue"]] [(is-in-stock ?item ?rule) [(ground :is-in-stock) ?rule] [?item :item/inStock? true]] [(blue-items-in-stock ?item ?rule) [(ground :blue-items-in-stock) ?rule] (is-blue ?item ?rule) (is-in-stock ?item ?rule)]]] (d/q '{:find [?item (distinct ?rule)] :in [$ %] :where [(blue-items-in-stock ?item ?rule)]} (d/db conn) rules)) =&gt; [] ideally i would end up with something like this: =&gt; [[92358976734084 #{:blue-items-in-stock :is-blue :is-in-stock}]]

@joshkh it's your lucky day 🙂 tl;dr, use clojure.core/tap> and add however much tracing you want, whether it's just a single value or a map that you construct in your rule in order to capture the inputs. The below snippet should be added to the siderail (with whatever filename you want) of https://github.com/Datomic/ion-starter .

;; This assumes you're using dev-local, and have dev-local as a dependency.

;; Edit resources/datomic/ion/starter/config.edn to match your system
(require
 '[clojure.data.json :as json]
 '[clojure.edn :as edn]
 '[ :as io]
 '[clojure.pprint :as pp]
 '[datomic.client.api :as d]
 '[datomic.dev-local :as dl]
 '[datomic.ion.starter :as starter]
 '[datomic.ion.starter.attributes :as attrs]
 '[datomic.ion.starter.edn :as s-edn]
 '[datomic.ion.starter.lambdas :as lambdas]
 '[datomic.ion.starter.http :as http]
 '[datomic.ion.starter.inventory :as inventory]
 '[datomic.ion.starter.utils :as utils])

(if-let [r (io/resource "datomic/ion/starter/config.edn")]
  (dl/divert-system (edn/read-string (slurp r)))
  (throw (RuntimeException. "You need to add a resource datomic/ion/starter/config.edn with your connection config")))

;; test that config works
(def client (starter/get-client))

;; create database and load sample data:
(starter/ensure-sample-dataset)

(def conn (starter/get-connection))
@(def db (d/db conn))


;; Does tap work in queries?

(def rules
  '[[(trace> [?tracer])
     [(java.util.Date.) ?nt]
     [(assoc ?tracer :at ?nt) ?t]
     [(tap> ?t) _]]
    [(by-type [?type] ?e)
     ;pre
     [(hash-map :phase :pre :rule 'by-type :type ?type :e ?e) ?pre]
     (trace> ?pre)
     ;rule
     [?e :inv/type ?type]
     ;post
     [(hash-map :phase :post :rule 'by-type :type ?type :e ?e) ?post]
     (trace> ?post)]
    [(by-size [?size] ?e)
     ;pre
     [(hash-map :phase :pre :rule 'by-size :size ?size :e ?e) ?pre]
     (trace> ?pre)
     ;rule
     [?e :inv/size ?size]
     ;post
     [(hash-map :phase :post :rule 'by-size :size ?size :e ?e) ?post]
     (trace> ?post)]
    [(by-type-and-size [?type ?size] ?e)
     ;pre
     [(hash-map :phase :pre :rule 'by-type-and-size :type ?type :size ?size) ?pre]
     (trace> ?pre)
     ;rule
     (by-type ?type ?e)
     (by-size ?size ?e)
     ; post
     [(hash-map :phase :post :rule 'by-type-and-size :type ?type :size ?size :e ?e) ?post]
     (trace> ?post)
     ]])

(defn get-items-by-type-and-size
  "Returns pull maps describing all items matching type"
  [db type size pull-expr]
  (d/q '[:find (pull ?e pull-expr)
         :in $ % ?type ?size pull-expr
         :where
         (by-type-and-size ?type ?size ?e)]
       db rules type size pull-expr))

(get-items-by-type-and-size db :shirt :small '[:inv/sku :inv/color :inv/size])
Attached is a screenshot showing the output in REBL after executing get-items-by-type-and-size then browsing the tapped values as a collection of maps.

🆒 12