Split the query for counting terms in the filter by attributes widget.

For performance reasons the query is split in two: one for simple
products and variations with a concrete attribute value, and another
one for variations having "Any..." as the attribute value.
This commit is contained in:
Nestor Soriano 2020-09-02 16:22:25 +02:00
parent 07b62dabbd
commit 417bcf8fff
1 changed files with 40 additions and 16 deletions

View File

@ -430,18 +430,8 @@ class WC_Widget_Layered_Nav extends WC_Widget {
)
{$main_tax_query_sql['where']}";
// Generate the final query as the union+count of both.
$query_sql = "
SELECT COUNT(DISTINCT(product_id)) AS term_count, term_count_id FROM (
{$main_query_sql}
UNION ALL
{$query_sql_for_attributes_with_any_value}
) AS x GROUP BY term_count_id
";
// We have a query - let's see if cached results of this query already exist.
$query_hash = md5( $query_sql );
// We have two queries - let's see if cached results of this query already exist.
$query_hash = md5( $main_query_sql . $query_sql_for_attributes_with_any_value );
// Maybe store a transient of the count values.
$cache = apply_filters( 'woocommerce_layered_nav_count_maybe_cache', true );
@ -452,19 +442,53 @@ class WC_Widget_Layered_Nav extends WC_Widget {
}
if ( ! isset( $cached_counts[ $query_hash ] ) ) {
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
$results = $wpdb->get_results( $query_sql, ARRAY_A );
$counts = array_map( 'absint', wp_list_pluck( $results, 'term_count', 'term_count_id' ) );
$counts = $this->get_term_product_counts_from_queries( $main_query_sql, $query_sql_for_attributes_with_any_value );
$cached_counts[ $query_hash ] = $counts;
if ( true === $cache ) {
set_transient( 'wc_layered_nav_counts_' . sanitize_title( $taxonomy ), $cached_counts, DAY_IN_SECONDS );
}
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
}
return array_map( 'absint', (array) $cached_counts[ $query_hash ] );
}
/**
* Get the count of terms for products, using a set of SQL queries that are return pairs of product id - term id.
*
* @param string ...$queries SQL queries to use, each must return a "product_id" column and a "terms_count_id" column.
*
* @return array An array where the keys are term ids, and the values are term counts.
*/
private function get_term_product_counts_from_queries( ...$queries ) {
global $wpdb;
$total_counts = null;
foreach ( $queries as $query ) {
$query = "
SELECT COUNT(DISTINCT(product_id)) AS term_count, term_count_id FROM (
{$query}
) AS x GROUP BY term_count_id";
$results = $wpdb->get_results( $query, ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$counts = array_map( 'absint', wp_list_pluck( $results, 'term_count', 'term_count_id' ) );
if ( is_null( $total_counts ) ) {
$total_counts = $counts;
} else {
foreach ( $counts as $term_id => $term_count ) {
if ( array_key_exists( $term_id, $total_counts ) ) {
$total_counts[ $term_id ] += $term_count;
} else {
$total_counts[ $term_id ] = $term_count;
}
}
}
}
return $total_counts;
}
/**
* Wrapper for WC_Query::get_main_tax_query() to ease unit testing.
*