Search code examples
databasepostgresqlherokuclojureheroku-postgres

Looping on a database with Clojure


I just got started with Clojure on Heroku, first reading this introduction. Now in the phase of getting my hands dirty, I am facing this issue handling a database in a loop.

This is working:

(for 
  [s (db/query (env :database-url)
                   ["select * from My_List"])]
      ; here one can do something with s, for example:
      ; print out (:field s)
)

But it is not enough to update variables inside the loop as I want. Reading on the subject, I understand that Clojure having its own way of handling variables I need to use a loop pattern.

Here is what I tried:

(loop [a 0 b 1
       s (db/query (env :database-url)
                 ["select * from My_List"])]
      ; here I want to do something with s, for example
      ; print out (:field s)
      ; and do the following ... but it does not work!
  (if (> (:otherField s) 5)
    (:otherField s)
    (recur (+ a (:otherField s)) b s))
)

Since I tried various ways of doing before writing this post, I know that the code above works except for the fact that I am doing something wrong concerning the database.

So here comes my question: What do I need to change to make it work?


Solution

  • I see, that it's hard to get to the functional thinking at first, when you're used to a different paradigm.

    I don't think there is a correct explanation on “how to do this loop right”, because it's not right to do a loop here.

    The two things that feel most incorrect to me:

    1. Never do a SELECT * FROM table. This is not how relational databases are ment to be used. For example when you want the sum of all values greater than 5 you should do: SELECT SUM(field) FROM my_list WHERE field > 5

    2. Don't think in loops (how to do it) but in what you want to do with the data:

      • I want to work on field :otherFIeld
      • I am only interested in values bigger than 5
      • I want the sum of all the remaining values

    Then you come to something like this:

    (reduce +
            (filter #(> % 5)
                    (map :otherField
                         (db/query (env :database-url) ["select * from My_List"]))))
    

    (No loop at all.)