I am trying to generate korma query conditions based on a map of columns and values that I pass into a function.
I am finding that when an empty map is passed to korma's where:
(select "things"
(where conditions))
Generates queries with an empty WHERE which causes a SQL error:
SELECT * FROM things WHERE () LIMIT 10
However using this form:
(select "things"
(if-not (empty? conditions)
(where conditions)))
Results in an error: "Wrong number of args (1) passed to: core$where"
Is there an idiomatic way of handling dynamic clauses in korma?
UPDATE
The following works, but is pretty clumsy (note the strangely necessary if format)
(defn do-select []
(-> (select* "things")
(fields :id :data)
(limit 10)))
(defn add-constraints [query conditions]
(if (empty? conditions)
query
(where query (conditions-to-clause-map conditions))))
(select (do-select)
(add-constraints conditions)
(where (is_owner? owner)))
I don't think if it is possible to generate dynamic queries without looking under the hood of korma and trying to call some private API, but that is a bad bad idea. The problem with your second code is that select
and where
are macros. What select does is:
(defmacro select
[ent & body]
`(let [query# (-> (select* ~ent)
~@body)]
(exec query#)))
As you can see it thread the select*
return value to next forms and if you introduce the if
clause that causes this threading to break and where
gets only one value (which was your map) rather than the getting value of select* and the map, hence the error says wrong number of arguments.
As of now it seems eval
with some dynamic code generation is your friend. Something like:
(eval `(select "things"
~(if (empty? conditions)
`identity
`(where conditions))))
I haven't tried it, but I hope it gives you the idea.
It's a little odd at first but keeping the "code as data" mantra in mind, I created a "my-where" function that would either pass along the original query of no conditions or insert a real korma where clause as the result.
Something like:
(defn my-where [query conditions]
(if-not (nil? conditions)
(-> query (where conditions))
query))
Then you could use:
(select "things"
(my-where conditions))
Hope this helps,
Greg
You've probably figured out a way to solve this but I'll chime in. Personally I end up using the cond-> macro which is available if you're using Clojure 1.6+.
You can find information on the new threading macros here.
The resulting code looks like something like this:
(let [query (->(select* "things")
(fields :id :data)
(limit 10))]
(-> (cond-> query
conditions (where conditions)
more-conditions (where more-conditions))
(exec)))
来源:https://stackoverflow.com/questions/10961285/generating-query-clauses-with-korma-and-clojure