From aa967cab4f4e90616aca4d2cb4f450579d8cc21a Mon Sep 17 00:00:00 2001 From: Nestor Soriano Date: Mon, 12 Jul 2021 10:32:14 +0200 Subject: [PATCH] Optimize the query to count products using the attributes lookup table. The query to count products using the attributes lookup table for the filter by attribute widget was adding an "AND term_ids in (...)"-type subquery for each attribute participating in the filtering. Now at most two such subqueries are generated, one for attributes configured for OR type filtering and another one for the ones configured as AND; this speeds up the query significantly when many attributes are used simultaneously for the filtering. --- .../ProductAttributesLookup/Filterer.php | 75 +++++++++++-------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/src/Internal/ProductAttributesLookup/Filterer.php b/src/Internal/ProductAttributesLookup/Filterer.php index 4971a5b1d3c..6f34dc02b7a 100644 --- a/src/Internal/ProductAttributesLookup/Filterer.php +++ b/src/Internal/ProductAttributesLookup/Filterer.php @@ -213,42 +213,51 @@ class Filterer { $attributes_to_filter_by = \WC_Query::get_layered_nav_chosen_attributes(); if ( ! empty( $attributes_to_filter_by ) ) { - $all_terms_to_filter_by = array(); - foreach ( $attributes_to_filter_by as $taxonomy => $data ) { - $all_terms = get_terms( $taxonomy, array( 'hide_empty' => false ) ); - $term_ids_by_slug = wp_list_pluck( $all_terms, 'term_id', 'slug' ); - $term_ids_to_filter_by = array_values( array_intersect_key( $term_ids_by_slug, array_flip( $data['terms'] ) ) ); - $all_terms_to_filter_by = array_merge( $all_terms_to_filter_by, $term_ids_to_filter_by ); - $term_ids_to_filter_by_list = '(' . join( ',', $term_ids_to_filter_by ) . ')'; + $and_term_ids = array(); + $or_term_ids = array(); - $count = count( $term_ids_to_filter_by ); - if ( 0 !== $count ) { - $query['where'] .= ' AND product_or_parent_id IN ('; - if ( 'and' === $attributes_to_filter_by[ $taxonomy ]['query_type'] ) { - $query['where'] .= " - SELECT product_or_parent_id - FROM {$this->lookup_table_name} lt - WHERE is_variation_attribute=0 - {$in_stock_clause} - AND term_id in {$term_ids_to_filter_by_list} - GROUP BY product_id - HAVING COUNT(product_id)={$count} - UNION - SELECT product_or_parent_id - FROM {$this->lookup_table_name} lt - WHERE is_variation_attribute=1 - {$in_stock_clause} - AND term_id in {$term_ids_to_filter_by_list} - )"; - } else { - $query['where'] .= " - SELECT product_or_parent_id FROM {$this->lookup_table_name} - WHERE term_id in {$term_ids_to_filter_by_list} - {$in_stock_clause} - )"; - } + foreach ( $attributes_to_filter_by as $taxonomy => $data ) { + $all_terms = get_terms( $taxonomy, array( 'hide_empty' => false ) ); + $term_ids_by_slug = wp_list_pluck( $all_terms, 'term_id', 'slug' ); + $term_ids_to_filter_by = array_values( array_intersect_key( $term_ids_by_slug, array_flip( $data['terms'] ) ) ); + if ( 'and' === $data['query_type'] ) { + $and_term_ids = array_merge( $and_term_ids, $term_ids_to_filter_by ); + } else { + $or_term_ids = array_merge( $or_term_ids, $term_ids_to_filter_by ); } } + + if ( ! empty( $and_term_ids ) ) { + $terms_count = count( $and_term_ids ); + $term_ids_list = '(' . join( ',', $and_term_ids ) . ')'; + $query['where'] .= " + AND product_or_parent_id IN ( + SELECT product_or_parent_id + FROM {$this->lookup_table_name} lt + WHERE is_variation_attribute=0 + {$in_stock_clause} + AND term_id in {$term_ids_list} + GROUP BY product_id + HAVING COUNT(product_id)={$terms_count} + UNION + SELECT product_or_parent_id + FROM {$this->lookup_table_name} lt + WHERE is_variation_attribute=1 + {$in_stock_clause} + AND term_id in {$term_ids_list} + )"; + } + + if ( ! empty( $or_term_ids ) ) { + $term_ids_list = '(' . join( ',', $or_term_ids ) . ')'; + $query['where'] .= " + AND product_or_parent_id IN ( + SELECT product_or_parent_id FROM {$this->lookup_table_name} + WHERE term_id in {$term_ids_list} + {$in_stock_clause} + )"; + + } } else { $query['where'] .= $in_stock_clause; }