PluginProbe
ElasticPress / 4.2.0
ElasticPress v4.2.0
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
elasticpress / includes / classes / Indexable / Comment / Comment.php

Comment.php in ElasticPress 4.2.0, at includes/classes/Indexable/Comment/Comment.php

1,217 lines 29.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Comment indexable
4 *
5 * @since 3.6.0
6 * @package elasticpress
7 */
8
9 namespace ElasticPress\Indexable\Comment;
10
11 use ElasticPress\Indexable as Indexable;
12 use ElasticPress\Indexables as Indexables;
13 use ElasticPress\Elasticsearch as Elasticsearch;
14 use ElasticPress\Indexable\Post\DateQuery as DateQuery;
15 use \WP_Comment_Query as WP_Comment_Query;
16 use ElasticPress\Features as Features;
17
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit; // Exit if accessed directly.
20 }
21
22 /**
23 * Comment indexable class
24 */
25 class Comment extends Indexable {
26
27 /**
28 * Indexable slug
29 *
30 * @var string
31 * @since 3.6.0
32 */
33 public $slug = 'comment';
34
35 /**
36 * Create indexable and initialize dependencies
37 *
38 * @since 3.6.0
39 */
40 public function __construct() {
41 $this->labels = [
42 'plural' => esc_html__( 'Comments', 'elasticpress' ),
43 'singular' => esc_html__( 'Comment', 'elasticpress' ),
44 ];
45
46 $this->sync_manager = new SyncManager( $this->slug );
47 $this->query_integration = new QueryIntegration();
48 }
49
50 /**
51 * Format query vars into ES query
52 *
53 * @param array $query_vars WP_Comment_Query args.
54 * @since 3.6.0
55 * @return array
56 */
57 public function format_args( $query_vars ) {
58 /**
59 * Support `number` query var
60 */
61 if ( ! empty( $query_vars['number'] ) ) {
62 $number = (int) $query_vars['number'];
63 } else {
64 /**
65 * Set the maximum results window size.
66 *
67 * The request will return a HTTP 500 Internal Error if the size of the
68 * request is larger than the [index.max_result_window] parameter in ES.
69 * See the scroll api for a more efficient way to request large data sets.
70 *
71 * @return int The max results window size.
72 *
73 * @since 2.3.0
74 */
75 $number = apply_filters( 'ep_max_results_window', 10000 );
76 }
77
78 $formatted_args = [
79 'from' => 0,
80 'size' => $number,
81 ];
82
83 /**
84 * Support `offset` query var
85 */
86 if ( isset( $query_vars['offset'] ) ) {
87 $formatted_args['from'] = (int) $query_vars['offset'];
88 if ( empty( $query_vars['number'] ) ) {
89 $formatted_args['size'] -= (int) $formatted_args['from'];
90 }
91 }
92
93 /**
94 * Support `paged` query var
95 *
96 * If `offset` is used, that takes precendence
97 * over this.
98 */
99 if ( isset( $query_vars['paged'] ) && empty( $query_vars['offset'] ) && $query_vars['paged'] > 1 ) {
100 $formatted_args['from'] = $number * ( $query_vars['paged'] - 1 );
101 }
102
103 /**
104 * Support `order` and `orderby` query vars
105 */
106
107 // Set sort order, default is 'desc'.
108 if ( ! empty( $query_vars['order'] ) ) {
109 $order = $this->parse_order( $query_vars['order'] );
110 } else {
111 $order = 'desc';
112 }
113
114 // Default sort by comment date
115 if ( empty( $query_vars['orderby'] ) ) {
116 $query_vars['orderby'] = 'comment_date_gmt';
117 }
118
119 // Set sort type.
120 $formatted_args['sort'] = $this->parse_orderby( $query_vars['orderby'], $order, $query_vars );
121
122 $filter = [
123 'bool' => [
124 'must' => [],
125 ],
126 ];
127
128 $use_filters = false;
129
130 /**
131 * Support `author_email` query var
132 */
133 if ( ! empty( $query_vars['author_email'] ) ) {
134 $filter['bool']['must'][] = [
135 'term' => [
136 'comment_author_email.raw' => $query_vars['author_email'],
137 ],
138 ];
139
140 $use_filters = true;
141 }
142
143 /**
144 * Support `author_url` query var
145 */
146 if ( ! empty( $query_vars['author_url'] ) ) {
147 $filter['bool']['must'][] = [
148 'term' => [
149 'comment_author_url.raw' => $query_vars['author_url'],
150 ],
151 ];
152
153 $use_filters = true;
154 }
155
156 /**
157 * Support `user_id` query var
158 */
159 if ( ! empty( $query_vars['user_id'] ) ) {
160 $filter['bool']['must'][] = [
161 'term' => [
162 'user_id' => (int) $query_vars['user_id'],
163 ],
164 ];
165
166 $use_filters = true;
167 }
168
169 /**
170 * Support `author__in` query var
171 */
172 if ( ! empty( $query_vars['author__in'] ) ) {
173 $filter['bool']['must'][]['bool']['must'] = [
174 'terms' => [
175 'user_id' => array_values( (array) $query_vars['author__in'] ),
176 ],
177 ];
178
179 $use_filters = true;
180 }
181
182 /**
183 * Support `author__not_in` query var
184 */
185 if ( ! empty( $query_vars['author__not_in'] ) ) {
186 $filter['bool']['must'][]['bool']['must_not'] = [
187 'terms' => [
188 'user_id' => array_values( (array) $query_vars['author__not_in'] ),
189 ],
190 ];
191
192 $use_filters = true;
193 }
194
195 /**
196 * Support `comment__in` query var
197 */
198 if ( ! empty( $query_vars['comment__in'] ) ) {
199 $filter['bool']['must'][]['bool']['must'] = [
200 'terms' => [
201 'comment_ID' => array_values( (array) $query_vars['comment__in'] ),
202 ],
203 ];
204
205 $use_filters = true;
206 }
207
208 /**
209 * Support `comment__not_in` query var
210 */
211 if ( ! empty( $query_vars['comment__not_in'] ) ) {
212 $filter['bool']['must'][]['bool']['must_not'] = [
213 'terms' => [
214 'comment_ID' => array_values( (array) $query_vars['comment__not_in'] ),
215 ],
216 ];
217
218 $use_filters = true;
219 }
220
221 /**
222 * Support `date_query` query var
223 */
224 if ( ! empty( $query_vars['date_query'] ) ) {
225 $date_query = new DateQuery( $query_vars['date_query'] );
226 $date_filter = $date_query->get_es_filter();
227
228 if ( array_key_exists( 'and', $date_filter ) ) {
229 $filter['bool']['must'][] = $date_filter['and'];
230 $use_filters = true;
231 }
232 }
233
234 /**
235 * Support `fields` query var.
236 */
237 if ( isset( $query_vars['fields'] ) ) {
238 switch ( $query_vars['fields'] ) {
239 case 'ids':
240 $formatted_args['_source'] = [
241 'includes' => [
242 'comment_ID',
243 ],
244 ];
245 break;
246 }
247 }
248
249 /**
250 * Support `karma` query var.
251 */
252 if ( ! empty( $query_vars['karma'] ) || 0 === $query_vars['karma'] ) {
253 $filter['bool']['must'][]['bool']['must'] = [
254 'term' => [
255 'comment_karma' => $query_vars['karma'],
256 ],
257 ];
258
259 $use_filters = true;
260 }
261
262 $meta_queries = [];
263
264 /**
265 * Support `meta_key` and `meta_value` query args
266 */
267 if ( ! empty( $query_vars['meta_key'] ) ) {
268 $meta_query_array = [
269 'key' => $query_vars['meta_key'],
270 ];
271
272 if ( isset( $query_vars['meta_value'] ) ) {
273 $meta_query_array['value'] = $query_vars['meta_value'];
274 }
275
276 $meta_queries[] = $meta_query_array;
277 }
278
279 /**
280 * Support 'meta_query' query var.
281 */
282 if ( ! empty( $query_vars['meta_query'] ) ) {
283 $meta_queries = array_merge( $meta_queries, $query_vars['meta_query'] );
284 }
285
286 if ( ! empty( $meta_queries ) ) {
287 $built_meta_queries = $this->build_meta_query( $meta_queries );
288
289 if ( $built_meta_queries ) {
290 $filter['bool']['must'][] = $built_meta_queries;
291 $use_filters = true;
292 }
293 }
294
295 /**
296 * Support `hierarchical` query var
297 */
298 if ( ! empty( $query_vars['hierarchical'] ) && empty( $query_vars['parent'] ) ) {
299 $query_vars['parent'] = 0;
300 }
301
302 /**
303 * Support `parent` query var.
304 */
305 if ( ! empty( $query_vars['parent'] ) || 0 === $query_vars['parent'] ) {
306 $filter['bool']['must'][]['bool']['must'] = [
307 'term' => [
308 'comment_parent' => (int) $query_vars['parent'],
309 ],
310 ];
311
312 $use_filters = true;
313 }
314
315 /**
316 * Support `parent__in` query var
317 */
318 if ( ! empty( $query_vars['parent__in'] ) ) {
319 $filter['bool']['must'][]['bool']['must'] = [
320 'terms' => [
321 'comment_parent' => array_values( (array) $query_vars['parent__in'] ),
322 ],
323 ];
324
325 $use_filters = true;
326 }
327
328 /**
329 * Support `parent__not_in` query var
330 */
331 if ( ! empty( $query_vars['parent__not_in'] ) ) {
332 $filter['bool']['must'][]['bool']['must_not'] = [
333 'terms' => [
334 'comment_parent' => array_values( (array) $query_vars['parent__not_in'] ),
335 ],
336 ];
337
338 $use_filters = true;
339 }
340
341 /**
342 * Support `post_author` query var.
343 */
344 if ( ! empty( $query_vars['post_author'] ) ) {
345 $filter['bool']['must'][]['bool']['must'] = [
346 'term' => [
347 'comment_post_author_ID' => (int) $query_vars['post_author'],
348 ],
349 ];
350
351 $use_filters = true;
352 }
353
354 /**
355 * Support `post_author__in` query var
356 */
357 if ( ! empty( $query_vars['post_author__in'] ) ) {
358 $filter['bool']['must'][]['bool']['must'] = [
359 'terms' => [
360 'comment_post_author_ID' => array_values( (array) $query_vars['post_author__in'] ),
361 ],
362 ];
363
364 $use_filters = true;
365 }
366
367 /**
368 * Support `post_author__not_in` query var
369 */
370 if ( ! empty( $query_vars['post_author__not_in'] ) ) {
371 $filter['bool']['must'][]['bool']['must_not'] = [
372 'terms' => [
373 'comment_post_author_ID' => array_values( (array) $query_vars['post_author__not_in'] ),
374 ],
375 ];
376
377 $use_filters = true;
378 }
379
380 /**
381 * Support `post_id` query var.
382 */
383 if ( ! empty( $query_vars['post_id'] ) ) {
384 $filter['bool']['must'][]['bool']['must'] = [
385 'term' => [
386 'comment_post_ID' => (int) $query_vars['post_id'],
387 ],
388 ];
389
390 $use_filters = true;
391 }
392
393 /**
394 * Support `post__in` query var
395 */
396 if ( ! empty( $query_vars['post__in'] ) ) {
397 $filter['bool']['must'][]['bool']['must'] = [
398 'terms' => [
399 'comment_post_ID' => array_values( (array) $query_vars['post__in'] ),
400 ],
401 ];
402
403 $use_filters = true;
404 }
405
406 /**
407 * Support `post__not_in` query var
408 */
409 if ( ! empty( $query_vars['post__not_in'] ) ) {
410 $filter['bool']['must'][]['bool']['must_not'] = [
411 'terms' => [
412 'comment_post_ID' => array_values( (array) $query_vars['post__not_in'] ),
413 ],
414 ];
415
416 $use_filters = true;
417 }
418
419 /**
420 * Support `post_status` query var
421 */
422 if ( ! empty( $query_vars['post_status'] ) && 'any' !== $query_vars['post_status'] ) {
423 $post_status = (array) ( is_string( $query_vars['post_status'] ) ? explode( ',', $query_vars['post_status'] ) : $query_vars['post_status'] );
424 $post_status = array_map( 'trim', $post_status );
425 $terms_map_name = 'terms';
426
427 if ( count( $post_status ) < 2 ) {
428 $terms_map_name = 'term';
429 $post_status = $post_status[0];
430 }
431
432 $filter['bool']['must'][] = array(
433 $terms_map_name => array(
434 'comment_post_status' => $post_status,
435 ),
436 );
437
438 $use_filters = true;
439 }
440
441 /**
442 * Support `post_type` query var.
443 */
444 if ( ! empty( $query_vars['post_type'] ) ) {
445 $filter['bool']['must'][]['bool']['must'] = [
446 'term' => [
447 'comment_post_type.raw' => $query_vars['post_type'],
448 ],
449 ];
450
451 $use_filters = true;
452 }
453
454 /**
455 * Support `post_name` query var.
456 */
457 if ( ! empty( $query_vars['post_name'] ) ) {
458 $filter['bool']['must'][]['bool']['must'] = [
459 'term' => [
460 'comment_post_name.raw' => $query_vars['post_name'],
461 ],
462 ];
463
464 $use_filters = true;
465 }
466
467 /**
468 * Support `post_parent` query var.
469 */
470 if ( ! empty( $query_vars['post_parent'] ) ) {
471 $filter['bool']['must'][]['bool']['must'] = [
472 'term' => [
473 'comment_post_parent' => (int) $query_vars['post_parent'],
474 ],
475 ];
476
477 $use_filters = true;
478 }
479
480 /**
481 * Support `search` query_var
482 */
483 if ( ! empty( $query_vars['search'] ) ) {
484
485 $search = ! empty( $query_vars['search'] ) ? $query_vars['search'] : '';
486 $search_fields = [];
487
488 /**
489 * Allow for search field specification
490 */
491 if ( ! empty( $query_vars['search_fields'] ) ) {
492 $search_fields = $query_vars['search_fields'];
493 }
494
495 if ( ! empty( $search_fields ) ) {
496 $prepared_search_fields = [];
497
498 if ( ! empty( $search_fields['meta'] ) ) {
499 $metas = (array) $search_fields['meta'];
500
501 foreach ( $metas as $meta ) {
502 $prepared_search_fields[] = 'meta.' . $meta . '.value';
503 }
504
505 unset( $search_fields['meta'] );
506 }
507
508 $prepared_search_fields = array_merge( $search_fields, $prepared_search_fields );
509 } else {
510 $prepared_search_fields = [
511 'comment_author',
512 'comment_author_email',
513 'comment_author_url',
514 'comment_content',
515 ];
516 }
517
518 /**
519 * Filter default comment search fields
520 *
521 * If you are using the weighting engine, this filter should not be used.
522 * Instead, you should use the ep_weighting_configuration_for_search filter.
523 *
524 * @hook ep_comment_search_fields
525 * @since 3.6.0
526 * @param {array} $search_fields Default search fields
527 * @param {array} $query_vars WP_Comment_Query args
528 * @return {array} New defaults
529 */
530 $prepared_search_fields = apply_filters( 'ep_comment_search_fields', $prepared_search_fields, $query_vars );
531
532 $query = [
533 'bool' => [
534 'should' => [
535 [
536 'multi_match' => [
537 'query' => $search,
538 'type' => 'phrase',
539 'fields' => $prepared_search_fields,
540 /**
541 * Filter boost for comment match phrase query
542 *
543 * @hook ep_comment_match_phrase_boost
544 * @since 3.6.0
545 * @param {int} $boost Phrase boost
546 * @param {array} $prepared_search_fields Search fields
547 * @param {array} $query_vars Query variables
548 * @return {int} New phrase boost
549 */
550 'boost' => apply_filters( 'ep_comment_match_phrase_boost', 4, $prepared_search_fields, $query_vars ),
551 ],
552 ],
553 [
554 'multi_match' => [
555 'query' => $search,
556 'fields' => $prepared_search_fields,
557 /**
558 * Filter boost for comment match query
559 *
560 * @hook ep_comment_match_boost
561 * @param {int} $boost Boost
562 * @param {array} $prepared_search_fields Search fields
563 * @param {array} $query_vars Query variables
564 * @return {int} New boost
565 */
566 'boost' => apply_filters( 'ep_comment_match_boost', 2, $prepared_search_fields, $query_vars ),
567 'fuzziness' => 0,
568 'operator' => 'and',
569 ],
570 ],
571 [
572 'multi_match' => [
573 'fields' => $prepared_search_fields,
574 'query' => $search,
575 /**
576 * Filter fuzziness for post query
577 *
578 * @hook ep_comment_fuzziness_arg
579 * @since 3.6.0
580 * @param {int} $fuzziness Fuzziness
581 * @param {array} $prepared_search_fields Search fields
582 * @param {array} $query_vars Query variables
583 * @return {int} New fuzziness
584 */
585 'fuzziness' => apply_filters( 'ep_comment_fuzziness_arg', 1, $prepared_search_fields, $query_vars ),
586 ],
587 ],
588 ],
589 ],
590 ];
591
592 /**
593 * Filter formatted Elasticsearch post query (only contains query part)
594 *
595 * @hook ep_comment_formatted_args_query
596 * @since 3.6.0
597 * @param {array} $query Current query
598 * @param {array} $query_vars Query variables
599 * @param {string} $search_text Search text
600 * @param {array} $search_fields Search fields
601 * @return {array} New query
602 */
603 $formatted_args['query'] = apply_filters(
604 'ep_comment_formatted_args_query',
605 $query,
606 $query_vars,
607 $search,
608 $prepared_search_fields
609 );
610 } else {
611 $formatted_args['query']['match_all'] = [
612 'boost' => 1,
613 ];
614 }
615
616 /**
617 * Support `status` query var
618 */
619 if ( ! empty( $query_vars['status'] ) && 'all' !== $query_vars['status'] ) {
620 $comment_stati = (array) ( is_string( $query_vars['status'] ) ? explode( ',', $query_vars['status'] ) : $query_vars['status'] );
621 $comment_stati = array_map( 'trim', $comment_stati );
622
623 foreach ( $comment_stati as $key => $status ) {
624 if ( 'hold' === $status ) {
625 $comment_stati[ $key ] = 0;
626 }
627
628 if ( 'approve' === $status ) {
629 $comment_stati[ $key ] = 1;
630 }
631 }
632
633 $terms_map_name = 'terms';
634
635 if ( count( $comment_stati ) < 2 ) {
636 $terms_map_name = 'term';
637 $comment_stati = $comment_stati[0];
638 }
639
640 /**
641 * Support `include_unapproved` query var
642 */
643 if ( ! empty( $query_vars['include_unapproved'] ) ) {
644 $include_unapproved = wp_parse_list( $query_vars['include_unapproved'] );
645 $unapproved_ids = [];
646 $unapproved_emails = [];
647
648 foreach ( $include_unapproved as $unapproved_identifier ) {
649 // Numeric values are assumed to be user ids.
650 if ( is_numeric( $unapproved_identifier ) ) {
651 $unapproved_ids[] = $unapproved_identifier;
652
653 // Otherwise we assume it's an email address.
654 } else {
655 $unapproved_emails[] = $unapproved_identifier;
656 }
657 }
658
659 $filter['bool']['must'][]['bool']['should'] = [
660 [
661 $terms_map_name => [
662 'comment_approved' => $comment_stati,
663 ],
664 ],
665 [
666 'terms' => [
667 'user_id' => array_values( array_map( 'absint', $unapproved_ids ) ),
668 ],
669 ],
670 [
671 'terms' => [
672 'comment_author_email.raw' => array_values( $unapproved_emails ),
673 ],
674 ],
675 ];
676 } else {
677 $filter['bool']['must'][] = [
678 $terms_map_name => [
679 'comment_approved' => $comment_stati,
680 ],
681 ];
682 }
683
684 $use_filters = true;
685 }
686
687 /**
688 * Support `type` query var
689 */
690 if ( ! empty( $query_vars['type'] ) ) {
691 $types = (array) ( is_string( $query_vars['type'] ) ? explode( ',', $query_vars['type'] ) : $query_vars['type'] );
692 $types = array_map( 'trim', $types );
693
694 $terms_map_name = 'terms';
695
696 if ( count( $types ) < 2 ) {
697 $terms_map_name = 'term';
698 $types = $types[0];
699 }
700
701 $filter['bool']['must'][] = [
702 $terms_map_name => [
703 'comment_type.raw' => $types,
704 ],
705 ];
706
707 $use_filters = true;
708 }
709
710 /**
711 * Support `type__in` query var
712 */
713 if ( ! empty( $query_vars['type__in'] ) ) {
714 $filter['bool']['must'][]['bool']['must'] = [
715 'terms' => [
716 'comment_type.raw' => array_values( (array) $query_vars['type__in'] ),
717 ],
718 ];
719
720 $use_filters = true;
721 }
722
723 /**
724 * Support `type__not_in` query var
725 */
726 if ( ! empty( $query_vars['type__not_in'] ) ) {
727 $filter['bool']['must'][]['bool']['must_not'] = [
728 'terms' => [
729 'comment_type.raw' => array_values( (array) $query_vars['type__not_in'] ),
730 ],
731 ];
732
733 $use_filters = true;
734 }
735
736 if ( $use_filters ) {
737 $formatted_args['post_filter'] = $filter;
738 }
739
740 /**
741 * Filter formatted Elasticsearch query (entire query)
742 *
743 * @hook ep_comment_formatted_args
744 * @since 3.6.0
745 * @param {array} $formatted_args Formatted Elasticsearch query
746 * @param {array} $query_vars WP_Comment_Query args
747 * @return {array} New query
748 */
749 return apply_filters( 'ep_comment_formatted_args', $formatted_args, $query_vars );
750 }
751
752 /**
753 * Generate the mapping array
754 *
755 * @since 4.1.0
756 * @return array
757 */
758 public function generate_mapping() {
759 $es_version = Elasticsearch::factory()->get_elasticsearch_version();
760
761 if ( empty( $es_version ) ) {
762 /**
763 * Filter fallback Elasticsearch version
764 *
765 * @hook ep_fallback_elasticsearch_version
766 * @param {string} $version Fall back Elasticsearch version
767 * @return {string} New version
768 */
769 $es_version = apply_filters( 'ep_fallback_elasticsearch_version', '2.0' );
770 }
771
772 $mapping_file = 'initial.php';
773
774 if ( version_compare( $es_version, '5.0', '<' ) ) {
775 $mapping_file = 'pre-5-0.php';
776 } elseif ( version_compare( $es_version, '7.0', '>=' ) ) {
777 $mapping_file = '7-0.php';
778 }
779
780 /**
781 * Filter comment indexable mapping file
782 *
783 * @hook ep_comment_mapping_file
784 * @since 3.6.0
785 * @param {string} $file Path to file
786 * @return {string} New file path
787 */
788 $mapping = require apply_filters( 'ep_comment_mapping_file', __DIR__ . '/../../../mappings/comment/' . $mapping_file );
789
790 /**
791 * Filter comment indexable mapping
792 *
793 * @hook ep_comment_mapping
794 * @since 3.6.0
795 * @param {array} $mapping Mapping
796 * @return {array} New mapping
797 */
798 $mapping = apply_filters( 'ep_comment_mapping', $mapping );
799
800 return $mapping;
801 }
802
803 /**
804 * Returns indexable comment types
805 *
806 * @since 3.6.0
807 * @return array
808 */
809 public function get_indexable_comment_types() {
810 $comment_types = [ 'comment' ];
811
812 if ( Features::factory()->registered_features['woocommerce']->is_active() ) {
813 $comment_types[] = 'review';
814 }
815
816 /**
817 * Filter indexable comment types
818 *
819 * @hook ep_indexable_comment_types
820 * @since 3.6.0
821 * @param {array} $comment_types Indexable comment types
822 * @return {array} comment types
823 */
824 return apply_filters( 'ep_indexable_comment_types', $comment_types );
825 }
826
827 /**
828 * Returns indexable comment status
829 *
830 * @since 3.6.0
831 * @return array
832 */
833 public function get_indexable_comment_status() {
834 $comment_status = [ '1' ];
835
836 /**
837 * Filter indexable comment status
838 *
839 * @hook ep_indexable_comment_status
840 * @since 3.6.0
841 * @param {array} $comment_status Indexable comment status
842 * @return {array} comment status
843 */
844 return apply_filters( 'ep_indexable_comment_status', $comment_status );
845 }
846
847 /**
848 * Query DB for comments
849 *
850 * @param array $args Query arguments
851 * @since 3.6.0
852 * @return array
853 */
854 public function query_db( $args ) {
855
856 $defaults = [
857 'type' => $this->get_indexable_comment_types(),
858 'status' => $this->get_indexable_comment_status(),
859 'post_type' => Indexables::factory()->get( 'post' )->get_indexable_post_types(),
860 'post_status' => Indexables::factory()->get( 'post' )->get_indexable_post_status(),
861 'number' => $this->get_bulk_items_per_page(),
862 'offset' => 0,
863 'orderby' => 'comment_ID',
864 'order' => 'desc',
865 ];
866
867 if ( isset( $args['per_page'] ) ) {
868 $args['number'] = $args['per_page'];
869 }
870
871 /**
872 * Filter database arguments for comment query
873 *
874 * @hook ep_comment_query_db_args
875 * @param {array} $args Query arguments based to WP_Comment_Query
876 * @since 3.6.0
877 * @return {array} New arguments
878 */
879 $args = apply_filters( 'ep_comment_query_db_args', wp_parse_args( $args, $defaults ) );
880
881 $all_query_args = $args;
882
883 unset( $all_query_args['number'] );
884 unset( $all_query_args['offset'] );
885
886 /**
887 * Filter database arguments for term count query
888 *
889 * @hook ep_comment_all_query_db_args
890 * @param {array} $args Query arguments based to WP_Comment_Query
891 * @since 3.6.0
892 * @return {array} New arguments
893 */
894 $all_query = new WP_Comment_Query( apply_filters( 'ep_comment_all_query_db_args', $all_query_args, $args ) );
895
896 $total_objects = count( $all_query->comments );
897
898 if ( ! empty( $args['offset'] ) ) {
899 if ( (int) $args['offset'] >= $total_objects ) {
900 $total_objects = 0;
901 }
902 }
903
904 $query = new WP_Comment_Query( $args );
905
906 if ( is_array( $query->comments ) ) {
907 array_walk( $query->comments, [ $this, 'remap_comments' ] );
908 }
909
910 return [
911 'objects' => $query->comments,
912 'total_objects' => $total_objects,
913 ];
914 }
915
916 /**
917 * Prepare a comment document for indexing
918 *
919 * @param int $comment_id Comment ID
920 * @since 3.6.0
921 * @return bool|array
922 */
923 public function prepare_document( $comment_id ) {
924 $comment = get_comment( $comment_id );
925
926 if ( ! $comment || ! is_a( $comment, 'WP_Comment' ) ) {
927 return false;
928 }
929
930 $comment_post = get_post( $comment->comment_post_ID );
931
932 $comment_args = [
933 'comment_ID' => $comment->comment_ID,
934 'ID' => $comment->comment_ID,
935 'comment_post_ID' => $comment->comment_post_ID,
936 'comment_post_author_ID' => $comment_post->post_author,
937 'comment_post_status' => $comment_post->post_status,
938 'comment_post_type' => $comment_post->post_type,
939 'comment_post_name' => $comment_post->post_name,
940 'comment_post_parent' => $comment_post->post_parent,
941 'comment_author' => $comment->comment_author,
942 'comment_author_email' => $comment->comment_author_email,
943 'comment_author_url' => $comment->comment_author_url,
944 'comment_author_IP' => $comment->comment_author_IP,
945 'comment_date' => $comment->comment_date,
946 'comment_date_gmt' => $comment->comment_date_gmt,
947 'comment_content' => $comment->comment_content,
948 'comment_karma' => $comment->comment_karma,
949 'comment_approved' => $comment->comment_approved,
950 'comment_agent' => $comment->comment_agent,
951 'comment_type' => $comment->comment_type ? $comment->comment_type : 'comment',
952 'comment_parent' => $comment->comment_parent,
953 'user_id' => $comment->user_id,
954 'meta' => $this->prepare_meta_types( $this->prepare_meta( $comment->comment_ID ) ),
955 ];
956
957 /**
958 * Filter sync arguments for a comment.
959 *
960 * @hook ep_comment_sync_args
961 * @param {array} $comment_args Comment arguments
962 * @param {int} $comment_id Comment ID
963 * @return {array} New arguments
964 */
965 $comment_args = apply_filters( 'ep_comment_sync_args', $comment_args, $comment_id );
966
967 return $comment_args;
968 }
969
970 /**
971 * Rebuild our comment objects to match the fields we need.
972 *
973 * In particular, result of WP_Comment_Query does not
974 * include an "id" field, which our index command
975 * expects.
976 *
977 * @param object $value Comment object
978 * @since 3.6.0
979 * @return void Returns by reference
980 */
981 public function remap_comments( &$value ) {
982 $value = (object) [
983 'ID' => $value->comment_ID,
984 'comment_ID' => $value->comment_ID,
985 'comment_post_ID' => $value->comment_post_ID,
986 'comment_author' => $value->comment_author,
987 'comment_author_email' => $value->comment_author_email,
988 'comment_author_url' => $value->comment_author_url,
989 'comment_author_IP' => $value->comment_author_IP,
990 'comment_date' => $value->comment_date,
991 'comment_date_gmt' => $value->comment_date_gmt,
992 'comment_content' => $value->comment_content,
993 'comment_karma' => $value->comment_karma,
994 'comment_approved' => $value->comment_approved,
995 'comment_agent' => $value->comment_agent,
996 'comment_type' => $value->comment_type,
997 'comment_parent' => $value->comment_parent,
998 'user_id' => $value->user_id,
999 ];
1000 }
1001
1002 /**
1003 * Prepare meta to send to ES
1004 *
1005 * @param int $comment_id Comment ID
1006 * @since 3.6.0
1007 * @return array
1008 */
1009 public function prepare_meta( $comment_id ) {
1010 $meta = (array) get_comment_meta( $comment_id );
1011
1012 if ( empty( $meta ) ) {
1013 return [];
1014 }
1015
1016 $prepared_meta = [];
1017
1018 /**
1019 * Filter index-able private meta
1020 *
1021 * Allows for specifying private meta keys that may be indexed in the same manner as public meta keys.
1022 *
1023 * @since 3.6.0
1024 *
1025 * @param array Array of index-able private meta keys.
1026 * @param int $comment_id Comment ID.
1027 */
1028 $allowed_protected_keys = apply_filters(
1029 'ep_prepare_comment_meta_allowed_protected_keys',
1030 [],
1031 $comment_id
1032 );
1033
1034 /**
1035 * Filter non-indexed public meta
1036 *
1037 * Allows for specifying public meta keys that should be excluded from the ElasticPress index.
1038 *
1039 * @since 3.6.0
1040 *
1041 * @param array Array of public meta keys to exclude from index.
1042 * @param int $comment_id Comment ID.
1043 */
1044 $excluded_public_keys = apply_filters(
1045 'ep_prepare_comment_meta_excluded_public_keys',
1046 [],
1047 $comment_id
1048 );
1049
1050 foreach ( $meta as $key => $value ) {
1051
1052 $allow_index = false;
1053
1054 if ( is_protected_meta( $key ) ) {
1055
1056 if ( true === $allowed_protected_keys || in_array( $key, $allowed_protected_keys, true ) ) {
1057 $allow_index = true;
1058 }
1059 } else {
1060
1061 if ( true !== $excluded_public_keys && ! in_array( $key, $excluded_public_keys, true ) ) {
1062 $allow_index = true;
1063 }
1064 }
1065
1066 /**
1067 * Filter force allow a meta key
1068 *
1069 * @hook ep_prepare_comment_meta_allowed_key
1070 * @since 3.6.0
1071 * @param {bool} $allowed True to allow the key
1072 * @param {string} $key Meta key
1073 * @param {int} $comment_id Comment ID
1074 * @return {bool} New allowed value
1075 */
1076 if ( true === $allow_index || apply_filters( 'ep_prepare_comment_meta_allowed_key', false, $key, $comment_id ) ) {
1077 $prepared_meta[ $key ] = maybe_unserialize( $value );
1078 }
1079 }
1080
1081 return $prepared_meta;
1082 }
1083
1084 /**
1085 * Parse an 'order' query variable and cast it to asc or desc as necessary.
1086 *
1087 * @access protected
1088 *
1089 * @param string $order The 'order' query variable.
1090 * @since 3.6.0
1091 * @return string The sanitized 'order' query variable.
1092 */
1093 protected function parse_order( $order ) {
1094 if ( ! is_string( $order ) || empty( $order ) ) {
1095 return 'desc';
1096 }
1097
1098 if ( 'ASC' === strtoupper( $order ) ) {
1099 return 'asc';
1100 } else {
1101 return 'desc';
1102 }
1103 }
1104
1105 /**
1106 * Convert the alias to a properly-prefixed sort value.
1107 *
1108 * @access protected
1109 *
1110 * @param string $orderby Alias or path for the field to order by.
1111 * @param string $order Order direction
1112 * @param array $args Query args
1113 * @since 3.6.0
1114 * @return array
1115 */
1116 protected function parse_orderby( $orderby, $order, $args ) {
1117 $sort = [];
1118
1119 if ( empty( $orderby ) ) {
1120 return $sort;
1121 }
1122
1123 switch ( $orderby ) {
1124 case 'comment_agent':
1125 $orderby_field = 'comment_agent.raw';
1126 break;
1127
1128 case 'comment_approved':
1129 $orderby_field = 'comment_approved.raw';
1130 break;
1131
1132 case 'comment_author':
1133 $orderby_field = 'comment_author.raw';
1134 break;
1135
1136 case 'comment_author_email':
1137 $orderby_field = 'comment_author_email.raw';
1138 break;
1139
1140 case 'comment_author_IP':
1141 $orderby_field = 'comment_author_IP.raw';
1142 break;
1143
1144 case 'comment_author_url':
1145 $orderby_field = 'comment_author_url.raw';
1146 break;
1147
1148 case 'comment_content':
1149 $orderby_field = 'comment_content.raw';
1150 break;
1151
1152 case 'comment_date':
1153 $orderby_field = 'comment_date';
1154 break;
1155
1156 case 'comment_date_gmt':
1157 $orderby_field = 'comment_date_gmt';
1158 break;
1159
1160 case 'comment_ID':
1161 $orderby_field = 'comment_ID';
1162 break;
1163
1164 case 'comment_karma':
1165 $orderby_field = 'comment_karma';
1166 break;
1167
1168 case 'comment_parent':
1169 $orderby_field = 'comment_parent';
1170 break;
1171
1172 case 'comment_post_ID':
1173 $orderby_field = 'comment_post_ID';
1174 break;
1175
1176 case 'comment_type':
1177 $orderby_field = 'comment_type.raw';
1178 break;
1179
1180 case 'comment_post_type':
1181 $orderby_field = 'comment_post_type.raw';
1182 break;
1183
1184 case 'user_id':
1185 $orderby_field = 'user_id';
1186 break;
1187
1188 case 'meta_value':
1189 if ( ! empty( $args['meta_key'] ) ) {
1190 $orderby_field = 'meta.' . $args['meta_key'] . '.value';
1191 }
1192 break;
1193
1194 case 'meta_value_num':
1195 if ( ! empty( $args['meta_key'] ) ) {
1196 $orderby_field = 'meta.' . $args['meta_key'] . '.long';
1197 }
1198 break;
1199
1200 default:
1201 $orderby_field = $orderby;
1202 break;
1203 }
1204
1205 if ( ! empty( $orderby_field ) ) {
1206 $sort[] = [
1207 $orderby_field => [
1208 'order' => $order,
1209 ],
1210 ];
1211 }
1212
1213 return $sort;
1214 }
1215
1216 }
1217