PluginProbe
ElasticPress / 5.3.5
ElasticPress v5.3.5
5.3.5 5.3.4 3.6.5 3.6.6 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.7.0 4.7.1 4.7.2 5.0.0 5.0.1 5.0.2 All 108 releases
← All changes | includes/classes/Indexable/Term/Term.php +821 -583 4.2.25.3.5 View file →
@@ -7,11 +7,11 @@
7 7 */
8 8
9 9 namespace ElasticPress\Indexable\Term;
10 10
11 -use ElasticPress\Indexable as Indexable;
12 -use ElasticPress\Elasticsearch as Elasticsearch;
13 -use \WP_Term_Query as WP_Term_Query;
11 +use WP_Term_Query;
12 +use ElasticPress\Elasticsearch;
13 +use ElasticPress\Indexable;
14 14
15 15 if ( ! defined( 'ABSPATH' ) ) {
16 16 // @codeCoverageIgnoreStart
17 17 exit; // Exit if accessed directly.
@@ -31,13 +31,23 @@
31 31 */
32 32 public $slug = 'term';
33 33
34 34 /**
35 - * Create indexable and initialize dependencies
35 + * Flag to indicate if the indexable has support for
36 + * `id_range` pagination method during a sync.
36 37 *
37 - * @since 3.1
38 + * @var boolean
39 + * @since 5.2.0
38 40 */
39 - public function __construct() {
41 + public $support_indexing_advanced_pagination = true;
42 +
43 + /**
44 + * Instantiate the indexable SyncManager and QueryIntegration, the main responsibles for the WP integration.
45 + *
46 + * @since 4.5.0
47 + * @return void
48 + */
49 + public function setup() {
40 50 $this->labels = [
41 51 'plural' => esc_html__( 'Terms', 'elasticpress' ),
42 52 'singular' => esc_html__( 'Term', 'elasticpress' ),
43 53 ];
@@ -53,521 +63,26 @@
53 63 * @since 3.1
54 64 * @return array
55 65 */
56 66 public function format_args( $query_vars ) {
57 - /**
58 - * Support `number` query var
59 - */
60 - if ( ! empty( $query_vars['number'] ) ) {
61 - $number = (int) $query_vars['number'];
62 - } else {
63 - /**
64 - * Set the maximum results window size.
65 - *
66 - * The request will return a HTTP 500 Internal Error if the size of the
67 - * request is larger than the [index.max_result_window] parameter in ES.
68 - * See the scroll api for a more efficient way to request large data sets.
69 - *
70 - * @return int The max results window size.
71 - *
72 - * @since 2.3.0
73 - */
74 - $number = apply_filters( 'ep_max_results_window', 10000 );
75 - }
67 + $query_vars = $this->sanitize_query_vars( $query_vars );
76 68
77 69 $formatted_args = [
78 - 'from' => 0,
79 - 'size' => $number,
70 + 'from' => $this->parse_from( $query_vars ),
71 + 'size' => $this->parse_size( $query_vars ),
80 72 ];
81 73
82 - /**
83 - * Support `offset` query var
84 - */
85 - if ( isset( $query_vars['offset'] ) ) {
86 - $formatted_args['from'] = (int) $query_vars['offset'];
87 - }
74 + $formatted_args = $this->maybe_orderby( $formatted_args, $query_vars );
88 75
89 - /**
90 - * Support `order` and `orderby` query vars
91 - */
92 -
93 - // Set sort order, default is 'ASC'.
94 - if ( ! empty( $query_vars['order'] ) ) {
95 - $order = $this->parse_order( $query_vars['order'] );
96 - } else {
97 - $order = 'desc';
76 + $filters = $this->parse_filters( $query_vars );
77 + if ( ! empty( $filters ) ) {
78 + $formatted_args['post_filter'] = $filters;
98 79 }
99 80
100 - // Set orderby, default is 'name'.
101 - if ( empty( $query_vars['orderby'] ) ) {
102 - $query_vars['orderby'] = 'name';
103 - }
81 + $formatted_args = $this->maybe_set_search_fields( $formatted_args, $query_vars );
82 + $formatted_args = $this->maybe_set_fields( $formatted_args, $query_vars );
104 83
105 - // Set sort type.
106 - $formatted_args['sort'] = $this->parse_orderby( $query_vars['orderby'], $order, $query_vars );
107 -
108 - $filter = [
109 - 'bool' => [
110 - 'must' => [],
111 - ],
112 - ];
113 -
114 - $use_filters = false;
115 -
116 84 /**
117 - * Support `taxonomy` query var
118 - */
119 - $taxonomy = [];
120 - if ( ! empty( $query_vars['taxonomy'] ) ) {
121 - $taxonomy = (array) $query_vars['taxonomy'];
122 - $terms_map_name = 'terms';
123 -
124 - if ( count( $taxonomy ) < 2 ) {
125 - $terms_map_name = 'term';
126 - $taxonomy = $taxonomy[0];
127 - }
128 -
129 - $filter['bool']['must'][] = [
130 - $terms_map_name => [
131 - 'taxonomy.raw' => $taxonomy,
132 - ],
133 - ];
134 -
135 - $use_filters = true;
136 - }
137 -
138 - /**
139 - * Support `object_ids` query var
140 - */
141 - if ( ! empty( $query_vars['object_ids'] ) ) {
142 - $filter['bool']['must'][]['bool']['must'][] = [
143 - 'terms' => [
144 - 'object_ids.value' => (array) $query_vars['object_ids'],
145 - ],
146 - ];
147 -
148 - $use_filters = true;
149 - }
150 -
151 - /**
152 - * Support `get` query var
153 - */
154 - if ( ! empty( $query_vars['get'] ) && 'all' === $query_vars['get'] ) {
155 - $query_vars['childless'] = false;
156 - $query_vars['child_of'] = 0;
157 - $query_vars['hide_empty'] = false;
158 - $query_vars['hierarchical'] = false;
159 - $query_vars['pad_counts'] = false;
160 - }
161 -
162 - /**
163 - * Support `include` query var
164 - */
165 - if ( ! empty( $query_vars['include'] ) ) {
166 - $filter['bool']['must'][]['bool']['must'] = [
167 - 'terms' => [
168 - 'term_id' => array_values( (array) $query_vars['include'] ),
169 - ],
170 - ];
171 -
172 - $use_filters = true;
173 - }
174 -
175 - /**
176 - * Support `exclude` query var
177 - */
178 - if ( empty( $query_vars['include'] ) && ! empty( $query_vars['exclude'] ) ) {
179 - $filter['bool']['must'][]['bool']['must_not'] = [
180 - 'terms' => [
181 - 'term_id' => array_values( (array) $query_vars['exclude'] ),
182 - ],
183 - ];
184 -
185 - $use_filters = true;
186 - }
187 -
188 - /**
189 - * Support `exclude_tree` query var
190 - */
191 - if ( empty( $query_vars['include'] ) && ! empty( $query_vars['exclude_tree'] ) ) {
192 - $filter['bool']['must'][]['bool']['must_not'] = [
193 - 'terms' => [
194 - 'term_id' => array_values( (array) $query_vars['exclude_tree'] ),
195 - ],
196 - ];
197 -
198 - $filter['bool']['must'][]['bool']['must_not'] = [
199 - 'terms' => [
200 - 'parent' => array_values( (array) $query_vars['exclude_tree'] ),
201 - ],
202 - ];
203 -
204 - $use_filters = true;
205 - }
206 -
207 - /**
208 - * Support `name` query var
209 - */
210 - if ( ! empty( $query_vars['name'] ) ) {
211 - $filter['bool']['must'][] = [
212 - 'terms' => [
213 - 'name.raw' => (array) $query_vars['name'],
214 - ],
215 - ];
216 -
217 - $use_filters = true;
218 - }
219 -
220 - /**
221 - * Support `slug` query var
222 - */
223 - if ( ! empty( $query_vars['slug'] ) ) {
224 - $filter['bool']['must'][] = [
225 - 'terms' => [
226 - 'slug.raw' => (array) $query_vars['slug'],
227 - ],
228 - ];
229 -
230 - $use_filters = true;
231 - }
232 -
233 - /**
234 - * Support `term_taxonomy_id` query var
235 - */
236 - if ( ! empty( $query_vars['term_taxonomy_id'] ) ) {
237 - $filter['bool']['must'][]['bool']['must'] = [
238 - 'terms' => [
239 - 'term_taxonomy_id' => array_values( (array) $query_vars['term_taxonomy_id'] ),
240 - ],
241 - ];
242 -
243 - $use_filters = true;
244 - }
245 -
246 - /**
247 - * Support `hierarchical` and `hide_empty` query var
248 - *
249 - * `hierarchical` needs to work in conjunction with `hide_empty`, as per WP docs:
250 - * > `hierarchical`: Whether to include terms that have non-empty descendants (even if $hide_empty is set to true).
251 - *
252 - * In summary:
253 - * - hide_empty AND hierarchical: count > 1 OR hierarchy.children > 1
254 - * - hide_empty AND NOT hierarchical: count > 1 (ignore hierarchy.children)
255 - * - NOT hide_empty (AND hierarchical): there is no need to limit the query
256 - *
257 - * @see https://developer.wordpress.org/reference/classes/WP_Term_Query/__construct/
258 - */
259 - $hide_empty = isset( $query_vars['hide_empty'] ) ? $query_vars['hide_empty'] : '';
260 - if ( $hide_empty ) {
261 - $hierarchical = isset( $query_vars['hierarchical'] ) ? $query_vars['hierarchical'] : '';
262 -
263 - if ( $hierarchical ) {
264 - $filter_clause = [ 'bool' => [ 'should' => [] ] ];
265 -
266 - $filter_clause['bool']['should'][] = [
267 - 'range' => [
268 - 'count' => [
269 - 'gte' => 1,
270 - ],
271 - ],
272 - ];
273 -
274 - $filter_clause['bool']['should'][] = [
275 - 'range' => [
276 - 'hierarchy.children.count' => [
277 - 'gte' => 1,
278 - ],
279 - ],
280 - ];
281 -
282 - $filter['bool']['must'][] = $filter_clause;
283 -
284 - $use_filters = true;
285 - } else {
286 - $filter['bool']['must'][] = [
287 - 'range' => [
288 - 'count' => [
289 - 'gte' => 1,
290 - ],
291 - ],
292 - ];
293 - }
294 -
295 - $use_filters = true;
296 - }
297 -
298 - /**
299 - * Support `search`, `name__like` and `description__like` query_vars
300 - */
301 - if ( ! empty( $query_vars['search'] ) || ! empty( $query_vars['name__like'] ) || ! empty( $query_vars['description__like'] ) ) {
302 -
303 - $search = ! empty( $query_vars['search'] ) ? $query_vars['search'] : '';
304 - $search_fields = [];
305 -
306 - if ( ! empty( $query_vars['name__like'] ) ) {
307 - $search = $query_vars['name__like'];
308 - $search_fields[] = 'name';
309 - }
310 -
311 - if ( ! empty( $query_vars['description__like'] ) ) {
312 - $search = $query_vars['description__like'];
313 - $search_fields[] = 'description';
314 - }
315 -
316 - /**
317 - * Allow for search field specification
318 - */
319 - if ( ! empty( $query_vars['search_fields'] ) ) {
320 - $search_fields = $query_vars['search_fields'];
321 - }
322 -
323 - if ( ! empty( $search_fields ) ) {
324 - $prepared_search_fields = [];
325 -
326 - if ( ! empty( $search_fields['meta'] ) ) {
327 - $metas = (array) $search_fields['meta'];
328 -
329 - foreach ( $metas as $meta ) {
330 - $prepared_search_fields[] = 'meta.' . $meta . '.value';
331 - }
332 -
333 - unset( $search_fields['meta'] );
334 - }
335 -
336 - $prepared_search_fields = array_merge( $search_fields, $prepared_search_fields );
337 - } else {
338 - $prepared_search_fields = [
339 - 'name',
340 - 'slug',
341 - 'taxonomy',
342 - 'description',
343 - ];
344 - }
345 -
346 - /**
347 - * Filter fields to search on Term query
348 - *
349 - * @hook ep_term_search_fields
350 - * @param {array} $search_fields Search fields
351 - * @param {array} $query_vars Query variables
352 - * @since 3.4
353 - * @return {array} New search fields
354 - */
355 - $prepared_search_fields = apply_filters( 'ep_term_search_fields', $prepared_search_fields, $query_vars );
356 -
357 - $query = [
358 - 'bool' => [
359 - 'should' => [
360 - [
361 - 'multi_match' => [
362 - 'query' => $search,
363 - 'type' => 'phrase',
364 - 'fields' => $prepared_search_fields,
365 - /**
366 - * Filter term match phrase boost amount
367 - *
368 - * @hook ep_term_match_phrase_boost
369 - * @param {int} $boos Boost amount for match phrase
370 - * @param {array} $query_vars Query variables
371 - * @since 3.4
372 - * @return {int} New boost amount
373 - */
374 - 'boost' => apply_filters( 'ep_term_match_phrase_boost', 4, $prepared_search_fields, $query_vars ),
375 - ],
376 - ],
377 - [
378 - 'multi_match' => [
379 - 'query' => $search,
380 - 'fields' => $prepared_search_fields,
381 - /**
382 - * Filter term match boost amount
383 - *
384 - * @hook ep_term_match_boost
385 - * @param {int} $boost Boost amount for match
386 - * @param {array} $query_vars Query variables
387 - * @since 3.4
388 - * @return {int} New boost amount
389 - */
390 - 'boost' => apply_filters( 'ep_term_match_boost', 2, $prepared_search_fields, $query_vars ),
391 - 'fuzziness' => 0,
392 - 'operator' => 'and',
393 - ],
394 - ],
395 - [
396 - 'multi_match' => [
397 - 'fields' => $prepared_search_fields,
398 - 'query' => $search,
399 - /**
400 - * Filter term fuzziness amount
401 - *
402 - * @hook ep_term_fuzziness_arg
403 - * @param {int} $fuzziness Amount of fuziness to factor into search
404 - * @param {array} $query_vars Query variables
405 - * @since 3.4
406 - * @return {int} New boost amount
407 - */
408 - 'fuzziness' => apply_filters( 'ep_term_fuzziness_arg', 1, $prepared_search_fields, $query_vars ),
409 - ],
410 - ],
411 - ],
412 - ],
413 - ];
414 -
415 - /**
416 - * Filter Elasticsearch query used for Terms indexable
417 - *
418 - * @hook ep_term_formatted_args_query
419 - * @param {array} $query Elasticsearch query
420 - * @param {array} $query_vars Query variables
421 - * @since 3.4
422 - * @return {array} New query
423 - */
424 - $formatted_args['query'] = apply_filters( 'ep_term_formatted_args_query', $query, $query_vars );
425 -
426 - } else {
427 - $formatted_args['query']['match_all'] = [
428 - 'boost' => 1,
429 - ];
430 - }
431 -
432 - /**
433 - * Support `child_of` query var.
434 - */
435 - if ( ! empty( $query_vars['child_of'] ) && ( is_string( $taxonomy ) || count( $taxonomy ) < 2 ) ) {
436 - $filter['bool']['must'][]['bool']['must'][] = [
437 - 'match_phrase' => [
438 - 'hierarchy.ancestors.terms' => (int) $query_vars['child_of'],
439 - ],
440 - ];
441 -
442 - $use_filters = true;
443 - }
444 -
445 - /**
446 - * Support `parent` query var.
447 - */
448 - if ( isset( $query_vars['parent'] ) && '' !== $query_vars['parent'] ) {
449 - $filter['bool']['must'][]['bool']['must'] = [
450 - 'term' => [
451 - 'parent' => (int) $query_vars['parent'],
452 - ],
453 - ];
454 -
455 - $use_filters = true;
456 - }
457 -
458 - /**
459 - * Support `childless` query var.
460 - */
461 - if ( ! empty( $query_vars['childless'] ) ) {
462 - $filter['bool']['must'][]['bool']['must'] = [
463 - 'term' => [
464 - 'hierarchy.children.terms' => 0,
465 - ],
466 - ];
467 -
468 - $use_filters = true;
469 - }
470 -
471 - $meta_queries = [];
472 -
473 - /**
474 - * Support `meta_key`, `meta_value`, and `meta_compare` query args
475 - */
476 - if ( ! empty( $query_vars['meta_key'] ) ) {
477 - $meta_query_array = [
478 - 'key' => $query_vars['meta_key'],
479 - ];
480 -
481 - if ( isset( $query_vars['meta_value'] ) && '' !== $query_vars['meta_value'] ) {
482 - $meta_query_array['value'] = $query_vars['meta_value'];
483 - }
484 -
485 - if ( isset( $query_vars['meta_compare'] ) ) {
486 - $meta_query_array['compare'] = $query_vars['meta_compare'];
487 - }
488 -
489 - $meta_queries[] = $meta_query_array;
490 - }
491 -
492 - /**
493 - * Support 'meta_query' query var.
494 - */
495 - if ( ! empty( $query_vars['meta_query'] ) ) {
496 - $meta_queries = array_merge( $meta_queries, $query_vars['meta_query'] );
497 - }
498 -
499 - if ( ! empty( $meta_queries ) ) {
500 - $built_meta_queries = $this->build_meta_query( $meta_queries );
501 -
502 - if ( $built_meta_queries ) {
503 - $filter['bool']['must'][] = $built_meta_queries;
504 - $use_filters = true;
505 - }
506 - }
507 -
508 - /**
509 - * Support `fields` query var.
510 - */
511 - if ( isset( $query_vars['fields'] ) ) {
512 - switch ( $query_vars['fields'] ) {
513 - case 'ids':
514 - $formatted_args['_source'] = [
515 - 'includes' => [
516 - 'term_id',
517 - ],
518 - ];
519 - break;
520 -
521 - case 'id=>name':
522 - $formatted_args['_source'] = [
523 - 'includes' => [
524 - 'term_id',
525 - 'name',
526 - ],
527 - ];
528 - break;
529 -
530 - case 'id=>parent':
531 - $formatted_args['_source'] = [
532 - 'includes' => [
533 - 'term_id',
534 - 'parent',
535 - ],
536 - ];
537 - break;
538 -
539 - case 'id=>slug':
540 - $formatted_args['_source'] = [
541 - 'includes' => [
542 - 'term_id',
543 - 'slug',
544 - ],
545 - ];
546 - break;
547 -
548 - case 'names':
549 - $formatted_args['_source'] = [
550 - 'includes' => [
551 - 'name',
552 - ],
553 - ];
554 - break;
555 - case 'tt_ids':
556 - $formatted_args['_source'] = [
557 - 'includes' => [
558 - 'term_taxonomy_id',
559 - ],
560 - ];
561 - break;
562 - }
563 - }
564 -
565 - if ( $use_filters ) {
566 - $formatted_args['post_filter'] = $filter;
567 - }
568 -
569 - /**
570 85 * Filter full Elasticsearch query for Terms indexable
571 86 *
572 87 * @hook ep_term_formatted_args
573 88 * @param {array} $query Elasticsearch query
@@ -589,15 +104,14 @@
589 104
590 105 if ( empty( $es_version ) ) {
591 106 $es_version = apply_filters( 'ep_fallback_elasticsearch_version', '2.0' );
592 107 }
108 + $es_version = (string) $es_version;
593 109
594 - $mapping_file = 'initial.php';
110 + $mapping_file = '7-0.php';
595 111
596 - if ( version_compare( $es_version, '5.0', '<' ) ) {
597 - $mapping_file = 'pre-5-0.php';
598 - } elseif ( version_compare( $es_version, '7.0', '>=' ) ) {
599 - $mapping_file = '7-0.php';
112 + if ( version_compare( $es_version, '7.0', '<' ) ) {
113 + $mapping_file = 'initial.php';
600 114 }
601 115
602 116 /**
603 117 * Filter mapping file for Terms indexable
@@ -673,16 +187,19 @@
673 187 * @since 3.1
674 188 * @return array
675 189 */
676 190 public function query_db( $args ) {
677 -
678 191 $defaults = [
679 - 'number' => $this->get_bulk_items_per_page(),
680 - 'offset' => 0,
681 - 'orderby' => 'id',
682 - 'order' => 'desc',
683 - 'taxonomy' => $this->get_indexable_taxonomies(),
684 - 'hide_empty' => false,
192 + 'number' => $this->get_bulk_items_per_page(),
193 + 'offset' => 0,
194 + 'orderby' => 'id',
195 + 'order' => 'desc',
196 + 'taxonomy' => $this->get_indexable_taxonomies(),
197 + 'hide_empty' => false,
198 + 'hierarchical' => false,
199 + 'update_term_meta_cache' => false,
200 + 'cache_results' => false,
201 + 'ep_indexing_advanced_pagination' => true,
685 202 ];
686 203
687 204 if ( isset( $args['per_page'] ) ) {
688 205 $args['number'] = $args['per_page'];
@@ -687,8 +204,16 @@
687 204 if ( isset( $args['per_page'] ) ) {
688 205 $args['number'] = $args['per_page'];
689 206 }
690 207
208 + if ( isset( $args['include'] ) ) {
209 + $args['include'] = $args['include'];
210 + }
211 +
212 + if ( isset( $args['exclude'] ) ) {
213 + $args['exclude'] = $args['exclude'];
214 + }
215 +
691 216 /**
692 217 * Filter database arguments for term query
693 218 *
694 219 * @hook ep_term_query_db_args
@@ -703,33 +228,57 @@
703 228 unset( $all_query_args['number'] );
704 229 unset( $all_query_args['offset'] );
705 230 unset( $all_query_args['fields'] );
706 231
707 - /**
708 - * This just seems so inefficient.
709 - *
710 - * @todo Better way to do this?
711 - */
232 + if ( isset( $args['include'] ) || 0 < $args['offset'] ) {
233 + // Disable advanced pagination. Not useful if only indexing specific IDs.
234 + $args['ep_indexing_advanced_pagination'] = false;
235 + }
712 236
713 - /**
714 - * Filter database arguments for term count query
715 - *
716 - * @hook ep_term_all_query_db_args
717 - * @param {array} $args Query arguments based to WP_Term_Query
718 - * @since 3.4
719 - * @return {array} New arguments
720 - */
721 - $all_query = new WP_Term_Query( apply_filters( 'ep_term_all_query_db_args', $all_query_args, $args ) );
237 + // Explicitly set the orderby to ID to prevent accidental modifications by other code.
238 + add_filter( 'terms_clauses', [ $this, 'set_orderby' ], 9999, 3 );
722 239
723 - $total_objects = count( $all_query->terms );
240 + // Enforce the following query args during advanced pagination to ensure things work correctly.
241 + if ( $args['ep_indexing_advanced_pagination'] ) {
242 + $args = array_merge(
243 + $args,
244 + [
245 + 'suppress_filters' => false,
246 + 'orderby' => 'ID',
247 + 'order' => 'DESC',
248 + 'paged' => 1,
249 + 'offset' => 0,
250 + ]
251 + );
252 + add_filter( 'terms_clauses', [ $this, 'bulk_indexing_filter_terms_where' ], 9999, 3 );
724 253
725 - if ( ! empty( $args['offset'] ) ) {
726 - if ( (int) $args['offset'] >= $total_objects ) {
727 - $total_objects = 0;
254 + $query = new WP_Term_Query( $args );
255 + $total_objects = $this->get_total_objects_for_query( $args );
256 +
257 + remove_filter( 'terms_clauses', [ $this, 'bulk_indexing_filter_terms_where' ], 9999, 3 );
258 + } else {
259 +
260 + /**
261 + * Filter database arguments for term count query
262 + *
263 + * @hook ep_term_all_query_db_args
264 + * @param {array} $args Query arguments based to `wp_count_terms()`
265 + * @since 3.4
266 + * @return {array} New arguments
267 + */
268 + $total_objects = wp_count_terms( apply_filters( 'ep_term_all_query_db_args', $all_query_args, $args ) );
269 + $total_objects = ! is_wp_error( $total_objects ) ? (int) $total_objects : 0;
270 +
271 + if ( ! empty( $args['offset'] ) ) {
272 + if ( (int) $args['offset'] >= $total_objects ) {
273 + $total_objects = 0;
274 + }
728 275 }
276 +
277 + $query = new WP_Term_Query( $args );
729 278 }
730 279
731 - $query = new WP_Term_Query( $args );
280 + remove_filter( 'terms_clauses', [ $this, 'set_orderby' ], 9999, 3 );
732 281
733 282 if ( is_array( $query->terms ) ) {
734 283 array_walk( $query->terms, array( $this, 'remap_terms' ) );
735 284 }
@@ -740,8 +289,82 @@
740 289 ];
741 290 }
742 291
743 292 /**
293 + * Filters the WHERE clause of the SQL query used for bulk indexing terms by modifying it to include a range of
294 + * comment IDs based on advanced pagination parameters.
295 + *
296 + * @param array $clauses Associative array of the clauses for the query.
297 + * @param array $taxonomies An array of taxonomy names.
298 + * @param array $args An array of term query arguments.
299 + *
300 + * @return array The modified SQL WHERE clauses.
301 + */
302 + public function bulk_indexing_filter_terms_where( $clauses, $taxonomies, $args ) {
303 + $using_advanced_pagination = $args['ep_indexing_advanced_pagination'] ?? false;
304 +
305 + if ( $using_advanced_pagination ) {
306 + $requested_upper_limit_id = $args['ep_indexing_upper_limit_object_id'] ?? PHP_INT_MAX;
307 + $requested_lower_limit_object_id = $args['ep_indexing_lower_limit_object_id'] ?? 0;
308 + $last_processed_id = $args['ep_indexing_last_processed_object_id'] ?? null;
309 +
310 + // On the first loopthrough we begin with the requested upper limit ID. Afterwards, use the last processed ID to paginate.
311 + $upper_limit_range_object_id = $requested_upper_limit_id;
312 + if ( is_numeric( $last_processed_id ) ) {
313 + $upper_limit_range_object_id = $last_processed_id - 1;
314 + }
315 +
316 + // Sanitize. Abort if unexpected data at this point.
317 + if ( ! is_numeric( $upper_limit_range_object_id ) || ! is_numeric( $requested_lower_limit_object_id ) ) {
318 + return $clauses;
319 + }
320 +
321 + $range = [
322 + 'upper_limit' => "t.term_id <= {$upper_limit_range_object_id}",
323 + 'lower_limit' => "t.term_id >= {$requested_lower_limit_object_id}",
324 + ];
325 +
326 + // Skip the end range if it's unnecessary.
327 + $skip_ending_range = 0 === $requested_lower_limit_object_id;
328 + $where = $clauses['where'];
329 + $where = $skip_ending_range ? " {$range['upper_limit']} AND {$where}" : " {$range['upper_limit']} AND {$range['lower_limit']} AND {$where}";
330 +
331 + $clauses['where'] = $where;
332 + }
333 +
334 + return $clauses;
335 + }
336 +
337 + /**
338 + * Get the total number of terms for a given query.
339 + *
340 + * @param array $query_args The query args.
341 + * @return int The total number of terms.
342 + */
343 + protected function get_total_objects_for_query( $query_args ) {
344 + static $object_counts = [];
345 +
346 + // Reset the pagination-related args for optimal caching.
347 + $normalized_query_args = array_merge(
348 + $query_args,
349 + [
350 + 'offset' => 0,
351 + 'paged' => 1,
352 + 'posts_per_page' => 1,
353 + 'no_found_rows' => false,
354 + 'ep_indexing_last_processed_object_id' => null,
355 + ]
356 + );
357 +
358 + $cache_key = md5( get_current_blog_id() . wp_json_encode( $normalized_query_args ) );
359 + if ( ! isset( $object_counts[ $cache_key ] ) ) {
360 + $object_counts[ $cache_key ] = wp_count_terms( $normalized_query_args );
361 + }
362 +
363 + return $object_counts[ $cache_key ];
364 + }
365 +
366 + /**
744 367 * Returns indexable taxonomies for the current site
745 368 *
746 369 * @since 3.1
747 370 * @return mixed|void
@@ -849,13 +472,11 @@
849 472
850 473 if ( true === $allowed_protected_keys || in_array( $key, $allowed_protected_keys, true ) ) {
851 474 $allow_index = true;
852 475 }
853 - } else {
476 + } elseif ( true !== $excluded_public_keys && ! in_array( $key, $excluded_public_keys, true ) ) {
854 477
855 - if ( true !== $excluded_public_keys && ! in_array( $key, $excluded_public_keys, true ) ) {
856 - $allow_index = true;
857 - }
478 + $allow_index = true;
858 479 }
859 480
860 481 /**
861 482 * Filter kill switch for any term meta
@@ -971,62 +592,679 @@
971 592 if ( empty( $orderby ) ) {
972 593 return $sort;
973 594 }
974 595
975 - switch ( $orderby ) {
976 - case 'name':
977 - $es_version = Elasticsearch::factory()->get_elasticsearch_version();
978 - $es_field_name = 'name.sortable';
596 + $from_to = [
597 + 'slug' => 'slug.raw',
598 + 'id' => 'term_id',
599 + 'description' => 'description.sortable',
600 + ];
979 601
980 - if ( version_compare( $es_version, '7.0', '<' ) ) {
981 - $es_field_name = 'name.raw';
602 + if ( in_array( $orderby, [ 'meta_value', 'meta_value_num' ], true ) ) {
603 + if ( empty( $args['meta_key'] ) ) {
604 + return $sort;
605 + } else {
606 + $from_to['meta_value'] = 'meta.' . $args['meta_key'] . '.value';
607 + $from_to['meta_value_num'] = 'meta.' . $args['meta_key'] . '.long';
608 + }
609 + }
610 +
611 + if ( 'name' === $orderby ) {
612 + $es_version = Elasticsearch::factory()->get_elasticsearch_version();
613 + $from_to['name'] = version_compare( (string) $es_version, '7.0', '<' ) ? 'name.raw' : 'name.sortable';
614 + }
615 +
616 + $orderby = $from_to[ $orderby ] ?? $orderby;
617 +
618 + $sort[] = array(
619 + $orderby => array(
620 + 'order' => $order,
621 + ),
622 + );
623 +
624 + return $sort;
625 + }
626 +
627 + /**
628 + * Sanitize WP_Term_Query arguments to be used to create the ES query.
629 + *
630 + * @since 5.1.0
631 + * @param array $query_vars WP_Term_Query arguments
632 + * @return array
633 + */
634 + protected function sanitize_query_vars( $query_vars ) {
635 + if ( ! empty( $query_vars['get'] ) && 'all' === $query_vars['get'] ) {
636 + $query_vars['childless'] = false;
637 + $query_vars['child_of'] = 0;
638 + $query_vars['hide_empty'] = false;
639 + $query_vars['hierarchical'] = false;
640 + $query_vars['pad_counts'] = false;
641 + }
642 +
643 + $query_vars['taxonomy'] = ( ! empty( $query_vars['taxonomy'] ) ) ?
644 + (array) $query_vars['taxonomy'] :
645 + [];
646 +
647 + return $query_vars;
648 + }
649 +
650 + /**
651 + * Parse the `from` clause of the ES Query.
652 + *
653 + * @since 5.1.0
654 + * @param array $query_vars WP_Term_Query arguments
655 + * @return int
656 + */
657 + protected function parse_from( $query_vars ) {
658 + return ( isset( $query_vars['offset'] ) ) ? (int) $query_vars['offset'] : 0;
659 + }
660 +
661 + /**
662 + * Parse the `size` clause of the ES Query.
663 + *
664 + * @since 5.1.0
665 + * @param array $query_vars WP_Term_Query arguments
666 + * @return int
667 + */
668 + protected function parse_size( $query_vars ) {
669 + if ( ! empty( $query_vars['number'] ) ) {
670 + $number = (int) $query_vars['number'];
671 + } else {
672 + /**
673 + * Set the maximum results window size.
674 + *
675 + * The request will return a HTTP 500 Internal Error if the size of the
676 + * request is larger than the [index.max_result_window] parameter in ES.
677 + * See the scroll api for a more efficient way to request large data sets.
678 + *
679 + * @return int The max results window size.
680 + *
681 + * @since 2.3.0
682 + */
683 + $number = apply_filters( 'ep_max_results_window', 10000 );
684 + }
685 +
686 + return $number;
687 + }
688 +
689 + /**
690 + * Parse the order of results in the ES query.
691 + *
692 + * @since 5.1.0
693 + * @param array $formatted_args Formatted Elasticsearch query
694 + * @param array $query_vars WP_Term_Query arguments
695 + * @return array
696 + */
697 + protected function maybe_orderby( $formatted_args, $query_vars ) {
698 + // Set sort order, default is 'ASC'.
699 + if ( ! empty( $query_vars['order'] ) ) {
700 + $order = $this->parse_order( $query_vars['order'] );
701 + } else {
702 + $order = 'desc';
703 + }
704 +
705 + // Set orderby, default is 'name'.
706 + if ( empty( $query_vars['orderby'] ) ) {
707 + $query_vars['orderby'] = 'name';
708 + }
709 +
710 + // Set sort type.
711 + $formatted_args['sort'] = $this->parse_orderby( $query_vars['orderby'], $order, $query_vars );
712 +
713 + return $formatted_args;
714 + }
715 +
716 + /**
717 + * Based on WP_Term_Query arguments, parses the various filters that could be applied into the ES query.
718 + *
719 + * @since 5.1.0
720 + * @param array $query_vars WP_Term_Query arguments
721 + * @return array
722 + */
723 + protected function parse_filters( $query_vars ) {
724 + $filters = [
725 + 'taxonomy' => $this->parse_taxonomy( $query_vars ),
726 + 'object_ids' => $this->parse_object_ids( $query_vars ),
727 + 'include' => $this->parse_include( $query_vars ),
728 + 'exclude' => $this->parse_exclude( $query_vars ),
729 + 'exclude_tree' => $this->parse_exclude_tree( $query_vars ),
730 + 'name' => $this->parse_name( $query_vars ),
731 + 'slug' => $this->parse_slug( $query_vars ),
732 + 'term_taxonomy_id' => $this->parse_term_taxonomy_id( $query_vars ),
733 + 'hierarchical_hide_empty' => $this->parse_hierarchical_hide_empty( $query_vars ),
734 + 'child_of' => $this->parse_child_of( $query_vars ),
735 + 'parent' => $this->parse_parent( $query_vars ),
736 + 'childless' => $this->parse_childless( $query_vars ),
737 + 'meta_query' => $this->parse_meta_queries( $query_vars ),
738 + ];
739 +
740 + $filters = array_values( array_filter( $filters ) );
741 +
742 + if ( ! empty( $filters ) ) {
743 + $filters = [
744 + 'bool' => [
745 + 'must' => $filters,
746 + ],
747 + ];
748 + }
749 +
750 + return $filters;
751 + }
752 +
753 + /**
754 + * Parse the `taxonomy` WP Term Query arg and transform it into an ES query clause.
755 + *
756 + * @since 5.1.0
757 + * @param array $query_vars WP_Term_Query arguments
758 + * @return array
759 + */
760 + protected function parse_taxonomy( $query_vars ) {
761 + if ( empty( $query_vars['taxonomy'] ) ) {
762 + return [];
763 + }
764 +
765 + if ( count( $query_vars['taxonomy'] ) < 2 ) {
766 + return [
767 + 'term' => [
768 + 'taxonomy.raw' => $query_vars['taxonomy'][0],
769 + ],
770 + ];
771 + }
772 +
773 + return [
774 + 'terms' => [
775 + 'taxonomy.raw' => $query_vars['taxonomy'],
776 + ],
777 + ];
778 + }
779 +
780 + /**
781 + * Parse the `object_ids` WP Term Query arg and transform it into an ES query clause.
782 + *
783 + * @since 5.1.0
784 + * @param array $query_vars WP_Term_Query arguments
785 + * @return array
786 + */
787 + protected function parse_object_ids( $query_vars ) {
788 + if ( empty( $query_vars['object_ids'] ) ) {
789 + return [];
790 + }
791 +
792 + return [
793 + 'bool' => [
794 + 'must' => [
795 + 'terms' => [
796 + 'object_ids.value' => (array) $query_vars['object_ids'],
797 + ],
798 + ],
799 + ],
800 + ];
801 + }
802 +
803 + /**
804 + * Parse the `include` WP Term Query arg and transform it into an ES query clause.
805 + *
806 + * @since 5.1.0
807 + * @param array $query_vars WP_Term_Query arguments
808 + * @return array
809 + */
810 + protected function parse_include( $query_vars ) {
811 + if ( empty( $query_vars['include'] ) ) {
812 + return [];
813 + }
814 +
815 + return [
816 + 'bool' => [
817 + 'must' => [
818 + 'terms' => [
819 + 'term_id' => array_values( (array) $query_vars['include'] ),
820 + ],
821 + ],
822 + ],
823 + ];
824 + }
825 +
826 + /**
827 + * Parse the `exclude` WP Term Query arg and transform it into an ES query clause.
828 + *
829 + * @since 5.1.0
830 + * @param array $query_vars WP_Term_Query arguments
831 + * @return array
832 + */
833 + protected function parse_exclude( $query_vars ) {
834 + if ( ! empty( $query_vars['include'] ) || empty( $query_vars['exclude'] ) ) {
835 + return [];
836 + }
837 +
838 + return [
839 + 'bool' => [
840 + 'must_not' => [
841 + 'terms' => [
842 + 'term_id' => array_values( (array) $query_vars['exclude'] ),
843 + ],
844 + ],
845 + ],
846 + ];
847 + }
848 +
849 + /**
850 + * Parse the `exclude_tree` WP Term Query arg and transform it into an ES query clause.
851 + *
852 + * @since 5.1.0
853 + * @param array $query_vars WP_Term_Query arguments
854 + * @return array
855 + */
856 + protected function parse_exclude_tree( $query_vars ) {
857 + if ( ! empty( $query_vars['include'] ) || empty( $query_vars['exclude_tree'] ) ) {
858 + return [];
859 + }
860 +
861 + return [
862 + 'bool' => [
863 + 'must_not' => [
864 + [
865 + 'terms' => [
866 + 'term_id' => array_values( (array) $query_vars['exclude_tree'] ),
867 + ],
868 + ],
869 + [
870 + 'terms' => [
871 + 'parent' => array_values( (array) $query_vars['exclude_tree'] ),
872 + ],
873 + ],
874 + ],
875 + ],
876 + ];
877 + }
878 +
879 + /**
880 + * Parse the `name` WP Term Query arg and transform it into an ES query clause.
881 + *
882 + * @since 5.1.0
883 + * @param array $query_vars WP_Term_Query arguments
884 + * @return array
885 + */
886 + protected function parse_name( $query_vars ) {
887 + if ( empty( $query_vars['name'] ) ) {
888 + return [];
889 + }
890 +
891 + return [
892 + 'terms' => [
893 + 'name.raw' => (array) $query_vars['name'],
894 + ],
895 + ];
896 + }
897 +
898 + /**
899 + * Parse the `slug` WP Term Query arg and transform it into an ES query clause.
900 + *
901 + * @since 5.1.0
902 + * @param array $query_vars WP_Term_Query arguments
903 + * @return array
904 + */
905 + protected function parse_slug( $query_vars ) {
906 + if ( empty( $query_vars['slug'] ) ) {
907 + return [];
908 + }
909 +
910 + $query_vars['slug'] = (array) $query_vars['slug'];
911 + $query_vars['slug'] = array_map( 'sanitize_title', $query_vars['slug'] );
912 +
913 + return [
914 + 'terms' => [
915 + 'slug.raw' => (array) $query_vars['slug'],
916 + ],
917 + ];
918 + }
919 +
920 + /**
921 + * Parse the `term_taxonomy_id` WP Term Query arg and transform it into an ES query clause.
922 + *
923 + * @since 5.1.0
924 + * @param array $query_vars WP_Term_Query arguments
925 + * @return array
926 + */
927 + protected function parse_term_taxonomy_id( $query_vars ) {
928 + if ( empty( $query_vars['term_taxonomy_id'] ) ) {
929 + return [];
930 + }
931 +
932 + return [
933 + 'bool' => [
934 + 'must' => [
935 + 'terms' => [
936 + 'term_taxonomy_id' => array_values( (array) $query_vars['term_taxonomy_id'] ),
937 + ],
938 + ],
939 + ],
940 + ];
941 + }
942 +
943 + /**
944 + * Parse the `hide_empty` and `hierarchical` WP Term Query args and transform them into ES query clauses.
945 + *
946 + * `hierarchical` needs to work in conjunction with `hide_empty`, as per WP docs:
947 + * > `hierarchical`: Whether to include terms that have non-empty descendants (even if $hide_empty is set to true).
948 + *
949 + * In summary:
950 + * - hide_empty AND hierarchical: count > 1 OR hierarchy.children > 1
951 + * - hide_empty AND NOT hierarchical: count > 1 (ignore hierarchy.children)
952 + * - NOT hide_empty (AND hierarchical): there is no need to limit the query
953 + *
954 + * @see https://developer.wordpress.org/reference/classes/WP_Term_Query/__construct/
955 + * @since 5.1.0
956 + * @param array $query_vars WP_Term_Query arguments
957 + * @return array
958 + */
959 + protected function parse_hierarchical_hide_empty( $query_vars ) {
960 + $hide_empty = isset( $query_vars['hide_empty'] ) ? $query_vars['hide_empty'] : '';
961 + if ( ! $hide_empty ) {
962 + return [];
963 + }
964 +
965 + $hierarchical = isset( $query_vars['hierarchical'] ) ? $query_vars['hierarchical'] : '';
966 + if ( ! $hierarchical ) {
967 + return [
968 + 'range' => [
969 + 'count' => [
970 + 'gte' => 1,
971 + ],
972 + ],
973 + ];
974 + }
975 +
976 + return [
977 + 'bool' => [
978 + 'should' => [
979 + [
980 + 'range' => [
981 + 'count' => [
982 + 'gte' => 1,
983 + ],
984 + ],
985 + ],
986 + [
987 + 'range' => [
988 + 'hierarchy.children.count' => [
989 + 'gte' => 1,
990 + ],
991 + ],
992 + ],
993 + ],
994 + ],
995 + ];
996 + }
997 +
998 + /**
999 + * Parse the `child_of` WP Term Query arg and transform it into an ES query clause.
1000 + *
1001 + * @since 5.1.0
1002 + * @param array $query_vars WP_Term_Query arguments
1003 + * @return array
1004 + */
1005 + protected function parse_child_of( $query_vars ) {
1006 + if ( empty( $query_vars['child_of'] ) || count( $query_vars['taxonomy'] ) > 1 ) {
1007 + return [];
1008 + }
1009 +
1010 + return [
1011 + 'bool' => [
1012 + 'must' => [
1013 + 'match_phrase' => [
1014 + 'hierarchy.ancestors.terms' => (int) $query_vars['child_of'],
1015 + ],
1016 + ],
1017 + ],
1018 + ];
1019 + }
1020 +
1021 + /**
1022 + * Parse the `parent` WP Term Query arg and transform it into an ES query clause.
1023 + *
1024 + * @since 5.1.0
1025 + * @param array $query_vars WP_Term_Query arguments
1026 + * @return array
1027 + */
1028 + protected function parse_parent( $query_vars ) {
1029 + if ( ! isset( $query_vars['parent'] ) || '' === $query_vars['parent'] ) {
1030 + return [];
1031 + }
1032 +
1033 + return [
1034 + 'bool' => [
1035 + 'must' => [
1036 + 'term' => [
1037 + 'parent' => (int) $query_vars['parent'],
1038 + ],
1039 + ],
1040 + ],
1041 + ];
1042 + }
1043 +
1044 + /**
1045 + * Parse the `childless` WP Term Query arg and transform it into an ES query clause.
1046 + *
1047 + * @since 5.1.0
1048 + * @param array $query_vars WP_Term_Query arguments
1049 + * @return array
1050 + */
1051 + protected function parse_childless( $query_vars ) {
1052 + if ( empty( $query_vars['childless'] ) ) {
1053 + return [];
1054 + }
1055 +
1056 + return [
1057 + 'bool' => [
1058 + 'must' => [
1059 + 'term' => [
1060 + 'hierarchy.children.terms' => 0,
1061 + ],
1062 + ],
1063 + ],
1064 + ];
1065 + }
1066 +
1067 + /**
1068 + * Parse WP Term Query meta queries and transform them into ES query clauses.
1069 + *
1070 + * @since 5.1.0
1071 + * @param array $query_vars WP_Term_Query arguments
1072 + * @return array
1073 + */
1074 + protected function parse_meta_queries( $query_vars ) {
1075 + $meta_queries = [];
1076 + /**
1077 + * Support `meta_key`, `meta_value`, and `meta_compare` query args
1078 + */
1079 + if ( ! empty( $query_vars['meta_key'] ) ) {
1080 + $meta_query_array = [
1081 + 'key' => $query_vars['meta_key'],
1082 + ];
1083 +
1084 + if ( isset( $query_vars['meta_value'] ) && '' !== $query_vars['meta_value'] ) {
1085 + $meta_query_array['value'] = $query_vars['meta_value'];
1086 + }
1087 +
1088 + if ( isset( $query_vars['meta_compare'] ) ) {
1089 + $meta_query_array['compare'] = $query_vars['meta_compare'];
1090 + }
1091 +
1092 + $meta_queries[] = $meta_query_array;
1093 + }
1094 +
1095 + /**
1096 + * Support 'meta_query' query var.
1097 + */
1098 + if ( ! empty( $query_vars['meta_query'] ) ) {
1099 + $meta_queries = array_merge( $meta_queries, $query_vars['meta_query'] );
1100 + }
1101 +
1102 + if ( ! empty( $meta_queries ) ) {
1103 + $built_meta_queries = $this->build_meta_query( $meta_queries );
1104 +
1105 + if ( $built_meta_queries ) {
1106 + return $built_meta_queries;
1107 + }
1108 + }
1109 +
1110 + return [];
1111 + }
1112 +
1113 + /**
1114 + * If in a search context, using `name__like`, or `description__like` set search fields, otherwise query everything.
1115 + *
1116 + * @since 5.1.0
1117 + * @param array $formatted_args Formatted Elasticsearch query
1118 + * @param array $query_vars WP_Term_Query arguments
1119 + * @return array
1120 + */
1121 + protected function maybe_set_search_fields( $formatted_args, $query_vars ) {
1122 + if ( empty( $query_vars['search'] ) && empty( $query_vars['name__like'] ) && empty( $query_vars['description__like'] ) ) {
1123 + $formatted_args['query']['match_all'] = [
1124 + 'boost' => 1,
1125 + ];
1126 +
1127 + return $formatted_args;
1128 + }
1129 +
1130 + $search = ! empty( $query_vars['search'] ) ? $query_vars['search'] : '';
1131 + $search_fields = [];
1132 +
1133 + if ( ! empty( $query_vars['name__like'] ) ) {
1134 + $search = $query_vars['name__like'];
1135 + $search_fields[] = 'name';
1136 + }
1137 +
1138 + if ( ! empty( $query_vars['description__like'] ) ) {
1139 + $search = $query_vars['description__like'];
1140 + $search_fields[] = 'description';
1141 + }
1142 +
1143 + /**
1144 + * Allow for search field specification
1145 + */
1146 + if ( ! empty( $query_vars['search_fields'] ) ) {
1147 + $search_fields = $query_vars['search_fields'];
1148 + }
1149 +
1150 + if ( ! empty( $search_fields ) ) {
1151 + $prepared_search_fields = [];
1152 +
1153 + if ( ! empty( $search_fields['meta'] ) ) {
1154 + $metas = (array) $search_fields['meta'];
1155 +
1156 + foreach ( $metas as $meta ) {
1157 + $prepared_search_fields[] = 'meta.' . $meta . '.value';
982 1158 }
983 1159
984 - break;
1160 + unset( $search_fields['meta'] );
1161 + }
985 1162
986 - case 'slug':
987 - $es_field_name = 'slug.raw';
988 - break;
1163 + $prepared_search_fields = array_merge( $search_fields, $prepared_search_fields );
1164 + } else {
1165 + $prepared_search_fields = [
1166 + 'name',
1167 + 'slug',
1168 + 'taxonomy',
1169 + 'description',
1170 + ];
1171 + }
989 1172
990 - case 'term_id':
991 - case 'id':
992 - $es_field_name = 'term_id';
993 - break;
1173 + /**
1174 + * Filter fields to search on Term query
1175 + *
1176 + * @hook ep_term_search_fields
1177 + * @param {array} $search_fields Search fields
1178 + * @param {array} $query_vars Query variables
1179 + * @since 3.4
1180 + * @return {array} New search fields
1181 + */
1182 + $prepared_search_fields = apply_filters( 'ep_term_search_fields', $prepared_search_fields, $query_vars );
994 1183
995 - case 'description':
996 - $es_field_name = 'description.sortable';
997 - break;
1184 + $search_algorithm = $this->get_search_algorithm( $search, $prepared_search_fields, $query_vars );
1185 + $formatted_args['query'] = $search_algorithm->get_query( 'term', $search, $prepared_search_fields, $query_vars );
998 1186
999 - case 'meta_value':
1000 - if ( ! empty( $args['meta_key'] ) ) {
1001 - $es_field_name = 'meta.' . $args['meta_key'] . '.value';
1002 - }
1187 + return $formatted_args;
1188 + }
1003 1189
1190 + /**
1191 + * If needed set the `fields` ES query clause.
1192 + *
1193 + * @since 5.1.0
1194 + * @param array $formatted_args Formatted Elasticsearch query
1195 + * @param array $query_vars WP_Term_Query arguments
1196 + * @return array
1197 + */
1198 + protected function maybe_set_fields( $formatted_args, $query_vars ) {
1199 + if ( ! isset( $query_vars['fields'] ) ) {
1200 + return $formatted_args;
1201 + }
1202 +
1203 + switch ( $query_vars['fields'] ) {
1204 + case 'ids':
1205 + $formatted_args['_source'] = [
1206 + 'includes' => [
1207 + 'term_id',
1208 + ],
1209 + ];
1004 1210 break;
1005 1211
1006 - case 'meta_value_num':
1007 - if ( ! empty( $args['meta_key'] ) ) {
1008 - $es_field_name = 'meta.' . $args['meta_key'] . '.long';
1009 - }
1212 + case 'id=>name':
1213 + $formatted_args['_source'] = [
1214 + 'includes' => [
1215 + 'term_id',
1216 + 'name',
1217 + ],
1218 + ];
1219 + break;
1010 1220
1221 + case 'id=>parent':
1222 + $formatted_args['_source'] = [
1223 + 'includes' => [
1224 + 'term_id',
1225 + 'parent',
1226 + ],
1227 + ];
1011 1228 break;
1012 1229
1013 - case 'parent':
1014 - case 'count':
1015 - default:
1016 - $es_field_name = $orderby;
1230 + case 'id=>slug':
1231 + $formatted_args['_source'] = [
1232 + 'includes' => [
1233 + 'term_id',
1234 + 'slug',
1235 + ],
1236 + ];
1017 1237 break;
1018 - }
1019 1238
1020 - // For `meta_value` and `meta_value_num`, for example, there is a chance this wasn't set.
1021 - if ( ! empty( $es_field_name ) ) {
1022 - $sort[] = array(
1023 - $es_field_name => array(
1024 - 'order' => $order,
1025 - ),
1026 - );
1239 + case 'names':
1240 + $formatted_args['_source'] = [
1241 + 'includes' => [
1242 + 'name',
1243 + ],
1244 + ];
1245 + break;
1246 + case 'tt_ids':
1247 + $formatted_args['_source'] = [
1248 + 'includes' => [
1249 + 'term_taxonomy_id',
1250 + ],
1251 + ];
1252 + break;
1027 1253 }
1028 1254
1029 - return $sort;
1255 + return $formatted_args;
1030 1256 }
1031 1257
1258 + /**
1259 + * Sets the ORDER BY clause for term queries to order terms by their term_id.
1260 + *
1261 + * @param array $clauses The SQL clauses array to modify.
1262 + * @return array The modified SQL clauses array with the ORDER BY clause set to term_id.
1263 + *
1264 + * @since 5.2.0
1265 + */
1266 + public function set_orderby( $clauses ): array {
1267 + $clauses['orderby'] = 'ORDER BY t.term_id';
1268 + return $clauses;
1269 + }
1032 1270 }