Generating query clauses with korma and clojure

你说的曾经没有我的故事 提交于 2020-01-13 20:43:15

问题


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)))     

回答1:


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.




回答2:


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




回答3:


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!