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.