Skip to content

Latest commit

 

History

History
83 lines (71 loc) · 3.7 KB

overview.md

File metadata and controls

83 lines (71 loc) · 3.7 KB

Love the Clojure REPL? Of course you do. That's why Walkable offers the most frequently mentioned feature of the REPL according to State of Clojure surveys for many years: slow start-up time :)

{% hint style="info" %}

By choosing Walkable over other SQL libraries like HugSQL, HoneySQL etc. you have agreed to take the risk of spending up to five minutes reading the documentation before your very first SQL query can hit the database!

{% endhint %}

Basically you define your floor-plan like this:

{:idents           { ;; query with `[:person/by-id 1]` will result in
                    ;; `FROM person WHERE person.id = 1`
                    :person/by-id :person/id
                    ;; just select from `person` table without any constraints
                    :people/all "person"}
 :true-columns    #{:person/name :person/yob}
 :extra-conditions { ;; enforce some constraints whenever this join is asked for
                    :pet/owner [:and
                                [:= :person/hidden true]
                                ;; yes, you can nest the conditions whatever you like
                                [:or [:= :person/id 5]
                                 ;; a vector implies an `AND` for the conditions inside
                                     [[:in :person/yob
                                       1970
                                       1971
                                       ;; yes, you can!
                                       [:cast "1972" :integer]]
                                      [:like :person/name "john"]]
                                 ;; you can even filter by properties of a join, not just
                                 ;; the item itself
                                 {:person/pet [:or [:= :pet/color "white"]
                                                   [:= :pet/color "green"]]}
                                 ]]}
 :joins            {;; using a join table. Similar to:
                    ;; "JOIN `person_pet` ON `person`.`id` = `person_pet`.`person_id` JOIN `pet` ON `person_pet`.`pet_id` = `pet`.`id`"
                    :person/pet [:person/id :person-pet/person-id :person-pet/pet-id :pet/id]
                    ;; a column refering to another column. Similar to:
                    ;; "JOIN `person` ON `pet`.``owner` = `person`.`id`"
                    :pet/owner [:pet/owner :person/id]}
 :cardinality      {:person/by-id :one
                    :person/pet   :many}}

then you can make queries like this:

'[{(:people/all {:limit    5
                 :offset   10
                 ;; remember the extra-conditions above? you can use the same syntax here:
                 :filters [:or [:= :person/id 1]
                               [:in :person/yob 1999 2000]]
                 ;; -> you've already limited what the user can access, so let them play freely
                 ;; with whatever left open to them.
                 :order-by [:person/id
                            :person/name :desc
                            ;; Note: sqlite doesn't support `:nils-first`, `:nils-last`
                            :person/yob :desc :nils-last]})
   [:person/id :person/name]}]

As you can see the filter syntax is in pure Clojure. It's not just for aesthetic purpose. The generated SQL will always get parameterized so it's safe from injection attacks. For instance:

[:or [:like :person/name "john"]
     [:in :person/id 3 4 7]]

will result in

(jdbc/query ["SELECT <...> WHERE person.name LIKE ? OR person.id IN (3, 4, 7)"
             "john"]

You can express pretty much anything with those S-expressions. Learn more about them here.