Fork me on GitHub
#datomic
<
2019-02-27
>
lambdam17:02:39

Hello, I'm strugling with Datalog to express this: give me all the user ids that are not in the list. Here are two versions that I tried and that don't work:

(d/q '[:find [?e ...]
       :in $ [?excluded-id ...]
       :where
       [?e :user/email]
       (not-join [?excluded-id]
         [?excluded-id :user/email])]
     db
     [5678 9123])
;; => []
;; no id

(d/q '[:find [?e ...]
       :in $ [?excluded-id ...]
       :where
       [?e :user/email]
       [(not= ?e ?excluded-id)]]
     db
     [5678 9123])
;; => [1234 5678 9123 ...]
;; all the ids
Does someone know how to do that ? Thanks

favila17:02:55

You don't want to join against your excluded-id collection. I.e. for any id ?e , even if it matches one of the excluded ids it can't match all of them. the not-join and not= will only exclude if it doesn't match any of them

favila17:02:16

just use a set

favila17:02:32

'[:find [?e ...]
       :in $ ?excluded-id-set
       :where
       [?e :user/email]
       (not [(contains? ?excluded-id-set ?e)])]

favila18:02:06

another possibility is to negate any successful join

favila18:02:22

you do this by pushing the destructuring down into the join

favila18:02:47

(d/q '[:find [?e ...]
       :in [?e ...] ?not-e+
       :where
       
       (not-join [?e ?not-e+]
         [(identity ?not-e+) [?e ...]])
       ]
  [1 2 3 4] [2])
=> [1 3 4]

favila18:02:12

using a set with contains? is usually faster and clearer

favila18:02:53

actually not-join is unnecessary, you can just (not [(identity ?not-e+) [?e ...]])

Keith02:02:10

I will say that using a collection binding [?excluded-id ...] causes a logical OR to happen.

Keith02:02:56

Logical AND can be enforced by using a double`not-join` as shown here: https://stackoverflow.com/a/43808266

lambdam11:02:28

Thanks for the solution. I was almost there with a version that I didn't put:

(d/q '[:find [?e ...]
       :in $ ?excluded-ids
       :where
       [?e :user/email]
       [(not (contains? ?excluded-ids ?e))]]
     db
     (set excluded-ids))
Version that doesn't work either. It doesn't filter and returns all the ids. Do you know why?

favila11:02:16

[(not (contains? ?excluded-ids ?e))]]

favila11:02:43

only the first level of an expression is evaluated

favila11:02:10

your not is negating a literal list

favila11:02:24

the list (contains? ?excluded-ids ?e)

favila11:02:43

you can do (not [(contains? ?excluded-ids ?e)]) like I did

favila11:02:25

or you can do two clauses [(contains? ?excluded-ids ?e) ?excluded] [(not ?excluded)]

lambdam12:02:27

Ok thanks. Now I get why my nested version wan't working. Thank you very much @U09R86PA4 and @U424XHTGT

5
Joe Lane18:02:28

@jaret IMO a solution/example/explaination the above problem statement should be documented somewhere other than slack, as I’ve run into this before and it was extremely difficult to find an answer. Eventually I had to phone a friend from a different corp so he could walk me through it. This was after going through all datomic cloud documentation, all day of datomic cloud, day of datomic documentation, and every post I could find. If I missed something, and then please ignore this documentation request.

👍 12
jaret20:02:49

@joe.lane Thanks! I’ll bring this up with the team and find the best place for it. I’ll cross post to the thread once we have it up.