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.