datomic 2017-06-29

@linuss has joined the channel

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?

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

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

query predicates only do query var replacement at the first level

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

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

or at least without branching

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

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

Okay, thanks! I'll try that 🙂

@favila what do you mean by cond->?

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

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

so I'll always get all the functiontypes

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

@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]))}})

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

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)

no and clause

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

That results in Join variables should not be empty

@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