Ticket #12891: scale.12891.4.2.diff

File scale.12891.4.2.diff, 8.7 KB (added by scribu, 3 years ago)

Use JOINs + avoid extra query from 'term_id' to 'term_id'

Line 
1Index: wp-includes/taxonomy.php
2===================================================================
3--- wp-includes/taxonomy.php    (revision 16403)
4+++ wp-includes/taxonomy.php    (working copy)
5@@ -460,98 +460,43 @@
6  * @uses $wpdb
7  * @uses wp_parse_args() Creates an array from string $args.
8  *
9- * @param mixed $terms Term id/slug/name or array of such to match against
10+ * @param int|array $term_ids Term id or array of term ids of terms that will be used
11  * @param string|array $taxonomies String of taxonomy name or Array of string values of taxonomy names
12- * @param array|string $args
13- *   'include_children' bool Whether to include term children (hierarchical taxonomies only)
14- *   'field' string Which term field is being used. Can be 'term_id', 'slug' or 'name'
15- *   'operator' string Can be 'IN' and 'NOT IN'
16- *   'do_query' bool Whether to execute the query or return the SQL string
17- *
18- * @return WP_Error If the taxonomy does not exist
19- * @return array The list of found object_ids
20- * @return string The SQL string, if do_query is set to false
21+ * @param array|string $args Change the order of the object_ids, either ASC or DESC
22+ * @return WP_Error|array If the taxonomy does not exist, then WP_Error will be returned. On success
23+ *     the array can be empty meaning that there are no $object_ids found or it will return the $object_ids found.
24  */
25-function get_objects_in_term( $terms, $taxonomies, $args = array() ) {
26+function get_objects_in_term( $term_ids, $taxonomies, $args = array() ) {
27        global $wpdb;
28 
29-       extract( wp_parse_args( $args, array(
30-               'include_children' => false,
31-               'field' => 'term_id',
32-               'operator' => 'IN',
33-               'do_query' => true,
34-       ) ), EXTR_SKIP );
35+       if ( ! is_array( $term_ids ) )
36+               $term_ids = array( $term_ids );
37 
38-       $taxonomies = (array) $taxonomies;
39+       if ( ! is_array( $taxonomies ) )
40+               $taxonomies = array( $taxonomies );
41 
42-       foreach ( $taxonomies as $taxonomy ) {
43+       foreach ( (array) $taxonomies as $taxonomy ) {
44                if ( ! taxonomy_exists( $taxonomy ) )
45-                       return new WP_Error( 'invalid_taxonomy', sprintf( __( 'Invalid Taxonomy: %s' ), $taxonomy ) );
46+                       return new WP_Error( 'invalid_taxonomy', __( 'Invalid Taxonomy' ) );
47        }
48 
49-       if ( !in_array( $field, array( 'term_id', 'slug', 'name' ) ) )
50-               $field = 'term_id';
51+       $defaults = array( 'order' => 'ASC' );
52+       $args = wp_parse_args( $args, $defaults );
53+       extract( $args, EXTR_SKIP );
54 
55-       if ( !in_array( $operator, array( 'IN', 'NOT IN' ) ) )
56-               $operator = 'IN';
57+       $order = ( 'desc' == strtolower( $order ) ) ? 'DESC' : 'ASC';
58 
59-       $terms = array_unique( (array) $terms );
60+       $term_ids = array_map('intval', $term_ids );
61 
62-       if ( is_taxonomy_hierarchical( $taxonomy ) && $include_children ) {
63-               $children = array();
64-               foreach ( $terms as $term ) {
65-                       if ( 'term_id' != $field ) {
66-                               if ( $term = get_term_by( $field, $term, $taxonomy ) )
67-                                       $term = $term->term_id;
68-                               else
69-                                       continue;
70-                       }
71-                       $children = array_merge( $children, get_term_children( $term, $taxonomy ) );
72-                       $children[] = $term;
73-               }
74-               $terms = $children;
75-               $field = 'term_id';
76-       }
77-
78-       if ( empty( $terms ) )
79-               return $do_query ? array() : '';
80-
81        $taxonomies = "'" . implode( "', '", $taxonomies ) . "'";
82+       $term_ids = "'" . implode( "', '", $term_ids ) . "'";
83 
84-       switch ( $field ) {
85-               case 'term_id':
86-                       $terms = array_map( 'intval', $terms );
87+       $object_ids = $wpdb->get_col("SELECT tr.object_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND tt.term_id IN ($term_ids) ORDER BY tr.object_id $order");
88 
89-                       $terms = implode( ',', $terms );
90-                       $sql = "
91-                               SELECT object_id
92-                               FROM $wpdb->term_relationships
93-                               INNER JOIN $wpdb->term_taxonomy USING (term_taxonomy_id)
94-                               WHERE taxonomy IN ($taxonomies)
95-                               AND term_id $operator ($terms)
96-                       ";
97-               break;
98+       if ( ! $object_ids )
99+               return array();
100 
101-               case 'slug':
102-               case 'name':
103-                       foreach ( $terms as $i => $term ) {
104-                               $terms[$i] = sanitize_title_for_query( $term );
105-                       }
106-                       $terms = array_filter($terms);
107-
108-                       $terms = "'" . implode( "','", $terms ) . "'";
109-                       $sql = "
110-                               SELECT object_id
111-                               FROM $wpdb->term_relationships
112-                               INNER JOIN $wpdb->term_taxonomy USING (term_taxonomy_id)
113-                               INNER JOIN $wpdb->terms USING (term_id)
114-                               WHERE taxonomy IN ($taxonomies)
115-                               AND $field $operator ($terms)
116-                       ";
117-               break;
118-       }
119-
120-       return $do_query ? $wpdb->get_col( $sql ) : $sql;
121+       return $object_ids;
122 }
123 
124 /*
125@@ -571,44 +516,112 @@
126  * - 'include_children' bool (optional) Whether to include child terms.
127  *             Default: true
128  *
129- * @param string $object_id_column
130+ * @param string $primary_table
131+ * @param string $primary_id_column
132  * @return string
133  */
134-function get_tax_sql( $tax_query, $object_id_column ) {
135+function get_tax_sql( $tax_query, $primary_table, $primary_id_column ) {
136        global $wpdb;
137 
138-       $sql = array();
139+       $join = '';
140+       $where = '';
141+       $i = 0;
142        foreach ( $tax_query as $query ) {
143-               if ( !isset( $query['include_children'] ) )
144-                       $query['include_children'] = true;
145+               extract( wp_parse_args( $query, array(
146+                       'taxonomy' => array(),
147+                       'terms' => array(),
148+                       'include_children' => true,
149+                       'field' => 'term_id',
150+                       'operator' => 'IN',
151+               ) ) );
152 
153-               $query['do_query'] = false;
154+               $taxonomies = (array) $taxonomy;
155 
156-               $sql_single = get_objects_in_term( $query['terms'], $query['taxonomy'], $query );
157+               foreach ( $taxonomies as $taxonomy ) {
158+                       if ( ! taxonomy_exists( $taxonomy ) )
159+                               return ' AND 0 = 1';
160+               }
161 
162-               if ( empty( $sql_single ) || is_wp_error( $sql_single ) )
163-                       return ' AND 0 = 1';
164+               if ( !in_array( $operator, array( 'IN', 'NOT IN' ) ) )
165+                       $operator = 'IN';
166 
167-               $sql[] = $sql_single;
168-       }
169+               $taxonomies = "'" . implode( "', '", $taxonomies ) . "'";
170 
171-       if ( 1 == count( $sql ) ) {
172-               $ids = $wpdb->get_col( $sql[0] );
173-       } else {
174-               $r = "SELECT object_id FROM $wpdb->term_relationships WHERE 1=1";
175-               foreach ( $sql as $query )
176-                       $r .= " AND object_id IN ($query)";
177+               $terms = array_unique( (array) $terms );
178 
179-               $ids = $wpdb->get_col( $r );
180+               if ( empty( $terms ) )
181+                       continue;
182+
183+               if ( is_taxonomy_hierarchical( $taxonomy ) && $include_children ) {
184+                       _transform_terms( $terms, $taxonomies, $field, 'term_id' );
185+
186+                       if ( empty( $terms ) )
187+                               continue;
188+
189+                       $children = array();
190+                       foreach ( $terms as $term ) {
191+                               $children = array_merge( $children, get_term_children( $term, $taxonomy ) );
192+                               $children[] = $term;
193+                       }
194+                       $terms = $children;
195+
196+                       _transform_terms( $terms, $taxonomies, 'term_id', 'term_taxonomy_id' );
197+               }
198+               else {
199+                       _transform_terms( $terms, $taxonomies, $field, 'term_taxonomy_id' );
200+               }
201+
202+               if ( empty( $terms ) )
203+                       continue;
204+
205+               $alias = $i ? 'tt' . $i : $wpdb->term_relationships;
206+
207+               $join .= "\nINNER JOIN $wpdb->term_relationships";
208+               $join .= $i ? " AS $alias" : '';
209+               $join .= " ON ($primary_table.$primary_id_column = $alias.object_id)";
210+
211+               $terms = implode( ',', $terms );
212+
213+               $where .= " AND $alias.term_taxonomy_id $operator ($terms)";
214+
215+               $i++;
216        }
217 
218-       if ( !empty( $ids ) )
219-               return " AND $object_id_column IN(" . implode( ', ', $ids ) . ")";
220-       else
221-               return ' AND 0 = 1';
222+       return compact( 'join', 'where' );
223 }
224 
225+function _transform_terms( &$terms, $taxonomies, $field, $resulting_field ) {
226+       global $wpdb;
227 
228+       if ( $field == $resulting_field )
229+               return;
230+
231+       $resulting_field = esc_sql( $resulting_field );
232+
233+       switch ( $field ) {
234+               case 'slug':
235+               case 'name':
236+                       $terms = "'" . implode( "','", array_map( 'sanitize_title_for_query', $terms ) ) . "'";
237+                       $terms = $wpdb->get_col( "
238+                               SELECT $resulting_field
239+                               FROM $wpdb->term_taxonomy
240+                               INNER JOIN $wpdb->terms USING (term_id)
241+                               WHERE taxonomy IN ($taxonomies)
242+                               AND $field IN ($terms)
243+                       " );
244+                       break;
245+
246+               default:
247+                       $terms = implode( ',', array_map( 'intval', $terms ) );
248+                       $terms = $wpdb->get_col( "
249+                               SELECT $resulting_field
250+                               FROM $wpdb->term_taxonomy
251+                               WHERE taxonomy IN ($taxonomies)
252+                               AND term_id IN ($terms)
253+                       " );
254+       }
255+}
256+
257 /**
258  * Get all Term data from database by Term ID.
259  *
260Index: wp-includes/query.php
261===================================================================
262--- wp-includes/query.php       (revision 16404)
263+++ wp-includes/query.php       (working copy)
264@@ -1933,6 +1933,11 @@
265                // Taxonomies
266                $q['tax_query'] = $this->parse_tax_query( $q );
267                if ( !empty( $q['tax_query'] ) ) {
268+                       $clauses = call_user_func_array( 'get_tax_sql', array( $q['tax_query'], $wpdb->posts, 'ID', &$this) );
269+
270+                       $join .= $clauses['join'];
271+                       $where .= $clauses['where'];
272+
273                        if ( empty($post_type) ) {
274                                $post_type = 'any';
275                                $post_status_join = true;
276@@ -1940,8 +1945,6 @@
277                                $post_status_join = true;
278                        }
279 
280-                       $where .= get_tax_sql( $q['tax_query'], "$wpdb->posts.ID" );
281-
282                        // Back-compat
283                        $tax_query_in = wp_list_filter( $q['tax_query'], array( 'operator' => 'IN' ) );
284                        if ( !empty( $tax_query_in ) ) {