It's interesting how this question pops up every now and again. I find
that, for the types of projects I work with, I'm perfectly happy writing
little combinators to work with result sets myself.

At one project though, a standard RESTful web application with a
traditional relational model, I ended up writing code to parse SQL result
sets into Clojure maps. Just enough code to make simple cases like this
work:

;; Usage

(def authors-parser
  (partial parse-result-set {:relation-prefix :authors
                             :relation-pk :id
                             :associations [[:posts :one-to-many]
                                            [:address :one-to-one]]}))

(authors-parser sample-result)

;; ({:address {:street "Atchison St"},
;;   :posts
;;   ({:title "Title 2", :id 1, :name "Leo"}
;;    {:title "Title New", :id 1, :name "Leo"}),
;;   :id 1,
;;   :name "Leo"}
;;  {:address {:street "Pitt St"},
;;   :posts ({:title "The Book", :id 2, :name "Enif"}),
;;   :id 2,
;;   :name "Enif"})


This worked reasonably well for what I needed.

But then I also got sucked in into implementing the reverse. That is,
generating the sql from a Clojure map. I got it working to the point where
a simple case like this successfully persisted changes - though I never
used this part of it in the project I mentioned above:

(def leo (first (select authors :include [posts])));; {:posts;;
({:title "Title 1", :id 20, :content "whaaa whaaaa"};;   {:title
"Title 2", :id 21, :content "whooo whoooo"}),;;  :name "Leonardo
Borges",;;  :id 1}
(-> leo
    (t/assoc :name "Leo")
    (t/assoc :posts [{:title "Title New", :content "I'm totally new!"}
                     {:title "Title 2", :id 21, :content "whooo whoooo"}])
    t/save!)

(first (select authors :include [posts]))
;; {:posts;;  ({:title "Title 2", :id 21, :content "whooo whoooo"};;
{:title "Title New", :id 22, :content "I'm totally new!"}),;;  :name
"Leo",;;  :id 1}


The amount of work required to turn this into a decent open source library
though isn't small - if nothing else I might finish it one day as an
exercise.

Unless other people are genuinely interested in seeing this out in the
wild. A lot of the time this is overkill though.

Cheers,

On Tue, May 26, 2015 at 9:45 AM Sean Corfield <s...@corfield.org> wrote:

> On May 25, 2015, at 3:19 PM, Sam Roberton <sam.rober...@gmail.com> wrote:
>
> On Tuesday, 26 May 2015 06:43:18 UTC+10, Krzysiek Herod wrote:
>>
>> Sean, maybe my use case is specific to web applications. In REST API's
>> it's common to respond with something like "Example Result" here:
>> https://dev.twitter.com/rest/reference/get/statuses/mentions_timeline I
>> completely agree with you about ORMs, but in case of REST API's one just
>> needs to somehow map the queryset to a structure of hashes and arrays (not
>> necessarily objects), and this task is much heavier than I thought in the
>> beginning, from choosing between making many queries to fill in the hash of
>> related objects vs making JOINs, to complexity of extracting data from a
>> set of rows with potential repetitions, rows of null values (like Daniel
>> said happens for instance in case of LEFT JOINS), and different kinds of
>> relations (has-one, has-many, belongs-to).
>>
>
> I found myself needing this for a REST API as well, so I implemented an
> 'unjoin' function which sounds like what you're looking for.
>
> Here's a gist, including test case (not necessarily a hugely comprehensive
> test case, but better than nothing):
> https://gist.github.com/samroberton/d72cedaf225526d9007a
>
>
> Yup, and that’s exactly what I meant by:
>
> "Any structural grouping semantics you want to apply are up to your
> application"
>
> In order to correctly "unjoin" your result set for your application, you
> need to apply structural grouping based on knowledge of the application
> domain (parent / child keys in desired result set) — which doesn't
> necessarily map 1:1 onto the SQL used to create the result set.
>
> Sean Corfield -- (904) 302-SEAN
> An Architect's View -- http://corfield.org/
>
> "Perfection is the enemy of the good."
> -- Gustave Flaubert, French realist novelist (1821-1880)
>
>
>
>  --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with
> your first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to clojure+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to