Fork me on GitHub
#datomic
<
2017-06-29
>
linuss12:06:31

Hey guys. I want to query some data that is filtered by a value, but ONLY if that value is not nil. I currently have:

'[:find ?label ?value
              :where
              [?i :functiontype/functiontype ?label]
              [?i :functiontype/functiontypeid ?value]
              [?i :functiontype/blocked false]
              [_ :offer/function ?fid]
              [?j :functions/functionid ?fid]
              [?j :functions/functiontypeid ?ftid]
              [(or (nil? ?fid) (= ?ftid ?value))]]
but that doesn't seem to work, and also doesn't seem very idiomatic... Could anybody suggest an alternative?

uwo13:06:58

@linuss there will never be a nil value in datomic. Instead use missing? http://docs.datomic.com/query.html#sec-5-12-5

linuss13:06:46

Ah, right!

linuss13:06:07

Hm, even when I replace the nil? with missing?, the query returns all :functiontypes

favila13:06:19

query predicates only do query var replacement at the first level

favila13:06:33

[(or (nil? ?fid) (= ?ftid ?value))] is always true, because those inner items of or are literal lists

favila13:06:41

what you want to do cannot be done without changing the query

favila13:06:07

or at least without branching

favila13:06:12

you could have a top-level or (or a rule) with two branches, one of which asserts value is nil, and this is the nil-case query, another one starts with [?i :functiontype/functiontypeid ?value] and is the non-nil case

favila13:06:42

Personally I almost always use cond-> to create different queries dynamically

linuss13:06:21

Okay, thanks! I'll try that 🙂

hmaurer13:06:37

@favila what do you mean by cond->?

linuss13:06:37

But the or in datomic doesn't short-circuit, right?

linuss13:06:53

So, it will return datoms that match either set of predicates

linuss13:06:00

so I'll always get all the functiontypes

favila13:06:37

yes, but one branch will assert that ?value is nil, and the other one will match on nil, and both of these can't be true at the same time

linuss13:06:49

ah! clever!

favila13:06:55

@hmaurer eg

(defn query-with-optional-filters [name]
  {:query {:find '[?e]
           :in   (cond-> '[$]
                   name (conj '?name))
           :where
                 (cond-> '[[?e :foo :bar]]
                   name (conj '[?e :name ?name]))}})

hmaurer13:06:24

@favila oh, so you build up the query dynamically based on the presence of a filter. Neat

linuss13:06:30

Okay, so this is my new query:

[:find ?label ?value
              :where
              (or
               (and [?i :functiontypes/functiontype ?label]
                    [?i :functiontypes/functiontypeid ?value]
                    [(missing $ ?e :offer/function)])
               (and [_ offer/function ?fid]
                    [?i :function/functionid ?fid]
                    [?i :function/functiontypeid ?value]
                    [?j :functiontypes/functiontypeid ?value]
                    [?j :functiontypes/functiontype ?label]))]
but this won't run, saying Cannot parse clause, expected (data-pattern | pred-expr | fn-expr | rule-expr | not-clause | not-join-clause | or-clause | or-join-clause)

linuss13:06:48

no and clause

hmaurer14:06:15

@linuss isn’t a vector the way to model a and clause?

linuss14:06:36

That results in Join variables should not be empty

hmaurer18:06:07

@marshall did you answer the earlier question on why d/with cannot be used with d/as-of? If you did, sorry, I missed it