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/Comment/Comment.php +211 -206 4.2.05.3.5 View file →
@@ -7,14 +7,14 @@
7 7 */
8 8
9 9 namespace ElasticPress\Indexable\Comment;
10 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;
11 +use WP_Comment_Query;
12 +use ElasticPress\Elasticsearch;
13 +use ElasticPress\Features;
14 +use ElasticPress\Indexable;
15 +use ElasticPress\Indexable\Post\DateQuery;
16 +use ElasticPress\Indexables;
17 17
18 18 if ( ! defined( 'ABSPATH' ) ) {
19 19 exit; // Exit if accessed directly.
20 20 }
@@ -32,13 +32,23 @@
32 32 */
33 33 public $slug = 'comment';
34 34
35 35 /**
36 - * Create indexable and initialize dependencies
36 + * Flag to indicate if the indexable has support for
37 + * `id_range` pagination method during a sync.
37 38 *
38 - * @since 3.6.0
39 + * @var boolean
40 + * @since 5.2.0
39 41 */
40 - public function __construct() {
42 + public $support_indexing_advanced_pagination = true;
43 +
44 + /**
45 + * Instantiate the indexable SyncManager and QueryIntegration, the main responsibles for the WP integration.
46 + *
47 + * @since 4.5.0
48 + * @return void
49 + */
50 + public function setup() {
41 51 $this->labels = [
42 52 'plural' => esc_html__( 'Comments', 'elasticpress' ),
43 53 'singular' => esc_html__( 'Comment', 'elasticpress' ),
44 54 ];
@@ -92,9 +102,9 @@
92 102
93 103 /**
94 104 * Support `paged` query var
95 105 *
96 - * If `offset` is used, that takes precendence
106 + * If `offset` is used, that takes precedence
97 107 * over this.
98 108 */
99 109 if ( isset( $query_vars['paged'] ) && empty( $query_vars['offset'] ) && $query_vars['paged'] > 1 ) {
100 110 $formatted_args['from'] = $number * ( $query_vars['paged'] - 1 );
@@ -442,10 +452,10 @@
442 452 * Support `post_type` query var.
443 453 */
444 454 if ( ! empty( $query_vars['post_type'] ) ) {
445 455 $filter['bool']['must'][]['bool']['must'] = [
446 - 'term' => [
447 - 'comment_post_type.raw' => $query_vars['post_type'],
456 + 'terms' => [
457 + 'comment_post_type.raw' => array_values( (array) $query_vars['post_type'] ),
448 458 ],
449 459 ];
450 460
451 461 $use_filters = true;
@@ -528,86 +538,10 @@
528 538 * @return {array} New defaults
529 539 */
530 540 $prepared_search_fields = apply_filters( 'ep_comment_search_fields', $prepared_search_fields, $query_vars );
531 541
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 - );
542 + $search_algorithm = $this->get_search_algorithm( $search, $prepared_search_fields, $query_vars );
543 + $formatted_args['query'] = $search_algorithm->get_query( 'comment', $search, $prepared_search_fields, $query_vars );
610 544 } else {
611 545 $formatted_args['query']['match_all'] = [
612 546 'boost' => 1,
613 547 ];
@@ -768,14 +702,14 @@
768 702 */
769 703 $es_version = apply_filters( 'ep_fallback_elasticsearch_version', '2.0' );
770 704 }
771 705
772 - $mapping_file = 'initial.php';
706 + $es_version = (string) $es_version;
773 707
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';
708 + $mapping_file = '7-0.php';
709 +
710 + if ( version_compare( $es_version, '7.0', '<' ) ) {
711 + $mapping_file = 'initial.php';
778 712 }
779 713
780 714 /**
781 715 * Filter comment indexable mapping file
@@ -851,18 +785,19 @@
851 785 * @since 3.6.0
852 786 * @return array
853 787 */
854 788 public function query_db( $args ) {
855 -
856 789 $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',
790 + 'type' => $this->get_indexable_comment_types(),
791 + 'status' => $this->get_indexable_comment_status(),
792 + 'post_type' => Indexables::factory()->get( 'post' )->get_indexable_post_types(),
793 + 'post_status' => Indexables::factory()->get( 'post' )->get_indexable_post_status(),
794 + 'number' => $this->get_bulk_items_per_page(),
795 + 'offset' => 0,
796 + 'orderby' => 'comment_ID',
797 + 'order' => 'desc',
798 + 'ep_indexing_advanced_pagination' => true,
799 + 'no_found_rows' => false,
865 800 ];
866 801
867 802 if ( isset( $args['per_page'] ) ) {
868 803 $args['number'] = $args['per_page'];
@@ -867,8 +802,16 @@
867 802 if ( isset( $args['per_page'] ) ) {
868 803 $args['number'] = $args['per_page'];
869 804 }
870 805
806 + if ( isset( $args['include'] ) ) {
807 + $args['comment__in'] = $args['include'];
808 + }
809 +
810 + if ( isset( $args['exclude'] ) ) {
811 + $args['comment__not_in'] = $args['exclude'];
812 + }
813 +
871 814 /**
872 815 * Filter database arguments for comment query
873 816 *
874 817 * @hook ep_comment_query_db_args
@@ -881,28 +824,46 @@
881 824 $all_query_args = $args;
882 825
883 826 unset( $all_query_args['number'] );
884 827 unset( $all_query_args['offset'] );
828 + $all_query_args['count'] = true;
885 829
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 ) );
830 + if ( isset( $args['comment__in'] ) || 0 < $args['offset'] ) {
831 + // Disable advanced pagination. Not useful if only indexing specific IDs.
832 + $args['ep_indexing_advanced_pagination'] = false;
833 + }
895 834
896 - $total_objects = count( $all_query->comments );
835 + // Explicitly set the orderby to ID to prevent accidental modifications by other code.
836 + add_filter( 'comments_clauses', [ $this, 'set_orderby' ], 9999, 2 );
897 837
898 - if ( ! empty( $args['offset'] ) ) {
899 - if ( (int) $args['offset'] >= $total_objects ) {
900 - $total_objects = 0;
901 - }
838 + // Enforce the following query args during advanced pagination to ensure things work correctly.
839 + if ( $args['ep_indexing_advanced_pagination'] ) {
840 + $args = array_merge(
841 + $args,
842 + [
843 + 'suppress_filters' => false,
844 + 'orderby' => 'comment_ID',
845 + 'order' => 'desc',
846 + 'paged' => 1,
847 + 'offset' => 0,
848 + ]
849 + );
850 +
851 + // It's important to pass a custom cache domain. By default, WordPress caches results based on the default query arguments and doesn't account for custom arguments. @see \WP_Comment_Query::get_comments()
852 + $cache_key = md5( get_current_blog_id() . wp_json_encode( $args ) );
853 + $args['cache_domain'] = 'elasticpress-comment-indexable-' . $cache_key;
854 +
855 + add_filter( 'comments_clauses', array( $this, 'bulk_indexing_filter_comments_where' ), 9999, 2 );
856 +
857 + $query = new WP_Comment_Query( $args );
858 + $total_objects = $this->get_total_objects_for_query( $args );
859 + remove_filter( 'comments_clauses', array( $this, 'bulk_indexing_filter_comments_where' ), 9999, 2 );
860 + } else {
861 + $query = new WP_Comment_Query( $args );
862 + $total_objects = $query->found_comments;
902 863 }
903 864
904 - $query = new WP_Comment_Query( $args );
865 + remove_filter( 'comments_clauses', [ $this, 'set_orderby' ], 9999, 2 );
905 866
906 867 if ( is_array( $query->comments ) ) {
907 868 array_walk( $query->comments, [ $this, 'remap_comments' ] );
908 869 }
@@ -913,8 +874,79 @@
913 874 ];
914 875 }
915 876
916 877 /**
878 + * Filters the WHERE clause of the SQL query used for bulk indexing comments by modifying it to include a range
879 + * of comment IDs based on advanced pagination parameters.
880 + *
881 + * @param array $clauses Associative array of the clauses for the query.
882 + * @param \WP_Comment_Query $query The current WP_Comment_Query instance.
883 + *
884 + * @return array Modified SQL query clauses.
885 + */
886 + public function bulk_indexing_filter_comments_where( $clauses, $query ) {
887 + global $wpdb;
888 +
889 + $using_advanced_pagination = $this->get_query_var( $query, 'ep_indexing_advanced_pagination', false );
890 +
891 + if ( $using_advanced_pagination ) {
892 + $requested_upper_limit_id = $this->get_query_var( $query, 'ep_indexing_upper_limit_object_id', PHP_INT_MAX );
893 + $requested_lower_limit_object_id = $this->get_query_var( $query, 'ep_indexing_lower_limit_object_id', 0 );
894 + $last_processed_id = $this->get_query_var( $query, 'ep_indexing_last_processed_object_id', null );
895 +
896 + // On the first loopthrough we begin with the requested upper limit ID. Afterwards, use the last processed ID to paginate.
897 + $upper_limit_range_object_id = $requested_upper_limit_id;
898 + if ( is_numeric( $last_processed_id ) ) {
899 + $upper_limit_range_object_id = $last_processed_id - 1;
900 + }
901 +
902 + // Sanitize. Abort if unexpected data at this point.
903 + if ( ! is_numeric( $upper_limit_range_object_id ) || ! is_numeric( $requested_lower_limit_object_id ) ) {
904 + return $clauses;
905 + }
906 +
907 + $range = [
908 + 'upper_limit' => "{$wpdb->comments}.comment_ID <= {$upper_limit_range_object_id}",
909 + 'lower_limit' => "{$wpdb->comments}.comment_ID >= {$requested_lower_limit_object_id}",
910 + ];
911 +
912 + // Skip the end range if it's unnecessary.
913 + $skip_ending_range = 0 === $requested_lower_limit_object_id;
914 + $where = $clauses['where'];
915 + $where = $skip_ending_range ? " {$range['upper_limit']} AND {$where}" : " {$range['upper_limit']} AND {$range['lower_limit']} AND {$where}";
916 +
917 + $clauses['where'] = $where;
918 + }
919 +
920 + return $clauses;
921 + }
922 +
923 + /**
924 + * Get the total number of comments for a given query.
925 + *
926 + * @param array $query_args The query args.
927 + * @return int The query result's found_comments.
928 + */
929 + protected function get_total_objects_for_query( $query_args ) {
930 + $normalized_query_args = array_merge(
931 + $query_args,
932 + [
933 + 'offset' => 0,
934 + 'paged' => 1,
935 + 'posts_per_page' => 1,
936 + 'no_found_rows' => false,
937 + 'ep_indexing_last_processed_object_id' => null,
938 + ]
939 + );
940 +
941 + $cache_key = md5( get_current_blog_id() . wp_json_encode( $normalized_query_args ) );
942 +
943 + $normalized_query_args['cache_domain'] = 'elasticpress-comment-indexable-' . $cache_key;
944 +
945 + return ( new WP_Comment_Query( $normalized_query_args ) )->found_comments;
946 + }
947 +
948 + /**
917 949 * Prepare a comment document for indexing
918 950 *
919 951 * @param int $comment_id Comment ID
920 952 * @since 3.6.0
@@ -1055,13 +1087,11 @@
1055 1087
1056 1088 if ( true === $allowed_protected_keys || in_array( $key, $allowed_protected_keys, true ) ) {
1057 1089 $allow_index = true;
1058 1090 }
1059 - } else {
1091 + } elseif ( true !== $excluded_public_keys && ! in_array( $key, $excluded_public_keys, true ) ) {
1060 1092
1061 - if ( true !== $excluded_public_keys && ! in_array( $key, $excluded_public_keys, true ) ) {
1062 1093 $allow_index = true;
1063 - }
1064 1094 }
1065 1095
1066 1096 /**
1067 1097 * Filter force allow a meta key
@@ -1119,98 +1149,73 @@
1119 1149 if ( empty( $orderby ) ) {
1120 1150 return $sort;
1121 1151 }
1122 1152
1123 - switch ( $orderby ) {
1124 - case 'comment_agent':
1125 - $orderby_field = 'comment_agent.raw';
1126 - break;
1153 + $from_to = [
1154 + 'comment_agent' => 'comment_agent.raw',
1155 + 'comment_approved' => 'comment_approved.raw',
1156 + 'comment_author' => 'comment_author.raw',
1157 + 'comment_author_email' => 'comment_author_email.raw',
1158 + 'comment_author_IP' => 'comment_author_IP.raw',
1159 + 'comment_author_url' => 'comment_author_url.raw',
1160 + 'comment_content' => 'comment_content.raw',
1161 + 'comment_type' => 'comment_type.raw',
1162 + 'comment_post_type' => 'comment_post_type.raw',
1163 + ];
1127 1164
1128 - case 'comment_approved':
1129 - $orderby_field = 'comment_approved.raw';
1130 - break;
1165 + if ( in_array( $orderby, [ 'meta_value', 'meta_value_num' ], true ) ) {
1166 + if ( empty( $args['meta_key'] ) ) {
1167 + return $sort;
1168 + } else {
1169 + $from_to['meta_value'] = 'meta.' . $args['meta_key'] . '.raw';
1170 + $from_to['meta_value_num'] = 'meta.' . $args['meta_key'] . '.long';
1171 + }
1172 + }
1131 1173
1132 - case 'comment_author':
1133 - $orderby_field = 'comment_author.raw';
1134 - break;
1174 + /**
1175 + * If `orderby` is 'none', WordPress will let the database decide on what should be used to order.
1176 + * It will use the primary key ASC.
1177 + */
1178 + if ( 'none' === $orderby ) {
1179 + $orderby = 'ID';
1180 + $order = 'asc';
1181 + }
1135 1182
1136 - case 'comment_author_email':
1137 - $orderby_field = 'comment_author_email.raw';
1138 - break;
1183 + $orderby = $from_to[ $orderby ] ?? $orderby;
1139 1184
1140 - case 'comment_author_IP':
1141 - $orderby_field = 'comment_author_IP.raw';
1142 - break;
1185 + $sort[] = array(
1186 + $orderby => array(
1187 + 'order' => $order,
1188 + ),
1189 + );
1143 1190
1144 - case 'comment_author_url':
1145 - $orderby_field = 'comment_author_url.raw';
1146 - break;
1191 + return $sort;
1192 + }
1147 1193
1148 - case 'comment_content':
1149 - $orderby_field = 'comment_content.raw';
1150 - break;
1194 + /**
1195 + * Retrieve a specific query variable from the query object.
1196 + *
1197 + * @param \WP_Comment_Query $query The query object.
1198 + * @param string $query_var The name of the query variable to retrieve.
1199 + * @param string $default_value The default value to return if the query variable is not set. Default is an empty string.
1200 + *
1201 + * @return mixed The value of the query variable if set, otherwise the default value.
1202 + */
1203 + public function get_query_var( $query, $query_var, $default_value = '' ) {
1204 + return $query->query_vars[ $query_var ] ?? $default_value;
1205 + }
1151 1206
1152 - case 'comment_date':
1153 - $orderby_field = 'comment_date';
1154 - break;
1207 + /**
1208 + * Sets the ORDER BY clause for comment queries to order comments by their ID.
1209 + *
1210 + * @param array $clauses The SQL clauses array to modify.
1211 + * @return array The modified SQL clauses array with the ORDER BY clause set.
1212 + *
1213 + * @since 5.2.0
1214 + */
1215 + public function set_orderby( $clauses ) {
1216 + global $wpdb;
1155 1217
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;
1218 + $clauses['orderby'] = "{$wpdb->comments}.comment_ID DESC";
1219 + return $clauses;
1214 1220 }
1215 -
1216 1221 }