Fork me on GitHub
#datomic
<
2023-06-01
>
Dean Hayes15:06:21

Hi everyone, I wonder if anybody can point me in the right direction with some behaviour I ran into with Datomic rules? Basically, I want to pass an entity ident to a rule but this doesn't seem to work how I expected. I've got a test case to show what I'm trying to do which I'll post in the thread of this message so I don't clog this view up...

Dean Hayes15:06:57

Here's my test case:

;; Set up the DB...
  (def my-conn
    (let [db-name "test"
          client-config {:server-type :dev-local
                         :system db-name
                         :storage-dir :mem}
          client (d/client client-config)]
      (d/create-database client {:db-name db-name})
      (d/connect client {:db-name db-name})))

  (d/transact my-conn {:tx-data [{:db/ident :parent/id
                                  :db/unique :db.unique/identity
                                  :db/valueType :db.type/uuid
                                  :db/cardinality :db.cardinality/one}

                                 {:db/ident :parent/child
                                  :db/valueType :db.type/ref
                                  :db/cardinality :db.cardinality/one}

                                 {:db/ident :child/id
                                  :db/unique :db.unique/identity
                                  :db/valueType :db.type/uuid
                                  :db/cardinality :db.cardinality/one}]})

  (def parent-id (random-uuid))
  (def child-id (random-uuid))

  (d/transact my-conn {:tx-data [{:parent/id parent-id
                                  :parent/child {:child/id child-id}}]})

  (def rules
    '[[(demo-rule ?entity ?attr ?val)
       [?entity ?attr ?val]]])

  ;; Here's the issue...
  (d/q '[:find ?parent
         :in $ ?child-ident
         :where
         [?parent :parent/child ?child-ident]]
       (d/db my-conn)
       [:child/id child-id])
  ;; Works as I expected: => [[79164837199948]]

  (d/q '[:find ?parent
         :in $ % ?child-ident
         :where
         (demo-rule ?parent :parent/child ?child-ident)]
       (d/db my-conn)
       rules
       [:child/id child-id])
  ;; Not what I expected: => []

Dean Hayes15:06:12

I've tried various things to try and work out what is going on here including printing the inputs to the rule (which all look as I expected). Unexpectedly, I've found that if I hard code the attr in the rule then it works as I expected, e.g.:

Dean Hayes15:06:25

(def rules
    '[[(demo-rule ?entity ?attr ?val)
       [?entity :parent/child ?val]]])
	   
  (d/q '[:find ?parent
         :in $ % ?child-ident
         :where
         (demo-rule ?parent :parent/child ?child-ident)]
       (d/db my-conn)
       rules
       [:child/id child-id])
  ;; Works: => [[79164837199948]]

Dean Hayes15:06:37

Although I'm not sure what this tells me!? If anyone has any ideas what might be up here (am I doing this incorrectly?) I'd be very grateful!

Dean Hayes15:06:50

thanks! 🙂

favila15:06:03

This isn’t caused by the rule. The problem is that when the attribute is not a static value, the query does not infer that the ident is a reference.

favila15:06:07

the ident keyword is not actually in the datom value being matched (just like attribute keywords are not actually in the datoms either). It is translated to a number before matching if the attribute slot is known to be a ref type statically

favila15:06:43

that is not knowable with this pattern [?entity ?attr ?value], so it only performs exact-matches on ?value

favila15:06:03

to work around this, you need to perform the ident->entity id translation yourself

Dean Hayes15:06:02

Ahh, I see. Thanks for the explanation, that explains it. Thank you, I think I'll be able to work this out now, then. Cheers! 🙂