Many-to-Many relationship problem (ALL subquery?)

Login or register to post comments
7 replies [Last post]
Offline
Joined: 07/17/2008

Basic layout

Resource
IdResource
Name

Keyword
IdKeyword
Name

There is a many-to-many association table connecting the two.

Problem:
The keywords are used as filters. A user can select any number of keywords and it should filter the results such that each resource has ALL of the keywords selected associated (and any others not selected - the important point is that all that are selected are associated).

I can implement in PHP manipulating arrays ... but that's *really* slow when dealing with thousands of resources.

I essentially want to do:
QQ::In_All(QQN::Resource()->Keyword->Keyword->IdKeyword, $arrSelectedKeywords) ... something like an SQL subquery with the ALL operator such that it will “return TRUE if the comparison is TRUE for ALL of the values in the column that the subquery returns.”

Any ideas? It's probably really simple - I'm just a bit perplexed.

Thanks,
Andrew

Offline
Joined: 03/31/2008

Well, currently there's no way to do this. But! We've got it built and ready to go:

1) Apply the patch in http://trac.qcu.be/projects/qcubed/ticket/321

2)

$conditions = QQ::All();
foreach($keywordArr as $keywordId)
{
  $conditions = QQ::AndCondition($conditions,
    QQ::Equal(QQ::Alias(QQN::Resource()->Keywords, 'k'.$keywordId)->KeywordId, $keywordId)
    );
}
$resources = Resource::QueryArray($conditions);

Offline
Joined: 07/17/2008

With the posted solution, I get SQL similar to what is found below. I, unfortunately, don't get any records returned...

I feel this is probably due to the fact that `t1`.`Keyword_idKeyword` can't be both 50 and 7 at the same time. The left-join is certainly down the right path ... getting a single set of resources from 't1' is the problem.

SELECT
    COUNT(*) AS q_row_count
FROM
    `Resource` AS `t0`
    LEFT JOIN `Resource_Keyword_assn` AS `t1` ON `t0`.`idResource` = `t1`.`Resource_idResource`
WHERE(
    (1=1 AND `t1`.`Keyword_idKeyword` = 50)
  AND
    `t1`.`Keyword_idKeyword` = 7)

Offline
Joined: 03/31/2008

This is what the alias is intended to prevent from happening. It shouldn't be joining Resource_Keyword_assn as `t1`, it should be joining it once as k50 and once as k7.

Are you sure you're using the code I supplied?

Offline
Joined: 07/17/2008

Yessir (using the 2nd version of your patch, too) - the patch only modifies QQuery.class.php, correct?:

$conditions = QQ::All();
foreach($arrSelectedKeywords as $keyword)
{
  $conditions =
  QQ::AndCondition(
  $conditions,
    QQ::Equal(QQ::Alias(QQN::Resource()->Keyword, 'k'.$keyword->IdKeyword)->KeywordIdKeyword, $keyword->IdKeyword)
);    
}

$this->dtrResource->DataSource = Resource::QueryArray(
$conditions,
                QQ::Clause(
                    $this->dtrResource->LimitClause                  
                )
            );

$this->dtrResource->TotalItemCount =
                Resource::QueryCount(
$conditions
                );

Offline
Joined: 03/31/2008

Huh. That sure looks right to me. But there should be no way for that to produce SQL without a table aliased as k-something. (You're effectively overriding the t1 alias with a k-something one.)

Have you tried this on the example database, to ensure the example page provided in that ticket works as expected?

It's behaving like you didn't change all the strName to strAlias in QQuery, or that you never set strAlias, which you clearly did.

Offline
Joined: 07/17/2008

Yes ... my mistake. Two strName entries weren't changed and now it works. I should've double checked.

Thanks a million!
-Andrew

Offline
Joined: 03/31/2008

Glad it works! I think you're the first other person to test that for me. :) Hopefully it'll be commited to core soon. :)