This is a repost of a question I asked on the Datomic google group (see here - last 3 posts are best).
This problem revolves around the fact that I'm dynamically generating Datomic queries. So the number of parameters (names and values) is not known until passed into a function. I can generate the query just fine. The core problem is that, using the Clojure Datomic API, I cannot call the datomic.api/q function with a dynamic number of arguments. So the below ~@unquote-splice expression fails. I tried several other approaches, including partial and apply, to no avail.
(def expression-final `(datomic.api/q ~expression-intermediate ~db-conn ~@param-values)) ;; gives the error in question
(eval expression-final)
java.lang.Exception: processing rule: (q__34868 ?e)
at datomic.datalog$eval_rule$fn__4687.invoke(datalog.clj:977)
at datomic.datalog$eval_rule.invoke(datalog.clj:957)
at datomic.datalog$eval_query.invoke(datalog.clj:999)
at datomic.datalog$qsqr.invoke(datalog.clj:1053)
at datomic.datalog$qsqr.invoke(datalog.clj:1021)
at datomic.query$q.invoke(query.clj:453)
at datomic.api$q.doInvoke(api.clj:31)
... 1 stack levels elided ...
at user$eval34866.invoke(crud_spec.clj:32)
... 3 stack levels elided ...
at stefon_datomic.crud$retrieve_entity.invoke(crud.clj:95)
...
Caused by: java.lang.Exception: processing clause: [?e :posts/title (quote ?title)] ;; this fails with or without the (quote ...)
at datomic.datalog$eval_clause$fn__4667.invoke(datalog.clj:934)
at datomic.datalog$eval_clause.invoke(datalog.clj:900)
at datomic.datalog$eval_rule$fn__4687.invoke(datalog.clj:972)
at datomic.datalog$eval_rule.invoke(datalog.clj:957)
...
Caused by: java.lang.UnsupportedOperationException: nth not supported on this type: Symbol
... 2 stack levels elided ...
at datomic.datalog$extrel_coll$fn__4384.invoke(datalog.clj:197)
... 4 stack levels elided ...
at datomic.datalog$iterator.invoke(datalog.clj:30)
I'm wondering if this is a bug in the Datomic Clojure API? Or if there's an easier way to pass in a dynamic number of variables. Hard coding the amount of variables passed in, defeats the purpose of dynamically generating the query. See the last 3 posts here, to get more detail.
Thanks
So, based on your source-code, I have managed to simplify the code a bit:
(defn add-entity-ns
[ekey datom-map]
(reduce-kv (fn [a k v]
(assoc a (keyword
(name ekey)
(name k))
v))
{}
datom-map))
(defn retrieve-entity
[conn constraint-map]
(let [name-fn (comp symbol
(partial str "?")
name)
param-names (map name-fn
(keys constraint-map))
param-vals (vals constraint-map)
constraint-map (add-entity-ns :posts constraint-map)
where-clause (map #(vector '?e % %2)
(keys constraint-map)
param-names)
in-clause (conj param-names '$)
final-clause (concat [:find '?e]
[:in] in-clause
[:where] where-clause)]
(apply d/q final-clause (d/db conn) param-vals)))
A note: When I started using datomic, I wrote myself similar functions to generate queries. I ended up throwing them away, for several reasons:
filter
and so on. If you have a large database, use reducers with foldcat
.