web-dev-qa-db-ja.com

2組のフィルターを使用した製品クエリのフィルタリング。 (J2Store)

分離した2つの製品フィルターセット$special_filters$other_filtersがあります。 $special_filtersのいずれかのフィルターと$other_filtersのいずれかのフィルターに一致する製品をデータベースから取得しようとしています。たとえば、$special_filters = {'New'}および$other_filters = {'Brakes', 'Tires'}の場合、すべての新しい部品、すべてのブレーキ、およびすべてのタイヤではなく、すべての新しいブレーキおよびすべての新しいタイヤを入手したいと考えています。これが私がphpMyAdminで機能させたものです(ここでproduct_filtersテーブルはproduct_idfilter_idを関連付ける2列のテーブルです:

SELECT q.product_id FROM (
    SELECT product_id FROM `<prefix>_j2store_product_filters`
    WHERE filter_id = 'tag1'
) AS q
INNER JOIN `<prefix>_j2store_product_filters` AS t ON q.product_id = t.product_id
WHERE filter_id IN ('tag2', 'tag3')

これにより、tag1、およびtag2またはtag3のいずれかを含むすべての製品が正常に検出されます。しかし、Joomlaへの翻訳が機能していないようです。

administrator/components/com_j2store/models/products.php関数の終わり近くにある_sfBuildWhereQueryのJ2Storeのコードを変更しています。これは、製品のリストを取得するJDatabaseクエリオブジェクトを設定しています。コードのこの時点で、アクティブなすべてのフィルターを含み、2つのフィルター配列の和集合である$filter_ids配列があります。適切な "WHERE"句を現在のクエリオブジェクトに追加する必要があります。私は基本的に自分のSQLコードをJDatabaseに変換しようとしました。これが私の試みです:

$other_ids = array_diff($filter_ids, $special_filters);
if (empty($special_filters) || empty($other_ids)) {
   // This is the usual case for the 'OR' operator.
   $query->where ( '#__j2store_product_filters.filter_id IN (' 
                   . implode ( ',', $filter_ids ) . ')' );
} else {
    // Make sub-sub-query to filter with special filters
    $subSubQuery = $db->getQuery(True);
    $subSubQuery
        ->select($db->quoteName('product_id'))
        ->from($db->quoteName('#__j2store_product_filters'))
        ->where($db->quoteName('filter_id') 
                . ' IN (' . implode(',', $special_filters) . ')');

    // Filter the result with the other filters in another subquery
    $subQuery = $db->getQuery(True);
    $subQuery
        ->select($db->quoteName('s.product_id'))
        ->from($subSubQuery, 's')
        ->join('INNER', 
                $db->quoteName('#__j2store_product_filters', 'f') 
                . ' ON (' . $db->quoteName('s.product_id') 
                          . ' = ' . $db->quoteName('f.product_id') . ')')
        ->where($db->quoteName('filter_id') 
                . ' IN (' . implode(',', $other_ids) . ')');

     $query->where('EXISTS (SELECT * FROM (' . $subQuery->__toString() . ') AS t 
                   WHERE ' . $db->quoteName('#__j2store_product_filters.product_id') . 
                    ' = ' . $db->quoteName('t.product_id') . ')');
}

ただし、結果が多すぎます。新しいブレーキと新しいタイヤだけでなく、すべての新しい部品、すべてのブレーキ、すべてのタイヤがまだ返されています。

これは私が必要とする条件付きロジックのデモです:db-fiddle.com/f/o1UWTKXVTxxwyvrpcupmqu/3

1
William

GROUP BY itemのほうが適しています。次に、HAVING句を使用して、集計データに対して条件付きのid_filtersspecial_filters、およびother_filters条件を実行します。

Db-fiddleデモについて...

SELECT
    item
FROM item_tags
GROUP BY item
HAVING
    MAX(CASE WHEN tag IN ('Used') THEN 1 ELSE NULL END)
    AND MAX(CASE WHEN tag IN ('Brake') THEN 1 ELSE NULL END)

NULLの結果が「偽」であるのに対して、満足のいく結果は「真実」でなければならないため、1は単に使用されます。両方の条件が非nullと評価された場合、対象となるitemがあります。簡単できれい。

さて、マイクロ最適化が必要な場合は、2つのフィルタリングリストを1つの文字列にマージして追加すると、次のような利点があります。

WHERE tag IN ($all_csv_filters_as_one)

ベンチマークするための現実的なデータがなかったので、この追加の句は調査しませんでした(そして、そうすることにあまり関心がありません)。これはHAVING句を置き換えるのではなく、「集約」データを事前にフィルタリングするので、HAVINGはそれほど多くのことを調べる必要がないことに注意してください。

J2storeクエリのJoomlaクエリビルダーの構文は次のようになります。

$query = $db->getQuery(true)
    ->select("product_id")
    ->from("#__j2store_product_filters")
    ->group("product_id")
    ->having([
        "MAX(CASE WHEN filter_id IN ({$filter_ids}) THEN 1 ELSE NULL END)",
        "MAX(CASE WHEN filter_id IN ({$special_filter_ids}) THEN 1 ELSE NULL END")
    ]);

pS.

それぞれのフィルターが空の文字列でないかどうかに応じて、条件付きでさらにHAVING句を追加する場合は、1つ以上のfilter_id値を含めるたびに、条件付きで新しいHAVINGメソッドを呼び出すことができます。このような...

// build $query with all of the other clauses then after ->group()...
if ($filter_ids)
{
    $query->having("MAX(CASE WHEN filter_id IN ({$filter_ids}) THEN 1 ELSE NULL END)");
}
if ($special_ids)
{
    $query->having("MAX(CASE WHEN filter_id IN ({$special_ids}) THEN 1 ELSE NULL END)");
}
if ($other_ids)
{
    $query->having("MAX(CASE WHEN filter_id IN ({$other_ids}) THEN 1 ELSE NULL END)");
}
1
mickmackusa

私の問題は実際にはSQLにありませんでした。さらにデバッグを行ったところ、「if empty」句しか入力していないことがわかりました。そのため、デフォルトの動作を行っていました。私の問題は、変数を理解していないことでした

$filter_ids = $state->productfilter_id;

配列だと思いましたが、実はカンマ区切りの文字列です!それを考慮に入れると、私のJoomlaコードは機能しました。

この質問は削除できると思いますが、一方で、上記のコードはこの特定の問題の解決策を提供します。

0
William