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?
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)
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])]
datalog comparisons are not “type”-aware. are all ?ignore-tx actually tx longs and not some other representation?
Yes
(type (first ignore-txes))
=> java.lang.Long
are they T or TX?
tx
(both are longs, but TXs have partition bits)
does this behave differently? [(!= ?ignore-tx ?tx)]
(instead of (not …)
Same result
print (first ignore-txes)
?
(first ignore-txes)
=> 13194142112981
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]
]
(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]]
Identical result with (not [(identity ?ignore-tx) ?tx]).
That is really weird. I can’t reproduce with a toy example
(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}
)
=> #{}
Yeah - that's what I would expect
what about using contains?
(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]}
I’m just kinda probing to see if this is a problem with comparisons or something deeper
(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}]})
=> []
That's the expected result. Still odd that the former didn't work.
Even odder is that it worked in your toy example.
I think that points to something funky with the numeric comparisons done by the datalog engine, like it’s using object identity or something.
my toy example used on-prem, but should be able to replicate with cloud or peer-server
I was using 1.0.6165
This is using the client api 0.8.102 and connecting to a system running in the cloud.
Here is a trivial example:
'[[(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)]]]
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.
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.
Seems to work as expected with dev-local as well.
thank you for the detailed example! 🙂
Datomic Cloud includes :db-name
and :database-id
as get
'able keys from a d/db
. Are these part of the official API?
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}
Note this example only searches refs in a forward direction. With two additional implementations, it could search backwards also
It would appear so (for :db-name at least) https://docs.datomic.com/client-api/datomic.client.api.html#var-db
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
Fairly certain this is a bug so I opened a support req: https://support.cognitect.com/hc/en-us/requests/2879