Fork me on GitHub
#xtdb
<
2021-04-12
>
mac13:04:40

I am having issues with using a predicate in a query. Is there any reason why this shoud not work? Not using a logic variable to try to find the error. Ns t is java-time. [(t/contains? (t/interval "2020-10-18T09:21:31Z/2020-10-30T12:10:00Z") (t/instant))]

refset13:04:32

I think there are two issues here: 1. namespaces have to be fully qualified, since the query engine won't have access to your local aliases 2. predicates aren't quite as a flexible as arbitrary clojure forms, they will only work 1-level-deep. So here your 2 parameters to t/contains? will be seen by the query engine as lists and not evaluated expressions. To work around this you can create additional clauses with intermediate variables, e.g. [(tick.alpha.api/interval "2020-10-18T09:21:31Z/2020-10-30T12:10:00Z") time][(tick.alpha.api/instant) now] [(tick.alpha.api/contains? time now)]

mac14:04:13

Thanks, that works.

mac14:04:51

And then again not. I am creating a function to take the interval and run the query, but I think I am messing up the quoting. Normally when I build these types of functions I quote the logic varibales individually. But this does not work here as the arguments to contains are never resolved.

(q
        {:find '[(pull ?i [*])]
         :where [['?i :type event-type]
                 ['?i :time '?time]
                 [(java-time/interval interval) ?it]
                 [(java-time/contains? ?it ?time)]]})

refset15:04:32

aha, sorry to assume it was tick!

refset15:04:37

hmm, should that be:

(q
        {:find '[(pull ?i [*])]
         :where [['?i :type event-type]
                 ['?i :time '?time]
                 '[(java-time/interval interval) ?it]
                 '[(java-time/contains? ?it ?time)]]})

refset15:04:04

or you can do:

(q
        {:find '[(pull ?i [*])]
         :where [['?i :type event-type]
                 ['?i :time '?time]
                 [(list java-time/interval 'interval) '?it]
                 [(list java-time/contains? '?it '?time)]]})

mac15:04:31

Both fails with :

"Clause refers to unknown variable: interval {:pred {:pred-fn #function[java-time.interval/interval], :args [interval]}, :return [:scalar ?it]}"}
                 error.clj:   12  crux.error/illegal-arg

mac15:04:23

For reference here is the entire function def:

(defn events-of-type
  [interval event-type]
  (map first
       (q
        {:find '[(pull ?i [*])]
         :where [['?i :type event-type]
                 ['?i :time '?time]
                 '[(java-time/interval interval) ?it]
                 '[(java-time/contains? ?it ?time)]]})))

refset18:04:28

thanks, that helps! you probably want this:

(defn events-of-type
  [interval event-type]
  (map first
       (q
        {:find '[(pull ?i [*])]
         :where [['?i :type event-type]
                 ['?i :time '?time]
                 [(list java-time/interval interval) '?it]
                 '[(java-time/contains? ?it ?time)]]})))

refset18:04:22

you could also use :in bindings to pass in the interval value (this will also have a very minor performance benefit, since compiled queries are cached)

mac18:04:22

This fails with:

Unhandled crux.IllegalArgumentException
   Query didn't match expected structure
   {:crux.error/error-type :illegal-argument,
    :crux.error/error-key :query-spec-failed,
    :crux.error/message "Query didn't match expected structure",
    :explain
    #:clojure.spec.alpha{:problems
                         ({:path [:where :triple :e],
                           :pred
                           (clojure.core/some-fn
                            crux.query/logic-var?
                            crux.codec/valid-id?
                            clojure.core/set?),
                           :val
                           (#function[java-time.interval/interval]
                            "2020-10-18T09:21:31Z/2020-10-30T12:10:00Z"),
                           :via
                           [:crux.query/query
                            :crux.query/where
                            :crux.query/term
                            :crux.query/triple],
                           :in [:where 2 0]}
                          {:path [:where :not],
                           :pred clojure.core/seq?,
                           :val
                           [(#function[java-time.interval/interval]
                             "2020-10-18T09:21:31Z/2020-10-30T12:10:00Z")
                            ?it],
                           :via
                           [:crux.query/query
                            :crux.query/where
                            :crux.query/term
                            :crux.query/not],
                           :in [:where 2]}
                          {:path [:where :not-join],
                           :pred clojure.core/seq?,
                           :val
                           [(#function[java-time.interval/interval]
                             "2020-10-18T09:21:31Z/2020-10-30T12:10:00Z")
                            ?it],
                           :via
                           [:crux.query/query
                            :crux.query/where
                            :crux.query/term
                            :crux.query/not-join],
                           :in [:where 2]}
                          {:path [:where :or],
                           :pred clojure.core/seq?,
                           :val
                           [(#function[java-time.interval/interval]
                             "2020-10-18T09:21:31Z/2020-10-30T12:10:00Z")
                            ?it],
                           :via
                           [:crux.query/query
                            :crux.query/where
                            :crux.query/term
                            :crux.query/or],
                           :in [:where 2]}
                          {:path [:where :or-join],
                           :pred clojure.core/seq?,
                           :val
                           [(#function[java-time.interval/interval]
                             "2020-10-18T09:21:31Z/2020-10-30T12:10:00Z")
                            ?it],
                           :via
                           [:crux.query/query
                            :crux.query/where
                            :crux.query/term
                            :crux.query/or-join],
                           :in [:where 2]}
                          {:path [:where :range],
                           :pred (clojure.core/= (clojure.core/count %) 1),
                           :val
                           [(#function[java-time.interval/interval]
                             "2020-10-18T09:21:31Z/2020-10-30T12:10:00Z")
                            ?it],
                           :via
                           [:crux.query/query
                            :crux.query/where
                            :crux.query/term
                            :crux.query/range],
                           :in [:where 2]}
                          {:path [:where :rule],
                           :pred clojure.core/list?,
                           :val
                           [(#function[java-time.interval/interval]
                             "2020-10-18T09:21:31Z/2020-10-30T12:10:00Z")
                            ?it],
                           :via
                           [:crux.query/query
                            :crux.query/where
                            :crux.query/term
                            :crux.query/rule],
                           :in [:where 2]}
                          {:path [:where :pred :pred :pred-fn],
                           :pred clojure.core/symbol?,
                           :val #function[java-time.interval/interval],
                           :via
                           [:crux.query/query
                            :crux.query/where
                            :crux.query/term
                            :crux.query/pred
                            :crux.query/pred-fn
                            :crux.query/pred-fn],
                           :in [:where 2 0 0]}),
                         :spec :crux.query/query,
                         :value
                         {:find [(pull ?i [*])],
                          :where
                          [[?i :type :incident]
                           [?i :time ?time]
                           [(#function[java-time.interval/interval]
                             "2020-10-18T09:21:31Z/2020-10-30T12:10:00Z")
                            ?it]
                           [(java-time/contains? ?it ?time)]]}}}
                 error.clj:   12  crux.error/illegal-arg
                 error.clj:    3  crux.error/illegal-arg
                 error.clj:    7  crux.error/illegal-arg
                 error.clj:    3  crux.error/illegal-arg
                 query.clj:  248  crux.query/normalize-and-conform-query/fn
         second_chance.clj:   44  crux.cache.second-chance.SecondChanceCache/computeIfAbsent
                 cache.clj:    7  crux.cache/compute-if-absent
                 cache.clj:    6  crux.cache/compute-if-absent
                 query.clj:  240  crux.query/normalize-and-conform-query
                 query.clj:  235  crux.query/normalize-and-conform-query
                 query.clj: 1813  crux.query.QueryDatasource/open_q_STAR_
                 query.clj: 1788  crux.query.QueryDatasource/q_STAR_
                   api.clj:  349  crux.api/q
                   api.clj:  343  crux.api/q
               RestFn.java:  425  clojure.lang.RestFn/invoke
                      REPL:   68  hypercontracts.domains.interpreter.db/q
                      REPL:   66  hypercontracts.domains.interpreter.db/q
                      REPL:   92  hypercontracts.domains.interpreter.db/events-of-type
                      REPL:   89  hypercontracts.domains.interpreter.db/events-of-type
                      REPL:   99  hypercontracts.domains.interpreter.db/eval88897
                      REPL:   99  hypercontracts.domains.interpreter.db/eval88897
             Compiler.java: 7181  clojure.lang.Compiler/eval
             Compiler.java: 7136  clojure.lang.Compiler/eval
                  core.clj: 3202  clojure.core/eval
                  core.clj: 3198  clojure.core/eval
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  667  clojure.core/apply
                  core.clj: 1977  clojure.core/with-bindings*
                  core.clj: 1977  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java:  137  clojure.lang.RestFn/applyTo
                  core.clj:  667  clojure.core/apply
                  core.clj:  662  clojure.core/apply
                regrow.clj:   20  refactor-nrepl.ns.slam.hound.regrow/wrap-clojure-repl/fn
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  152  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  202  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  201  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java:  834  java.lang.Thread/run

refset18:04:44

oh yeah, good spot by the query compiler 🙂 this is the key line > :val (#function[java-time.interval/interval] > "2020-10-18T09:21:31Z/2020-10-30T12:10:00Z")

refset18:04:58

you can't pass functions by reference, only by symbol name

refset18:04:15

i.e.

(defn events-of-type
  [interval event-type]
  (map first
       (q
        {:find '[(pull ?i [*])]
         :where [['?i :type event-type]
                 ['?i :time '?time]
                 [(list 'java-time/interval interval) '?it]
                 '[(java-time/contains? ?it ?time)]]})))

refset18:04:02

the alternative approach, with :in looks like:

(defn events-of-type
  [interval event-type]
  (map first
       (q
        '{:find [(pull ?i [*])]
          :in [?event-type ?interval]
          :where [[?i :type event-type]
                 [?i :time ?time]
                 [(java-time/interval ?interval) ?it]
                 [(java-time/contains? ?it ?time)]]}
         event-type interval)))

refset18:04:14

a bit nicer 🙂

mac18:04:13

First one works 🙂 The :in version fails with

Unhandled clojure.lang.ArityException
   Wrong number of args (3) passed to: hypercontracts.domains.interpreter.db/q

mac18:04:21

I thought there were double [ missing around the :in clause, but that does not make a difference.

refset18:04:47

Oh I assumed that was the Crux q function! You need to make your custom q function support & args

refset18:04:40

Since that's how :in work, you have variable arguments passed in to q

mac18:04:51

I just spotted that 🙂 sorry. N q is the below. I think I stole that from some of your example code.

(defn q
  [query]
  (crux/q (crux/db node) query))

mac18:04:26

The :in version is much more elegant.

mac18:04:21

@U899JBRPF Thanks for your help. I must say I don't find quoting rules easy to figure out. Using :in seems to make it much more straightforward.

refset19:04:00

nice, that's great 😊 thanks for the feedback!

refset19:04:38

I'm tempted to remove quoting from the docs and tutorials in general, since it is quite an alien thing for anyone who's not familiar with Clojure/lisp

mac20:04:42

That's one approach, but it is also necessary to do many useful things in Clojure. Or maybe show how using :in can solve some common cases.

👍 3