Grokking Diesel, Rust's ORM
medium.com
medium.com
The tests are indeed informative, and give a good idea of how it would be used in practice. I just looked at a few and I have a much better idea of how Diesel looks to be used in practice.
What I didn't see, and don't know if it exists since I haven't read a lot of the docs, is whether Diesel has a built in way to deliver joined results as HashMaps with all fields, as that would be the simplest way I would use it in practice.
For example, if I'm doing the equivalent of:
SELECT user.*, post.*, comment.* FROM user LEFT JOIN post ON user.id = post.user_id LEFT JOIN comment ON post.id = comment.post_id
Does it have a helper method to identify the three tables that are joined, and instead of returning individual rows, return a HashMap of the user_id to user, a HashMap of the user_id to a list of posts, and a HashMap of the post_id to a list of comments? I think that would be: vec![ HashMap<i32,User>, HashMap<i32,Vec<Post>>, HashMap<i32,Vec<Comment>> ]
This would approximate the actual "objectiness" or many ORMs, and would make it easier to loop over data in the desired way with a minimum of boilerplate.Edit: Or perhaps the more performant way would be to allow registering of callbacks for each change of user, post and comment, and call the appropriate one if defined. That way there isn't time and memory spent on condensing the data into hashmaps? I'm new to thinking about how to deal with ORM results in a language like this.
HashMap<User, (Vec<Post>, Vec<Comment>)>
HashMap<i32, (User, Vec<Post>, Vec<Comment>)>
Vec<(User, Vec<Post>, Vec<Comment>)>On rethinking this though, I'm wondering if the more performant way might be to provide a helper function that iterates over all returned rows, identifies when one record type in the result ends and another begins (the same steps you have to do to condense), but just executes a registered callback at that time. E.g. user_begin, user_end, post_begin, post_end, etc.
Although I should probably read up more on how Java/C/C# do this before spouting my half formed ideas about an area I'm not really informed in.... ;)
I haven't used Slick, I'd be interested, if you have the time, in an elaboration with examples on this stuff.
For instance this is an example from Diesel:
Post::belonging_to(user).load(&connection)
And this is how it would look like in Slick:
Post.filter(_.userID === 1).result
So both of the above would translate into SQL "SELECT * FROM posts WHERE user_id = 1", however Slick is more explicit whereas Diesel definitely feels like an ORM. The tripple equals in Slick example for instance is a nice touch and an example of operator overloading, because userID could be of type Integer or Option[Integer] (nullable field) and it would work both ways.
I personally find "ORM" to be such a broad term as to be almost meaningless. This may be a personal problem :)
I see now. I am pretty sure that you could write
posts.filter(user_id.eq(1)).load(&connection)
if you wanted; the belonging_to is just fancier and a bit shorter.Anyway, thank you! I've mentally filed a "check out Slick sometime" bug for my infinite personal-to-do list :)
I do agree that things like associations are an entirely new layer of abstraction.
val base = for {
ur <- UserRole
u <- ur.user
r <- ur.role
} yield (ur, u, r)
val withManager = for {
(ur, u, r) <- base
ts <- TeamStaff if u.id === ts.userId
} yield (ur, u, r, ts)
val teams = for {
(_, _, _, ts) <- withManager
t <- Team if t.id === ts.teamId
} yield (ts, t)
You can slice and dice a schema however you like, building up queries from other queries, none of which are run until you execute them. Basically you get semantically the same query plans as you'd get writing plain sql, but it's all type checked, sql injection safe, and compiled (queries generally are generated at compile time, not run time).Quill, another Scala FRM library, is worth checking out in this regard, as is Haskell's Esqueleto.
I think if you read the post, you'll see that Diesel is very data-driven.
Personally, I tend to call it a "query builder" if I think someone might be ORM-phobic; I find it closer to that than other ORMs, even though it's authored by the maintainer of one of the most massively popular ORMs ever. They've sometimes joked that that experience has taught them what not to do ;)
I agree that if you're just writing a single self-contained query, SQL is a great DSL for that. I've been trying to improve our story for working with raw SQL (one tradeoff will be that it isn't type checked).
However, there are major benefits to an in-language DSL as well. If you are wanting to re-use fragments of queries, or conditionally construct a different query, that's a pain to do with SQL strings.
Just curious, what ORM is that?
I understand. The other side of that coin though I guess is that knowing this might give some more confidence in using diesel. ActiveRecord was the first ORM I used, and knowing now that diesel is "from the same stock" (for want of a better phrase) gives some extra confidence in what it can do / where it is going.
Not that I was not confident in it before...
I don't see this particular case of someone who spent a significant amount of time coding core Rails OSS library being seen as a negative though, quite the opposite. If anything it would be an excellent measure of the language for that particular use case assuming any competent developer would seek to operate in the constraints of the language rather than shoehorning an existing implementation into something that doesn't fit.
The bigger question is whether the ORM label itself is limiting or can be understood to fit into a broader scope of implementations.
I agree that the "I make Rails" thing is a double edged sword. I would hope people would see it as an indicator that I have a lot of experience and lessons to learn from, but I think many people see the opposite.
[0] https://github.com/tildeio/helix - When Helix can handle that complexity (if it can't already)
Cool, didn't know Sean Griffin maintained Hibernate!
I've always found this convention to be problematic at best and I've learned to avoid it. Maintaining consistency of names inside and outside of code makes searches and references easier to follow without having to mentally (or even mechanically) expand the singular name to plural name or vice versa (say, in a scratch pad to compose a test query).
Is the pluralization rule literally as simple as stated in the article, namely appending an 's' and calling it done? English is rarely so simple and there are tons of corner cases which is why I tend to frown on the pluralization rules that are unpredictable. Those must be a nightmare for English-as-a-second-language folks too.
Yes: https://github.com/diesel-rs/diesel/blob/e52d1710d98a05c3aa4...
But it also converts CamelCase to snake_case: https://github.com/diesel-rs/diesel/blob/e52d1710d98a05c3aa4...