honeysql merge-where building a large query - clojure

I'm trying to use honeysql to programmatically build a query, adding on where clauses as I go.
Coming from python and using sqlalchemy, I can do something like:
In [3]: query = model.Account.query
In [4]: query = query.filter_by(id=1)
In [5]: query = query.filter_by(email='abc#foo.com')
In [6]: query = query.filter_by(username='someuser')
In [7]: query = query.filter_by(is_active=1)
In [8]: printquery(query)
SELECT *
FROM account
WHERE account.id = 1 AND account.email = 'abc#foo.com'
AND account.username = 'someuser' AND account.is_active = 1
However, using honeysql, my where clause isn't as clean.
user=> (require '[honeysql.core :as sql])
user=> (require '[honeysql.helpers :refer :all])
user=> (->
#_=> (select :*)
#_=> (from :test)
#_=> (merge-where [:= :a 1])
#_=> (merge-where [:= :b 2])
#_=> (merge-where [:= :c 3])
#_=> (merge-where [:= :d 4])
#_=> sql/format)
["SELECT * FROM test WHERE (((a = 1 AND b = 2) AND c = 3) AND d = 4)"]
I am aware that they're logically the same thing, but as I start to get more and more complex, I'm starting to get nervous that I'm gonna get some subtle query acting weird with extra parens that causes me problems.
Am I being crazy? Should I stop worrying and learn to love the extra parens (It is clojure after all)? Or is there a better pattern for query building I'm not aware of? Should I build my where clauses as a big vector, and add them all to the query map at the very end?
Any advice would be greatly appreciated!

Look at it from a logical perspective
a AND b AND c = ( a AND b ) AND c = a AND ( b AND c ).
where a, b and c are propositions like
A = 1
The AND operator is associative, meaning that it doesn't matter where the parenthesis are placed, or how many, as long as the sequence of the propositions remains the same.
Worked out examples here.

Related

Clojure- Remove entries from customized set

I'm a bit new to Clojure and was wondering if anybody can assist me here.
I'm trying my hands on clojure's persistent data-structures.I have 2 hashset like below example.
(def a #{[1 345] [2 346] [3 347]})
(def b #{1 2})
So,I want to remove 'b' from 'a'.I am expecting output like below:
#{[3 347]}
I've looked for quite a bit to try and get an understanding of how to do this, but so far I'm just puzzled.I've tried playing around with remove/tried to convert 'a' into a map.but couldn't get the expected result
.
Here's one way: Build a map from a and use dissoc to remove the keys found in b.
> (set (apply dissoc (into {} a) b))
#{[3 347]}
Here is my solution, though I must confess I like the above one more:
(set (filter #(not (contains? b (first %))) a))
#{[3 347]}
Using specter:
(setval [ALL (comp b first)] NONE a)

update or assoc a list rather than a vector

Updating a vector works fine:
(update [{:idx :a} {:idx :b}] 1 (fn [_] {:idx "Hi"}))
;; => [{:idx :a} {:idx "Hi"}]
However trying to do the same thing with a list does not work:
(update '({:idx :a} {:idx :b}) 1 (fn [_] {:idx "Hi"}))
;; => ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.Associative clojure.lang.RT.assoc (RT.java:807)
Exactly the same problem exists for assoc.
I would like to do update and overwrite operations on lazy types rather than vectors. What is the underlying issue here, and is there a way I can get around it?
The underlying issue is that the update function works on associative structures, i.e. vectors and maps. Lists can't take a key as a function to look up a value.
user=> (associative? [])
true
user=> (associative? {})
true
user=> (associative? `())
false
update uses get behind the scenes to do its random access work.
I would like to do update and overwrite operations on lazy types
rather than vectors
It's not clear what want to achieve here. You're correct that vectors aren't lazy, but if you wish to do random access operations on a collection then vectors are ideal for this scenario and lists aren't.
and is there a way I can get around it?
Yes, but you still wouldn't be able to use the update function, and it doesn't look like there would be any benefit in doing so, in your case.
With a list you'd have to walk the list in order to access an index somewhere in the list - so in many cases you'd have to realise a great deal of the sequence even if it was lazy.
You can define your own function, using take and drop:
(defn lupdate [list n function]
(let [[head & tail] (drop n list)]
(concat (take n list)
(cons (function head) tail))))
user=> (lupdate '(a b c d e f g h) 4 str)
(a b c d "e" f g h)
With lazy sequences, that means that you will compute the n first values (but not the remaining ones, which after all is an important part of why we use lazy sequences). You have also to take into account space and time complexity (concat, etc.). But if you truly need to operate on lazy sequences, that's the way to go.
Looking behind your question to the problem you are trying to solve:
You can use Clojure's sequence functions to construct a simple solution:
(defn elf [n]
(loop [population (range 1 (inc n))]
(if (<= (count population) 1)
(first population)
(let [survivors (->> population
(take-nth 2)
((if (-> population count odd?) rest identity)))]
(recur survivors)))))
For example,
(map (juxt identity elf) (range 1 8))
;([1 1] [2 1] [3 3] [4 1] [5 3] [6 5] [7 7])
This has complexity O(n). You can speed up count by passing the population count as a redundant argument in the loop, or by dumping the population and survivors into vectors. The sequence functions - take-nth and rest - are quite capable of doing the weeding.
I hope I got it right!

Clojure shortcuts for destructuring maps

This question just came up for me as I was learning about using the shortcut :keys for destructuring a map.
Suppose i have the following map:
(def my-hashmap {:a "A" :b "B" :c "C" :d "D"})
Now i can refer to the values writing
(let [{first-key :a second-key :b} my-hashmap]
(println first-key second-key))
and i get A B. Now using :keys the keys have to be the same as the original ones (without :), or otherwise clojure returns nil? So, for
(let [{:keys [first-key second-key]} my-hashmap]
(println first-key second-key))
you get nil nil.
Am i seeing this wrong or is really this un-logical thing happening in clojure, because both methodes were meant to refer to the keys and thus should work in the same manner? But they don't, because if I write (let [{:keys [a b]} my-hashmap] (println a b)) I get again the right answer!
Your requirement to :keys in the second example can't be met because there is no way to deduce which individual keys should be looked up to have their values bound to the arbitrary symbols you are using.
Notice that maps are associative, but not ordered data structures. Hence if you wish to use different names, they need to be mapped to the names and types their values can be looked up at in the destructored map, as you did in your first example.
You can use both symbols and keywords in the :keys vector that have fully qualified names of keywords in the map. Futhermore, :syms and :strs are available to destructor against keys which are symbols or strings.
Amir,
Not the specific answer to the question however; maps implement Iterableso you can potentialy create a scheme to work with maps without knowing the key names:
(defn map-sequence-example
[& [[[k v] kp2 & remaining] :as mymap] ]
(println "first key = " k " first value = " v)
(println "next key pair = " kp2)
(println "remaining = " remaining)
(println "mapseq = " mymap))
(map-sequence-example (seq {:a 1 :b 2 :c 3}))
first key = :a first value = 1
next key pair = [:b 2]
remaining = ([:c 3])
mapseq = (([:a 1] [:b 2] [:c 3]))

Named parameters in Yesql

How can one use named parameters when running queries using Yesql? For instance, given the following query
-- name: example
select * from table1 where col1=:value1 and col2=:value2
i'd like to use the following code:
(defquery q "query.sql")
(q db {:value1 "A" :value2 "B"})
Right now, Yesql treats the map as a single positional parameter.
Thanks!
The current version of Yesql - 0.4.0 - does not support easily this. The named parameters are only for documentation. You can see them if you do (doc q), but you can't use them to run the query. The author of Yesql has mentioned that they're planning exactly what you want for Yesql 0.5.
What I've done in the same situation is to just manually wrap the query into another function:
(defn q1 [db {:keys [value1 value2]}] (q db value1 value2))
It's a bit cumbersome, but it works.
It is possible to do this automatically even without improving Yesql, but... this is going to be a bit of a hack. You probably do not want to do this.
We can define a function similar to apply that uses the query function's metadata to get the arguments in the right order from the map of named parameters.
(defn apply-with-arglist-fn [f arglist & args]
(let [nargs (butlast args)
kwargs (last args)]
(apply f (concat nargs (map #(get kwargs (keyword %))
(drop (count nargs) arglist))))))
;; Let's define a macro to make calling this a bit easier.
(defmacro apply-with-arglist [f & args]
"Applies fn f to the arguments in a map by matching them with f's
arglist. Works like apply, except that the last parameter should be
a map of keyword arguments. For example:
(defn f [a b c] ...)
(apply-with-arglist f 1 {:b 2 :c 3})
This is equivalent to calling (f 1 2 3)."
`(apply-with-arglist-fn ~f (first (:arglists (meta (var ~f)))) ~#args))
You can use this to run the query:
(apply-with-arglist q db {:value1 "A" :value2 "B"})
Really, though, there should be error handling and dealing with corner cases. A better approach would be to see if you can help the Yesql author to get Yesql 0.5 ready.

Difference between doseq and for in Clojure

What's the difference between doseq and for in Clojure? What are some examples of when you would choose to use one over the other?
The difference is that for builds a lazy sequence and returns it while doseq is for executing side-effects and returns nil.
user=> (for [x [1 2 3]] (+ x 5))
(6 7 8)
user=> (doseq [x [1 2 3]] (+ x 5))
nil
user=> (doseq [x [1 2 3]] (println x))
1
2
3
nil
If you want to build a new sequence based on other sequences, use for. If you want to do side-effects (printing, writing to a database, launching a nuclear warhead, etc) based on elements from some sequences, use doseq.
Note also that doseq is eager while for is lazy. The example missing in Rayne's answer is
(for [x [1 2 3]] (println x))
At the REPL, this will generally do what you want, but that's basically a coincidence: the REPL forces the lazy sequence produced by for, causing the printlns to happen. In a non-interactive environment, nothing will ever be printed. You can see this in action by comparing the results of
user> (def lazy (for [x [1 2 3]] (println 'lazy x)))
#'user/lazy
user> (def eager (doseq [x [1 2 3]] (println 'eager x)))
eager 1
eager 2
eager 3
#'user/eager
Because the def form returns the new var created, and not the value which is bound to it, there's nothing for the REPL to print, and lazy will refer to an unrealized lazy-seq: none of its elements have been computed at all. eager will refer to nil, and all of its printing will have been done.