Using find results the DNY way

[...]

> I feel

Programming is ideally about facts, not feelings. (How closely we approach that ideal is another question...)

If only it were that simple :wink: I have used books to learn about Rails and I do use the docs alot, but most of the time I'm dealing with people who have very specific needs and it can be very hard to work your way through Rails' naming conventions. Eager loading, for example, is something you will really have to stumble across in a book. And when you know the term, you know where to find it and how to use it. Sometimes you just have to assume certain things and let others correct you if you're wrong.

> making one database request for everything I need is better > than making five, but I've now learned find doesn't work that way.

It can. Use :joins instead of :include.

I've used joins in the past (in my PHP era), but I have a feeling that it doesn't get me the results I want.

The docs mentioned that :joins can be called the same way as :include. Will this work? And will it trigger just one query?

@project = Project.find(:first, :conditions => { :id => params [:id] }, :joins => [:floors) @floors = @project.floors

My guess is that it's not that simple. Especially because what I understand from the MySQL docs, INNER JOIN or CROSS JOIN will generate a new table where rows with matching keys are combined and output its contents. Or would the above work? And would it also work for more complicated examples like the one I posted at 0:27 today?

> > making one database request for everything I need is better > > than making five, but I've now learned find doesn't work that way.

> It can. Use :joins instead of :include.

I've used joins in the past (in my PHP era), but I have a feeling that it doesn't get me the results I want.

The docs mentioned that :joins can be called the same way as :include. Will this work? And will it trigger just one query?

@project = Project.find(:first, :conditions => { :id => params [:id] }, :joins => [:floors) @floors = @project.floors

My guess is that it's not that simple. Especially because what I understand from the MySQL docs, INNER JOIN or CROSS JOIN will generate a new table where rows with matching keys are combined and output its contents. Or would the above work? And would it also work for more complicated examples like the one I posted at 0:27 today?

:joins just adds the appropriate JOIN clause to your select statement. It does not do anything else, so in particular it won't have done anything to @project.floors. :include does have a join based strategy that it will use if it can't use the multiple queries strategy. it is triggered if it thinks that you have conditions (or an order, select etc.) that refers to one of the associated tables

Fred

Sure, you can set up multiple master stuff, shard your data, run memcache servers, read from slaves etc. None of these are trivial though, certainly less so in my experience than adding another server running mongrels into your cluster (which requires 0 code changes). Not saying either that doing everything in ruby is the right thing either, just raising the point that it's not a black and white world.

Fred

True, it's not black and white at all. Every application has its needs and every solution has its benefits. However, database-heavy applications need (and thus already have) multiple database servers and adding a database server to such a cluster is very easy. Going from one to two servers is less trivial, agreed, but that's where we (hosting companies) come in. We've done that kind of stuff a hundred times.

Hi--

[...]

I feel

Programming is ideally about facts, not feelings. (How closely we approach that ideal is another question...)

If only it were that simple :wink: I have used books to learn about Rails and I do use the docs alot, but most of the time I'm dealing with people who have very specific needs and it can be very hard to work your way through Rails' naming conventions. Eager loading, for example, is something you will really have to stumble across in a book. And when you know the term, you know where to find it and how to use it. Sometimes you just have to assume certain things and let others correct you if you're wrong.

making one database request for everything I need is better than making five, but I've now learned find doesn't work that way.

It can. Use :joins instead of :include.

I've used joins in the past (in my PHP era), but I have a feeling that it doesn't get me the results I want.

The docs mentioned that :joins can be called the same way as :include. Will this work? And will it trigger just one query?

@project = Project.find(:first, :conditions => { :id => params [:id] }, :joins => [:floors) @floors = @project.floors

My guess is that it's not that simple. Especially because what I understand from the MySQL docs, INNER JOIN or CROSS JOIN will generate a new table where rows with matching keys are combined and output its contents. Or would the above work? And would it also work for more complicated examples like the one I posted at 0:27 today?

We may have different development methodologies, but mine is to get the demonstrably correct results without considering performance. By "demonstrably" I mean with tests that can verify that the results produced are the same as the ones expected. ActiveRecord is very smart, but it has a fair amount of breadth, as you are discovering. You may not grasp every nuance right away, but with the appropriate tests or specs or whatever TDD methodology underlying your models, you can identify performance bottlenecks and then try different tuning options to see which works in the environment and conditions under which your application code runs.

We can all make educated guesses about where performance will be impacted, but optimization is a long tedious task and 80% of the time often gains you 20% or less better results. I think your client will be more impressed by working code right now than fast code later. Heck, they may look at the results and say, "wow, that's cool but if we could only [insert new request here]..." which would send you back to the drawing board.

What I'm advocating -- and as often as it's repeated, it never gets old for me -- red / green / refactor. Get it right first, then make it pretty, make it fast, or move on to the next part of the effort.

Be aware that in production mode, AR does some slick optimizations of its own. Also, by being sensible with your database indexes, you might find all of this is handled by the infrastructure.

Sorry for being a bit preachy.

We may have different development methodologies, but mine is to get
the demonstrably correct results without considering performance. By
"demonstrably" I mean with tests that can verify that the results
produced are the same as the ones expected. ActiveRecord is very
smart, but it has a fair amount of breadth, as you are discovering.
You may not grasp every nuance right away, but with the appropriate
tests or specs or whatever TDD methodology underlying your models, you
can identify performance bottlenecks and then try different tuning
options to see which works in the environment and conditions under
which your application code runs.

We can all make educated guesses about where performance will be
impacted, but optimization is a long tedious task and 80% of the time
often gains you 20% or less better results. I think your client will
be more impressed by working code right now than fast code later.
Heck, they may look at the results and say, "wow, that's cool but if
we could only [insert new request here]..." which would send you back
to the drawing board.

What I'm advocating -- and as often as it's repeated, it never gets
old for me -- red / green / refactor. Get it right first, then make it
pretty, make it fast, or move on to the next part of the effort.

Be aware that in production mode, AR does some slick optimizations of
its own. Also, by being sensible with your database indexes, you might
find all of this is handled by the infrastructure.

Sorry for being a bit preachy.

Not preacy at all! Your comments are highly appreciated.

But please understand that opening this discussies was initiated by a question I asked myself: "am I doing this the right way?". If I can instantly get it right, it will make future projects alot easier for me. I'm not entirely new to Rails, but this is actually my first big Rails project, so I'm still learning.

This discussion is not about how to optimize my application as much as possible, but it's about using the right tools for the job.

Jaap Haagmans wrote: [...]

most of the time I'm dealing with people who have very specific needs and it can be very hard to work your way through Rails' naming conventions.

Huh?

Eager loading, for example, is something you will really have to stumble across in a book.

Nope (as evidenced by the fact that I've never read a paper Rails book...). It's mentioned in a number of places, including the AR associations docs.

And when you know the term, you know where to find it and how to use it. Sometimes you just have to assume certain things and let others correct you if you're wrong.

Yup. That's often a great way to learn. [...]

I've used joins in the past (in my PHP era), but I have a feeling that it doesn't get me the results I want.

I think you're wrong about that.

The docs mentioned that :joins can be called the same way as :include. Will this work? And will it trigger just one query?

I believe so. But why not try it and watch the generated SQL?

@project = Project.find(:first, :conditions => { :id => params [:id] }, :joins => [:floors) @floors = @project.floors

My guess is that it's not that simple.

Why not? And why are you guessing when you could just try it?

Especially because what I understand from the MySQL docs, INNER JOIN or CROSS JOIN will generate a new table where rows with matching keys are combined and output its contents.

That's what a join is. As far as I can tell, that's what you need. What's the problem?

Or would the above work? And would it also work for more complicated examples like the one I posted at 0:27 today?

I'll look at that other example in more depth.

Remember: the DB is good at doing sophisticated mass data manipulations quickly. Use it!

Best,

Hi--

Well, in that spirit, allow me to digress and mention that you have
run across one of the greatest strengths of Rails: Its strong,
supportive community. Some of the people who have contributed to this
thread (Fred, Marnen, Mauricio) give freely of their time and make
Rails a great "place" to work. They are the perfect people to have
engaged in such a discussion.

True. I hope I've been clear on appreciating this.

Huh?

You may not feel it that way, I'm not sure why I do, but I do.

Nope (as evidenced by the fact that I've never read a paper Rails book...). It's mentioned in a number of places, including the AR associations docs.

True, but I've still looked over it. I usually scan the pages I think I'd need, but alot of times I miss it, mainly because I'm not entirely sure what to look for. I'm coming from PHP and am very excited about Rails, but these things can be hard if you're getting started.

> The docs mentioned that :joins can be called the same way as :include. > Will this work? And will it trigger just one query?

I believe so. But why not try it and watch the generated SQL?

I did. It did generate an INNER JOIN query and still made a seperate request to the floors table. Nothing changed, except for my query being longer and getting results it doesn't use. I've tried a few other things to stop @floors = @project.floors to generate a new query, but I'm unsure where to look.

> @project = Project.find(:first, :conditions => { :id => params > [:id] }, :joins => [:floors) > @floors = @project.floors

> My guess is that it's not that simple.

Why not? And why are you guessing when you could just try it?

> Especially because what I > understand from the MySQL docs, INNER JOIN or CROSS JOIN will generate > a new table where rows with matching keys are combined and output its > contents.

That's what a join is. As far as I can tell, that's what you need. What's the problem?

Is it really what I need? When I have a project, with it's floors, with the active floor's rooms, with the furniture etc etc, won't a join just be very confusing? I'm not even sure whether I want a single database request, that's also what this discussion is for. How would you do this and why?

> Or would the above work? And would it also work for more > complicated examples like the one I posted at 0:27 today?

I'll look at that other example in more depth.

Remember: the DB is good at doing sophisticated mass data manipulations quickly. Use it!

I know, that's what I'm trying to do. But it's hard to find things if you don't know where to look.

Thank you.