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 <[email protected]> wrote:
> On May 25, 2015, at 3:19 PM, Sam Roberton <[email protected]> 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 [email protected]
> Note that posts from new members are moderated - please be patient with
> your first post.
> To unsubscribe from this group, send email to
> [email protected]
> 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 [email protected].
> 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 [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
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 [email protected].
For more options, visit https://groups.google.com/d/optout.