Byron Young wrote:
> Peter Rabbitson wrote on 2009-05-29:
>> Peter Rabbitson wrote:
>>> Byron Young wrote:
>>>> Hey all,
>>>>
>>>> Sorry if this is a FAQ, but I couldn't find any info in the cookbook
>>>> or the joining manual or other docs.
>>>>
>>>> I have a table for test results, 'results', that has two paths
>> through relationships to join to another table for hostnames,
>> 'hostnames', like so:
>>>> results --> map_results_tested_products --> tested_product -->
>>>> hostname results --> map_results_tested_hosts --> tested_host
>>>> -->
>> hostname
>>>> If I want to search all results for tests that tested a product with
>>>> hostname 'XXXXX', the query looks like:
>>>>
>>>> $schema->resultset("Results")->search({ "hostname.name" =>
>> "XXXXX" },
>>>> { join => {
>> map_results_tested_products => { tested_product => 'hostname' } }
>> });
>>>> However, if I want to chain that with another search so I get
>> all results for tests that tested a product with hostname 'XXXXX'
>> and included a host with hostname 'YYYYY', I have to use
>> 'hostname_2' in the search condition for the second query, since
>> 'hostname' has already been joined:
>>>> $schema->resultset("Results")->search({ "hostname.name" =>
>> 'XXXXX' },
>>>> { join => {
>> map_results_tested_products => { tested_product => "hostname" } }
>> })
>>>> ->search({ "hostname_2.name" =>
>> 'YYYYY' },
>>>> { join => {
>> map_results_tested_hosts => { tested_host => "hostname" } } });
>>>> This is fine when the whole query is exposed in code like this,
>> but the problem comes when I'm chaining searches on resultsets that
>> come from somewhere else. For example, if I add a custom resultset
>> method for the first search, the person writing the chained search
>> doesn't necessarily know that 'hostname' has already been joined
>> once and that they should append '_2':
>>>> $schema->resultset("Results")->tested_product_name("XXXXX")
>>>> ->search({ "hostname.name" =>
>> 'YYYYY' },
>>>> { join => {
>> map_results_tested_hosts => { tested_host => "hostname" } } });
>>>> The above looks right to the coder but wouldn't work how you
>> would expect from just looking at it, because it would search on
>> the wrong joined 'hostname' table.
>>>> The way this is coming up for me is that I have written a simple
>> list filtering system for my Catalyst app, where my Controller
>> classes can provide info about the where conditions and joins for
>> the filters in the controller config, and my List base controller
>> class chains the filters that the user selected into a query and
>> displays the filtered list.
>>>> What I've done to get around it is to inspect the 'join'
>> attributes and use a %seen hash to count the number of times a
>> table has been joined, and then fix up the where condition
>> appropriately. It works, but the code is more convoluted than I
>> would like, and I'm worried that by messing with my queries like
>> this I'm relying on some internal DBIC behavior that may change at
>> some point.
>>>> Is there a better way to solve this issue so that I can chain queries
>>>> that don't collide even if they join to the same table?
>>>>
>>> This is definitely not a faq, and concerns one of the hairier parts of
>>> DBIC. Since 0.08103 DBIC can do the counting for you properly, but with
>>> the caveat that you are accessing internal methods which can change
>>> without notice:
>>>
>>> my $aliases = $rs->_joinpath_aliases ($rs->_resolved_attrs-
>>> {from});
>>>
>>> If you Data::Dumper the resulting hashref you will see that the answer
>>> to your question is then as simple as:
>>>
>>> my $alias = $aliases-
>>> {map_results_tested_hosts}{tested_host}{hostname};
>>>
>> Sorry, my bad. What I sent will not work correctly if you have
>> ->search_related calls in your chain. The full code is:
>>
>> my $rattr = $rs->_resolved_attrs_copy;
>> my $aliases = $rs->_joinpath_aliases ($rattr->{from}, $rattr-
>>> {seen_join});
>
> Thanks for the help, Peter. I'll look into upgrading at some point so I can
> get those features. I'm still running 0.08010.
>
> I'm curious, though - why can't search() do this internally with the search
> conditions? If it sees 'table.column' in the condition and it knows it's
> joining to 'table' and that 'table' will have an alias 'table_X', couldn't it
> update the search condition and do that work behind the scenes?
>
> If it's something that could be done but nobody has had the time or
> inclination, I'd be happy to offer a patch provided I can get some guidance
> with the DBIC internals. However, I'm going to guess that there are lots of
> implications to a change like this that I'm not aware of, and that it's
> probably been thought through and dumped a few times already.
>
Because how does search() know that you want to search on the
join you just supplied or on the join that appeared in a
search() before? Instead of making assumptions, dbic does
exactly what you told it to.
_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/[email protected]