Make WordPress Core


Ignore:
Timestamp:
03/10/2022 10:56:09 AM (2 years ago)
Author:
spacedmonkey
Message:

Taxonomy: Only store term_ids and object_ids in WP_Term_Query query caches.

The query cache currently implemented in WP_Term_Query caches the final output of the query, depending on what fields are requested. This is wasteful, as if a user requests fields => all, then an unlimited array of WP_Term objects could be stored in the object cache. Instead of storing the whole WP_Term object, this change only the term_id is stored. To get an array the full WP_Term objects, the _prime_term_caches function is called with an array of ids. In instances where a persistent object cache is not in use, then this will result in another SQL query to be run. After _prime_term_caches is called if this term is requested again in the same page load, then it will already be loaded into memory. If a user runs WP_Term_Query with the fields param set to all_with_object_id, an array of objects containing both the term_id and object_ids are stored in cache.

This change also improves the logic to load term meta caches. This change ensures that term meta is always primed for all terms loaded in the term query.

Props Spacedmonkey, boonebgorges, jbpaul17, peterwilsoncc, flixos90, pbearne.
Fixes #37189.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-term-query.php

    r52669 r52836  
    637637        $selects = array();
    638638        switch ( $args['fields'] ) {
    639             case 'all':
    640             case 'all_with_object_id':
    641             case 'tt_ids':
    642             case 'slugs':
    643                 $selects = array( 't.*', 'tt.*' );
    644                 if ( 'all_with_object_id' === $args['fields'] && ! empty( $args['object_ids'] ) ) {
    645                     $selects[] = 'tr.object_id';
    646                 }
    647                 break;
    648             case 'ids':
    649             case 'id=>parent':
    650                 $selects = array( 't.term_id', 'tt.parent', 'tt.count', 'tt.taxonomy' );
    651                 break;
    652             case 'names':
    653                 $selects = array( 't.term_id', 'tt.parent', 'tt.count', 't.name', 'tt.taxonomy' );
    654                 break;
    655639            case 'count':
    656640                $orderby = '';
     
    658642                $selects = array( 'COUNT(*)' );
    659643                break;
    660             case 'id=>name':
    661                 $selects = array( 't.term_id', 't.name', 'tt.parent', 'tt.count', 'tt.taxonomy' );
    662                 break;
    663             case 'id=>slug':
    664                 $selects = array( 't.term_id', 't.slug', 'tt.parent', 'tt.count', 'tt.taxonomy' );
     644            default:
     645                $selects = array( 't.term_id' );
     646                if ( 'all_with_object_id' === $args['fields'] && ! empty( $args['object_ids'] ) ) {
     647                    $selects[] = 'tr.object_id';
     648                }
    665649                break;
    666650        }
     
    689673
    690674        if ( ! empty( $this->query_vars['object_ids'] ) ) {
    691             $join .= " INNER JOIN {$wpdb->term_relationships} AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id";
     675            $join    .= " INNER JOIN {$wpdb->term_relationships} AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id";
     676            $distinct = 'DISTINCT';
    692677        }
    693678
     
    749734        $cache        = wp_cache_get( $cache_key, 'terms' );
    750735        if ( false !== $cache ) {
    751             if ( 'all' === $_fields || 'all_with_object_id' === $_fields ) {
    752                 $cache = $this->populate_terms( $cache );
     736            if ( 'ids' === $_fields ) {
     737                $term_ids = wp_list_pluck( $cache, 'term_id' );
     738                $cache    = array_map( 'intval', $term_ids );
     739            } elseif ( 'count' !== $_fields ) {
     740                $term_ids = wp_list_pluck( $cache, 'term_id' );
     741                _prime_term_caches( $term_ids, $args['update_term_meta_cache'] );
     742                $term_objects = $this->populate_terms( $cache );
     743                $cache        = $this->format_terms( $term_objects, $_fields );
    753744            }
    754745
     
    758749
    759750        if ( 'count' === $_fields ) {
    760             $count = $wpdb->get_var( $this->request );
     751            $count = $wpdb->get_var( $this->request ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
    761752            wp_cache_set( $cache_key, $count, 'terms' );
    762753            return $count;
    763754        }
    764755
    765         $terms = $wpdb->get_results( $this->request );
    766 
    767         if ( 'all' === $_fields || 'all_with_object_id' === $_fields ) {
    768             update_term_cache( $terms );
    769         }
    770 
    771         // Prime termmeta cache.
    772         if ( $args['update_term_meta_cache'] ) {
    773             $term_ids = wp_list_pluck( $terms, 'term_id' );
    774             update_termmeta_cache( $term_ids );
    775         }
     756        $terms = $wpdb->get_results( $this->request ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
    776757
    777758        if ( empty( $terms ) ) {
     
    779760            return array();
    780761        }
     762
     763        $term_ids = wp_list_pluck( $terms, 'term_id' );
     764        _prime_term_caches( $term_ids, false );
     765        $term_objects = $this->populate_terms( $terms );
    781766
    782767        if ( $child_of ) {
     
    784769                $children = _get_term_hierarchy( $_tax );
    785770                if ( ! empty( $children ) ) {
    786                     $terms = _get_term_children( $child_of, $terms, $_tax );
     771                    $term_objects = _get_term_children( $child_of, $term_objects, $_tax );
    787772                }
    788773            }
     
    792777        if ( $args['pad_counts'] && 'all' === $_fields ) {
    793778            foreach ( $taxonomies as $_tax ) {
    794                 _pad_term_counts( $terms, $_tax );
     779                _pad_term_counts( $term_objects, $_tax );
    795780            }
    796781        }
    797782
    798783        // Make sure we show empty categories that have children.
    799         if ( $hierarchical && $args['hide_empty'] && is_array( $terms ) ) {
    800             foreach ( $terms as $k => $term ) {
     784        if ( $hierarchical && $args['hide_empty'] && is_array( $term_objects ) ) {
     785            foreach ( $term_objects as $k => $term ) {
    801786                if ( ! $term->count ) {
    802787                    $children = get_term_children( $term->term_id, $term->taxonomy );
     
    811796
    812797                    // It really is empty.
    813                     unset( $terms[ $k ] );
     798                    unset( $term_objects[ $k ] );
    814799                }
    815800            }
     
    837822        }
    838823
    839         $_terms = array();
    840         if ( 'id=>parent' === $_fields ) {
    841             foreach ( $terms as $term ) {
    842                 $_terms[ $term->term_id ] = $term->parent;
    843             }
    844         } elseif ( 'ids' === $_fields ) {
    845             foreach ( $terms as $term ) {
    846                 $_terms[] = (int) $term->term_id;
    847             }
    848         } elseif ( 'tt_ids' === $_fields ) {
    849             foreach ( $terms as $term ) {
    850                 $_terms[] = (int) $term->term_taxonomy_id;
    851             }
    852         } elseif ( 'names' === $_fields ) {
    853             foreach ( $terms as $term ) {
    854                 $_terms[] = $term->name;
    855             }
    856         } elseif ( 'slugs' === $_fields ) {
    857             foreach ( $terms as $term ) {
    858                 $_terms[] = $term->slug;
    859             }
    860         } elseif ( 'id=>name' === $_fields ) {
    861             foreach ( $terms as $term ) {
    862                 $_terms[ $term->term_id ] = $term->name;
    863             }
    864         } elseif ( 'id=>slug' === $_fields ) {
    865             foreach ( $terms as $term ) {
    866                 $_terms[ $term->term_id ] = $term->slug;
    867             }
    868         }
    869 
    870         if ( ! empty( $_terms ) ) {
    871             $terms = $_terms;
    872         }
    873 
    874824        // Hierarchical queries are not limited, so 'offset' and 'number' must be handled now.
    875825        if ( $hierarchical && $number && is_array( $terms ) ) {
    876826            if ( $offset >= count( $terms ) ) {
    877                 $terms = array();
     827                $terms        = array();
     828                $term_objects = array();
    878829            } else {
    879                 $terms = array_slice( $terms, $offset, $number, true );
    880             }
     830                $terms        = array_slice( $terms, $offset, $number, true );
     831                $term_objects = array_slice( $term_objects, $offset, $number, true );
     832            }
     833        }
     834
     835        // Prime termmeta cache.
     836        if ( $args['update_term_meta_cache'] ) {
     837            $term_ids = wp_list_pluck( $term_objects, 'term_id' );
     838            update_termmeta_cache( $term_ids );
    881839        }
    882840
    883841        wp_cache_add( $cache_key, $terms, 'terms' );
    884 
    885         if ( 'all' === $_fields || 'all_with_object_id' === $_fields ) {
    886             $terms = $this->populate_terms( $terms );
    887         }
     842        $terms = $this->format_terms( $term_objects, $_fields );
    888843
    889844        $this->terms = $terms;
     
    951906
    952907    /**
     908     * Format response depending on field requested.
     909     *
     910     * @since 6.0.0
     911     *
     912     * @param WP_Term[] $term_objects Array of term objects.
     913     * @param string $_fields Field to format.
     914     *
     915     * @return WP_Term[]|int[]|string[] Array of terms / strings / ints depending on field requested.
     916     */
     917    protected function format_terms( $term_objects, $_fields ) {
     918        $_terms = array();
     919        if ( 'id=>parent' === $_fields ) {
     920            foreach ( $term_objects as $term ) {
     921                $_terms[ $term->term_id ] = $term->parent;
     922            }
     923        } elseif ( 'ids' === $_fields ) {
     924            foreach ( $term_objects as $term ) {
     925                $_terms[] = (int) $term->term_id;
     926            }
     927        } elseif ( 'tt_ids' === $_fields ) {
     928            foreach ( $term_objects as $term ) {
     929                $_terms[] = (int) $term->term_taxonomy_id;
     930            }
     931        } elseif ( 'names' === $_fields ) {
     932            foreach ( $term_objects as $term ) {
     933                $_terms[] = $term->name;
     934            }
     935        } elseif ( 'slugs' === $_fields ) {
     936            foreach ( $term_objects as $term ) {
     937                $_terms[] = $term->slug;
     938            }
     939        } elseif ( 'id=>name' === $_fields ) {
     940            foreach ( $term_objects as $term ) {
     941                $_terms[ $term->term_id ] = $term->name;
     942            }
     943        } elseif ( 'id=>slug' === $_fields ) {
     944            foreach ( $term_objects as $term ) {
     945                $_terms[ $term->term_id ] = $term->slug;
     946            }
     947        } elseif ( 'all' === $_fields || 'all_with_object_id' === $_fields ) {
     948            $_terms = $term_objects;
     949        }
     950
     951        return $_terms;
     952    }
     953
     954    /**
    953955     * Generate the ORDER BY clause for an 'orderby' param that is potentially related to a meta query.
    954956     *
     
    10541056     * @since 4.9.8
    10551057     *
    1056      * @param array $term_ids Term IDs.
    1057      * @return array
    1058      */
    1059     protected function populate_terms( $term_ids ) {
    1060         $terms = array();
    1061 
    1062         if ( ! is_array( $term_ids ) ) {
    1063             return $terms;
    1064         }
    1065 
    1066         foreach ( $term_ids as $key => $term_id ) {
    1067             $term = get_term( $term_id );
     1058     * @param Object[]|int[] $terms List of objects or term ids.
     1059     * @return WP_Term[] Array of `WP_Term` objects.
     1060     */
     1061    protected function populate_terms( $terms ) {
     1062        $term_objects = array();
     1063        if ( ! is_array( $terms ) ) {
     1064            return $term_objects;
     1065        }
     1066
     1067        foreach ( $terms as $key => $term_data ) {
     1068            if ( is_object( $term_data ) && property_exists( $term_data, 'term_id' ) ) {
     1069                $term = get_term( $term_data->term_id );
     1070                if ( property_exists( $term_data, 'object_id' ) ) {
     1071                    $term->object_id = (int) $term_data->object_id;
     1072                }
     1073            } else {
     1074                $term = get_term( $term_data );
     1075            }
     1076
    10681077            if ( $term instanceof WP_Term ) {
    1069                 $terms[ $key ] = $term;
    1070             }
    1071         }
    1072 
    1073         return $terms;
     1078                $term_objects[ $key ] = $term;
     1079            }
     1080        }
     1081
     1082        return $term_objects;
    10741083    }
    10751084}
Note: See TracChangeset for help on using the changeset viewer.