From: Fitz Elliott Date: Thu, 21 Apr 2011 17:19:13 +0000 (-0400) Subject: prefetch docs: prefetch works on all rel types X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=307f12659a1a370e5e41a23ef35839e2807f1ca4;p=dbsrgits%2FDBIx-Class-Historic.git prefetch docs: prefetch works on all rel types Update the prefetch docs to state that prefetch works with all relationship types, where previously it had said that it works with only belongs_to and has_one. Also update the subsequent example. The old example was confusing because it specified two has_many relationships on the same level, something that DBIC emits a warning about. That example is replaced with a CD-centric example that demonstrates how many different relationship types can be used together in a prefetch, but omits the multiple has_manys. Add a warning, example, and explanation of the troubles with using multiple has_many relations in a prefetch. The old text incorrectly stated that prefetch will override join and select attributes. Describe current behavior with examples. --- diff --git a/lib/DBIx/Class.pm b/lib/DBIx/Class.pm index cc63fab..184227f 100644 --- a/lib/DBIx/Class.pm +++ b/lib/DBIx/Class.pm @@ -334,6 +334,8 @@ dwc: Daniel Westermann-Clark dyfrgi: Michael Leuchtenburg +felliott: Fitz Elliott + freetime: Bill Moseley frew: Arthur Axel "fREW" Schmidt diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index fc6bdd0..5c87ffa 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -3966,28 +3966,122 @@ case. Simple prefetches will be joined automatically, so there is no need for a C attribute in the above search. -C can be used with the following relationship types: C, -C (or if you're using C, any relationship declared -with an accessor type of 'single' or 'filter'). A more complex example that -prefetches an artists cds, the tracks on those cds, and the tags associated -with that artist is given below (assuming many-to-many from artists to tags): +L can be used with the any of the relationship types and +multiple prefetches can be specified together. Below is a more complex +example that prefetches a CD's artist, its liner notes (if present), +the cover image, the tracks on that cd, and the guests on those +tracks. - my $rs = $schema->resultset('Artist')->search( + # Assuming: + My::Schema::CD->belongs_to( artist => 'My::Schema::Artist' ); + My::Schema::CD->might_have( liner_note => 'My::Schema::LinerNotes' ); + My::Schema::CD->has_one( cover_image => 'My::Schema::Artwork' ); + My::Schema::CD->has_many( tracks => 'My::Schema::Track' ); + + My::Schema::Artist->belongs_to( record_label => 'My::Schema::RecordLabel' ); + + My::Schema::Track->has_many( guests => 'My::Schema::Guest' ); + + + my $rs = $schema->resultset('CD')->search( undef, { prefetch => [ - { cds => 'tracks' }, - { artist_tags => 'tags' } + { artist => 'record_label'}, # belongs_to => belongs_to + 'liner_note', # might_have + 'cover_image', # has_one + { tracks => 'guests' }, # has_many => has_many ] } ); +This will produce SQL like the following: + + SELECT cd.*, artist.*, record_label.*, liner_note.*, cover_image.*, + tracks.*, guests.* + FROM cd me + JOIN artist artist + ON artist.artistid = me.artistid + JOIN record_label record_label + ON record_label.labelid = artist.labelid + LEFT JOIN track tracks + ON tracks.cdid = me.cdid + LEFT JOIN guest guests + ON guests.trackid = track.trackid + LEFT JOIN liner_notes liner_note + ON liner_note.cdid = me.cdid + JOIN cd_artwork cover_image + ON cover_image.cdid = me.cdid + ORDER BY tracks.cd + +Now the C, C, C, C, +C, and C of the CD will all be available through the +relationship accessors without the need for additional queries to the +database. + +However, there is one caveat to be observed: it can be dangerous to +prefetch more than one L +relationship on a given level. e.g.: + + my $rs = $schema->resultset('CD')->search( + undef, + { + prefetch => [ + 'tracks', # has_many + { cd_to_producer => 'producer' }, # has_many => belongs_to (i.e. m2m) + ] + } + ); + +In fact, C will emit the following warning: + + Prefetching multiple has_many rels tracks and cd_to_producer at top + level will explode the number of row objects retrievable via ->next + or ->all. Use at your own risk. + +The collapser currently can't identify duplicate tuples for multiple +L relationships and as a +result the second L +relation could contain redundant objects. + +=head3 Using L with L + +L implies a L with the equivalent argument, and is +properly merged with any existing L specification. So the +following: + + my $rs = $schema->resultset('CD')->search( + {'record_label.name' => 'Music Product Ltd.'}, + { + join => {artist => 'record_label'}, + prefetch => 'artist', + } + ); + +... will work, searching on the record label's name, but only +prefetching the C. + +=head3 Using L with L / L / L / L + +L implies a L/L with the fields of the +prefetched relations. So given: + + my $rs = $schema->resultset('CD')->search( + undef, + { + select => ['cd.title'], + as => ['cd_title'], + prefetch => 'artist', + } + ); + +The L becomes: C<'cd.title', 'artist.*'> and the L +becomes: C<'cd_title', 'artist.*'>. -B If you specify a C attribute, the C and C