How Should I Iterate a Sequence? - clojure

Below is my attempt to iterate a sequence of maps; the code fails due to the casting error: Exception in thread "main" java.lang.RuntimeException: java.lang.ClassCastException: clojure.lang.Cons cannot be cast to java.util.Map$Entry.
Can anyone explain/demonstrate how I should iterate the result-set? Thanks.
(with-connection db
(with-query-results rs ["select category from users group by category"]
(doall
(for [s [rs]]
(do (println (val s)))))))

You wrapped the rs into a vector. So s will be bound to the whole sequence, not the individual map entries. So when you call val it doesn't know what to do with a sequence. Hence the exception. This should work:
(with-connection db
(with-query-results rs ["select category from users group by category"]
(doall
(for [rec rs
s rec]
(do
(println (val s)))))))
However the ugly doall and do around the for should ring a bell, that something could be improved. And indeed for is used to construct another lazy sequence. This does not work well with side-effects as you intend in your example. You should use doseq in this case.
(with-connection db
(with-query-results rs ["select category from users group by category"]
(doseq [rec rs
s rec]
(println (val s)))))
The interface for the bindings of doseq is identical to that of for. However it executes things immediatelly, and thusly realises any side-effects immediatelly. If you put multiple expressions in the body of a for, you have to wrap it into a do. This is a reminder that the body should produce a value. Multiple expressions however indicate side-effects. doseq therefore wraps the body into a do for you. So you can easily have multiple expressions. For illustration:
(doall
(for [s seq-of-maps]
(do
(println (key s))
(println (val s)))))
(doseq [s seq-of-maps]
(println (key s))
(println (val s)))))
As a rule of thumb: you need side-effects? Look for things starting in do!
As a rule of thumb 2: if something looks ugly (see above comparison), this should ring a bell.

OK, so it sounds like you are trying to do a DB query from Clojure. You may have to supply more information about the "users" table for instance and what your query result set looks like.
At any rate, something like this may work
(def a (with-query-results rs ["select category from users group by category"]
(doall rs)))
(map #(:category %) a)

Related

Clojure: delete item from a ref?

I created the ref "people" below, and want the function delete-person to delete an item from the data structure within a transaction.
(defrecord Person [name favorite-color])
(def people (ref ()))
(defn add-person [new-person]
(dosync
(alter people conj new-person)))
(add-person (Person. "Joe" "Red"))
(add-person (Person. "Sam" "Blue"))
;; how do I make this function work?
;; would #people (destructured) have to be the second argument to filter?
(defn delete-person [name-to-delete]
"Delete named person from ref"
(dosync
(alter people filter #(not= (:name %) name-to-delete))))
(delete-person "Joe")
IllegalArgumentException Don't know how to create ISeq from:
user$delete_person$fn__1407$fn__1408 clojure.lang.RT.seqFrom (RT.java:542)
The function below works because I filter on the destructured ref, but how do I do it in a transaction to mutate the data?
(filter #(not= (:name %) "Sam") #people)
=> (#user.Person{:name "Joe", :favorite-color "Red"})
As there error says, you're trying to iterate a function. This is coming about because when you write:
(alter people filter #(not= (:name %) name-to-delete))
The unwrapped people becomes the first argument to filter, not the last.
You'll need to use an full fn, or use partial:
(alter people
(fn [ps] (filter #(not= (:name %) name-to-delete) ps)))
Or
(alter people
(partial filter #(not= (:name %) name-to-delete)))
These make alter pass the unwrapped people as the last argument to filter, instead of implicitly as the first.
I'll note though:
As #cfrick brought up in the comments, using lazy sequences in a transaction may have the potential to cause problems. I can't offhand think of a scenario where it would, but it feels wrong. It could be argued that the realization of a lazy sequence is a side effect, and side effects shouldn't take place in a transaction, since transactions may run multiple times in the event of a conflict. Multiple realizations shouldn't cause a problem, but I can't say definitively that it's safe (honestly, I never use refs).
Make sure you actually need refs and transactions here. Transactions are for when you need to sequence multiple alterations to data, and need to be able to catch when the data involved has been changed part way through a transaction. If you just need a simple mutable container though, atom are much simpler.

How to iterate over a result set and extract one particular value in clojure?

Below is my attempt to iterate over a result set and get its values
(sql/with-connection db
(sql/with-query-results rs ["select * from user where UserID=?" 10000]
(doseq [rec rs
s rec]
(println (val s))
)))
But how do you extract one particular value from it; i need only the user name field.
Can anyone please demonstarte how to do this?
The result set is a sequence of maps, so if you wanted to obtain one field (e.g. one called name) then:
(sql/with-connection db
(sql/with-query-results rs ["select * from user where UserID=?" 10000]
(doseq [rec rs]
(let [name (:name rec)]
(println "User name:" name)
(println "Full record (including name):" rec)))))
But as mentioned in the comments, if you only want name, then select name from would be the more efficient option. The code above is useful when you need the full row for something else.
The with-connection / with-query-results syntax is deprecated as of clojure.java.jdbc 3.0. Filtering results can be done much easier with the new query syntax and additional :row-fn and :result-set-fn parameters.
(query db ["select * from user"]
:row-fn :name
:result-set-fn #(doall (take 1000 (drop 10000 %))))
Be sure to make the result-set-fn realize all values, it shouldn't return a lazy sequence (hence the doall in this example).

let does not execute the second expression in the body

If I understand the documentation right, I can write this:
(let [a 5 b 10]
(print a)
(print b))
This works as expected and prints out 5 and 10. I use this mechanism in this code snippet (The full code is here if you need more reference):
(defn create! [fields]
(let [username (user/unique-user (:author fields))
competition-id (:id (competition/latest))]
(if-not (exists? (:ld_uid fields))
(insert entries (values {
:ld_uid (:ld_uid fields)
:title (:title fields)
:description (:description fields)
:type (:type fields)
:user_id (:id username)
:competition_id competition-id})))
(for [link (:links fields)]
(source/create! (:id (by-ld-uid (:ld_uid fields))) (:url link) (:title link)))))
the problem is, the for expression is not executed. The statements as its own works as expected but in this code above the insert expression is executed but the for not. Do I oversee something? I am out of ideas whats going wrong here :(
The problem is not what you think: Clojure's for uses lazy evaluation -- that is, if you don't do anything with what it returns, the loop never runs.
That's very useful for long-running loops over massive sequences that you don't even need in their entirety, but when you're actually trying to run side effects, lazy evaluation is something you need to stay away from.
You want to use doseq instead (http://clojure.github.io/clojure/clojure.core-api.html#clojure.core/doseq).

clojure: dynamically compose query with korma

I am trying to create a very simple API with korma
Users can query a database like so:
localhost:8080/my_postgres_db/users.json?where[age]=50&limit=1
Currently I am getting an error when trying to apply a where clause to an existing, composable, query.
clojure.lang.ArityException: Wrong number of args (2) passed to: core$where
The code in question:
(defn- comp-query [q [func arg]]
(let [sql-fn (ns-resolve 'korma.core (-> func name symbol))]
(sql-fn q arg)))
(defn compose-query [table col]
(reduce comp-query (select* table) col))
Usage:
(def clauses {:where {:column1 10} :fields "a,b" :limit 10 :offset 500})
(-> (compose-query table clauses) select)
Everything behaves as expected, except for where clauses. I can combine limit, offset and fields in any way I choose and I get the expected results. Only when I have a :where key in my map do I run into the error.
Am I attempting something I shouldn't? Is this bad clojure? Any help would be appreciated.
Note: I have read this SO question
Edit: from lein repl I can manually compose a query in the same fashion and it works
(where (select* "my_table") {:a 5})
Edit:
If I modify my compose-query function to this:
(defn compose-query [table col]
; remove where clause to process seperately
(let [base (reduce comp-query (select* table) (dissoc col :where))]
(if-let [where-clause (:where col)]
(-> base (where where-clause))
base)))
Everything works as expected.
The problem here is that korma.core/where is not a function and needs to be handled specially. Where can't be implemented as a function and still correctly handle things like (where query (or (= :hits 1) (> :hits 5)))
You can use where* function as you are using select*.
Just make your clause map like:
(def clauses {:where* {:column1 10} :fields "a,b" :limit 10 :offset 500})
Just a hunch; expanding some of the threading macros makes it a little hard to see if they are correct:
core> (macroexpand-1 '(-> (compose-query table clauses) select))
(select (compose-query table clauses))
core> (macroexpand-1 '(-> func name symbol))
(clojure.core/-> (clojure.core/-> func name) symbol)
core> (macroexpand-1 '(clojure.core/-> func name))
(name func)
Passing func to name looks suspicious.

How can I improve this Clojure function?

I just wrote my first Clojure function based on my very limited knowledge of the language. I would love some feedback in regards to performance and use of types. For example, I'm not sure
if I should be using lists or vectors.
(defn actor-ids-for-subject-id [subject-id]
(sql/with-connection (System/getenv "DATABASE_URL")
(sql/with-query-results results
["SELECT actor_id FROM entries WHERE subject_id = ?" subject-id]
(let [res (into [] results)]
(map (fn [row] (get row :actor_id)) res)))))
It passes the following test (given proper seed data):
(deftest test-actor-ids-for-subject-id
(is (= ["123" "321"] (actor-ids-for-subject-id "123"))))
If it makes a difference (and I imagine it does) my usage characteristics of the returned data will almost exclusively involve generating the union and intersection of another set returned by the same function.
it's slightly more concise to use 'vec' instead of 'into' when the initial vector is empty. it may express the intent more clearly, though that's more a matter of preference.
(vec (map :actor_id results))
the results is a clojure.lang.Cons, is lazy sequence, return by clojure.java.jdbc/resultset-seq. each record is a map:
(defn actor-ids-for-subject-id [subject-id]
(sql/with-connection (System/getenv "DATABASE_URL")
(sql/with-query-results results
["SELECT actor_id FROM entries WHERE subject_id = ?" subject-id]
(into [] (map :actor_id results)))))