datomic

Ask questions on the official Q&A site at https://ask.datomic.com!
schmee 2020-10-19T09:40:54.065100Z

can I write a shortest path query in Datomic, e.g can I determine if it is possible to navigate from Entity A to Entity B via some reference attribute?

schmee 2020-10-19T09:41:21.065700Z

I’ve looked at all the examples of recursive rules that I could find and they all “hardcode” the depth of the search (such as the MBrainz example: https://github.com/Datomic/mbrainz-sample/blob/master/src/clj/datomic/samples/mbrainz/rules.clj#L37)

kenny 2020-10-19T16:31:58.068400Z

I would've expected the below query to return all txes where ?tx is not in ?ignore-tx. I actually get all txes, as if the not is completely ignore. ?ignore-tx is passed in as a set of tx ids. Why would this happen?

'[:find ?t ?status ?tx ?added
  :in $ [?ignore-tx ...]
  :where
  [?t ::task/status ?status ?tx ?added]
  (not [(identity ?ignore-tx) ?tx])]

favila 2020-10-19T16:38:05.068500Z

datalog comparisons are not “type”-aware. are all ?ignore-tx actually tx longs and not some other representation?

kenny 2020-10-19T16:38:43.068700Z

Yes

(type (first ignore-txes))
=> java.lang.Long

favila 2020-10-19T16:38:51.068900Z

are they T or TX?

kenny 2020-10-19T16:39:00.069100Z

tx

favila 2020-10-19T16:39:01.069300Z

(both are longs, but TXs have partition bits)

favila 2020-10-19T16:39:43.069500Z

does this behave differently? [(!= ?ignore-tx ?tx)]

favila 2020-10-19T16:39:52.069700Z

(instead of (not …)

kenny 2020-10-19T16:40:34.069900Z

Same result

favila 2020-10-19T16:41:23.070100Z

print (first ignore-txes) ?

kenny 2020-10-19T16:41:45.070300Z

(first ignore-txes)
=> 13194142112981

favila 2020-10-19T16:43:11.070500Z

and you’re actually sure this is in the result set? You can test with `

'[:find ?t ?status ?tx ?added
  :in $ [?tx ...]
  :where
  [?t ::task/status ?status ?tx ?added]
  ]

kenny 2020-10-19T16:44:35.070700Z

(d/q {:query '[:find ?t ?status ?tx ?added
                 :in $ [?ignore-tx ...]
                 :where
                 [?t ::task/status ?status ?tx ?added]
                 [(!= ?ignore-tx ?tx)]
                 [?tx :audit/user-id ?user]]
        :args  [(d/history (d/db conn))
                #{13194142035321 13194142112981}]
        :limit 10000})
=>
[[606930421025569 :cs.model.task/status-in-progress 13194142112981 false]
 [606930421025569 :cs.model.task/status-in-progress 13194142035321 true]
 [606930421025569 :cs.model.task/status-open 13194142112981 true]
 [606930421025569 :cs.model.task/status-open 13194142035321 false]]

kenny 2020-10-19T16:46:34.070900Z

Identical result with (not [(identity ?ignore-tx) ?tx]).

favila 2020-10-19T16:49:41.071100Z

That is really weird. I can’t reproduce with a toy example

favila 2020-10-19T16:49:43.071300Z

(d/q '[:find ?e ?stat ?tx ?op
       :in $ [?ignore-tx ...]
       :where
       [?e :status ?stat ?tx ?op]
       [(!= ?ignore-tx ?tx)]
       ]
     [[1 :status :foo 100 true]
      [1 :status :bar 100 false]]
     #{100}
     )

favila 2020-10-19T16:49:55.071500Z

=> #{}

kenny 2020-10-19T16:50:30.071700Z

Yeah - that's what I would expect

favila 2020-10-19T16:53:05.071900Z

what about using contains?

favila 2020-10-19T16:53:41.072100Z

(d/q '[:find ?e ?stat ?tx ?op
       :in $ ?ignore-txs
       :where
       [?e :status ?stat ?tx ?op]
       (not [(contains? ?ignore-txs ?tx)])
       ]
     [[1 :status :foo 13194142112981 true]
      [1 :status :bar 13194142112981 false]
      [1 :status :baz 13194142112982 true]]
     #{13194142112981}
     )
=> #{[1 :baz 13194142112982 true]}

favila 2020-10-19T16:54:33.072400Z

I’m just kinda probing to see if this is a problem with comparisons or something deeper

kenny 2020-10-19T16:54:55.072600Z

(d/q {:query '[:find ?t ?status ?tx ?added
                 :in $ ?ignore-tx
                 :where
                 [?t ::task/status ?status ?tx ?added]
                 (not [(contains? ?ignore-tx ?tx)])
                 [?tx :audit/user-id ?user]]
        :args  [(d/history (d/db conn))
                #{13194142035321 13194142112981}]})
=> []

kenny 2020-10-19T16:55:44.072800Z

That's the expected result. Still odd that the former didn't work.

kenny 2020-10-19T16:57:03.073Z

Even odder is that it worked in your toy example.

favila 2020-10-19T16:57:06.073200Z

I think that points to something funky with the numeric comparisons done by the datalog engine, like it’s using object identity or something.

favila 2020-10-19T16:58:45.073500Z

my toy example used on-prem, but should be able to replicate with cloud or peer-server

favila 2020-10-19T17:00:14.073700Z

I was using 1.0.6165

kenny 2020-10-19T17:01:18.073900Z

This is using the client api 0.8.102 and connecting to a system running in the cloud.

favila 2020-10-19T17:03:40.074100Z

Here is a trivial example:

favila 2020-10-19T17:03:44.074300Z

'[[(path-exists? ?e1 ?a ?e2)
   [?e1 ?a ?e2]]
  [(path-exists? ?e1 ?a ?e2)
   [?e1 ?a ?e-mid]
   [(!= ?e2 ?e-mid)]
   [(path-exists? ?e-mid ?a ?e2)]]]

favila 2020-10-19T17:04:52.074500Z

the general pattern with recursive rules is to define the rule multiple times, and have one that is terminal, and the rest recursive, and (generally but not required) the rule impls match disjoint sets.

favila 2020-10-19T17:05:57.074700Z

unfortunately there’s no “cut” to stop evaluation early. I’m pretty sure this example will exhaustively discover every possible path, even though any one will do. However, it may discover them in parallel.

kenny 2020-10-19T17:08:38.074900Z

Seems to work as expected with dev-local as well.

schmee 2020-10-19T17:13:33.075100Z

thank you for the detailed example! 🙂

kenny 2020-10-19T17:16:46.076100Z

Datomic Cloud includes :db-name and :database-id as get'able keys from a d/db. Are these part of the official API?

kenny 2020-10-19T17:17:23.076500Z

e.g.,

(d/db conn)
=>
{:t 2580397,
 :next-t 2580398,
 :db-name "my-db",
 :database-id "74353541-feea-4ea2-afa6-f522a169856d",
 :type :datomic.client/db}

favila 2020-10-19T17:18:18.076600Z

Note this example only searches refs in a forward direction. With two additional implementations, it could search backwards also

kenny 2020-10-19T17:19:01.076900Z

It would appear so (for :db-name at least) https://docs.datomic.com/client-api/datomic.client.api.html#var-db

kenny 2020-10-19T17:20:23.077700Z

If that is true, shouldn't dev-local support that? See below example using dev-local 0.9.203.

(def c2 (d/client {:server-type :dev-local,
                   :system "dev-local-bB7z07Io_A",
                   :storage-dir "/home/kenny/.datomic/data/dev-local-bB7z07Io_A"}))
(d/db (d/connect c2 {:db-name "cust-db__0535019e-79fe-44a1-a8d9-b19394abd958"}))


(:db-name *1)
=> nil

kenny 2020-10-19T17:31:23.077900Z

Fairly certain this is a bug so I opened a support req: https://support.cognitect.com/hc/en-us/requests/2879