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 +209 -127 4.3.15.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;
@@ -692,14 +702,14 @@
692 702 */
693 703 $es_version = apply_filters( 'ep_fallback_elasticsearch_version', '2.0' );
694 704 }
695 705
696 - $mapping_file = 'initial.php';
706 + $es_version = (string) $es_version;
697 707
698 - if ( version_compare( $es_version, '5.0', '<' ) ) {
699 - $mapping_file = 'pre-5-0.php';
700 - } elseif ( version_compare( $es_version, '7.0', '>=' ) ) {
701 - $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';
702 712 }
703 713
704 714 /**
705 715 * Filter comment indexable mapping file
@@ -775,18 +785,19 @@
775 785 * @since 3.6.0
776 786 * @return array
777 787 */
778 788 public function query_db( $args ) {
779 -
780 789 $defaults = [
781 - 'type' => $this->get_indexable_comment_types(),
782 - 'status' => $this->get_indexable_comment_status(),
783 - 'post_type' => Indexables::factory()->get( 'post' )->get_indexable_post_types(),
784 - 'post_status' => Indexables::factory()->get( 'post' )->get_indexable_post_status(),
785 - 'number' => $this->get_bulk_items_per_page(),
786 - 'offset' => 0,
787 - 'orderby' => 'comment_ID',
788 - '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,
789 800 ];
790 801
791 802 if ( isset( $args['per_page'] ) ) {
792 803 $args['number'] = $args['per_page'];
@@ -791,8 +802,16 @@
791 802 if ( isset( $args['per_page'] ) ) {
792 803 $args['number'] = $args['per_page'];
793 804 }
794 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 +
795 814 /**
796 815 * Filter database arguments for comment query
797 816 *
798 817 * @hook ep_comment_query_db_args
@@ -807,25 +826,44 @@
807 826 unset( $all_query_args['number'] );
808 827 unset( $all_query_args['offset'] );
809 828 $all_query_args['count'] = true;
810 829
811 - /**
812 - * Filter database arguments for comment count query
813 - *
814 - * @hook ep_comment_all_query_db_args
815 - * @param {array} $args Query arguments based to WP_Comment_Query
816 - * @since 3.6.0
817 - * @return {array} New arguments
818 - */
819 - $total_objects = get_comments( 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 + }
820 834
821 - if ( ! empty( $args['offset'] ) ) {
822 - if ( (int) $args['offset'] >= $total_objects ) {
823 - $total_objects = 0;
824 - }
835 + // Explicitly set the orderby to ID to prevent accidental modifications by other code.
836 + add_filter( 'comments_clauses', [ $this, 'set_orderby' ], 9999, 2 );
837 +
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;
825 863 }
826 864
827 - $query = new WP_Comment_Query( $args );
865 + remove_filter( 'comments_clauses', [ $this, 'set_orderby' ], 9999, 2 );
828 866
829 867 if ( is_array( $query->comments ) ) {
830 868 array_walk( $query->comments, [ $this, 'remap_comments' ] );
831 869 }
@@ -836,8 +874,79 @@
836 874 ];
837 875 }
838 876
839 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 + /**
840 949 * Prepare a comment document for indexing
841 950 *
842 951 * @param int $comment_id Comment ID
843 952 * @since 3.6.0
@@ -978,13 +1087,11 @@
978 1087
979 1088 if ( true === $allowed_protected_keys || in_array( $key, $allowed_protected_keys, true ) ) {
980 1089 $allow_index = true;
981 1090 }
982 - } else {
1091 + } elseif ( true !== $excluded_public_keys && ! in_array( $key, $excluded_public_keys, true ) ) {
983 1092
984 - if ( true !== $excluded_public_keys && ! in_array( $key, $excluded_public_keys, true ) ) {
985 1093 $allow_index = true;
986 - }
987 1094 }
988 1095
989 1096 /**
990 1097 * Filter force allow a meta key
@@ -1042,98 +1149,73 @@
1042 1149 if ( empty( $orderby ) ) {
1043 1150 return $sort;
1044 1151 }
1045 1152
1046 - switch ( $orderby ) {
1047 - case 'comment_agent':
1048 - $orderby_field = 'comment_agent.raw';
1049 - 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 + ];
1050 1164
1051 - case 'comment_approved':
1052 - $orderby_field = 'comment_approved.raw';
1053 - 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 + }
1054 1173
1055 - case 'comment_author':
1056 - $orderby_field = 'comment_author.raw';
1057 - 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 + }
1058 1182
1059 - case 'comment_author_email':
1060 - $orderby_field = 'comment_author_email.raw';
1061 - break;
1183 + $orderby = $from_to[ $orderby ] ?? $orderby;
1062 1184
1063 - case 'comment_author_IP':
1064 - $orderby_field = 'comment_author_IP.raw';
1065 - break;
1185 + $sort[] = array(
1186 + $orderby => array(
1187 + 'order' => $order,
1188 + ),
1189 + );
1066 1190
1067 - case 'comment_author_url':
1068 - $orderby_field = 'comment_author_url.raw';
1069 - break;
1191 + return $sort;
1192 + }
1070 1193
1071 - case 'comment_content':
1072 - $orderby_field = 'comment_content.raw';
1073 - 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 + }
1074 1206
1075 - case 'comment_date':
1076 - $orderby_field = 'comment_date';
1077 - 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;
1078 1217
1079 - case 'comment_date_gmt':
1080 - $orderby_field = 'comment_date_gmt';
1081 - break;
1082 -
1083 - case 'comment_ID':
1084 - $orderby_field = 'comment_ID';
1085 - break;
1086 -
1087 - case 'comment_karma':
1088 - $orderby_field = 'comment_karma';
1089 - break;
1090 -
1091 - case 'comment_parent':
1092 - $orderby_field = 'comment_parent';
1093 - break;
1094 -
1095 - case 'comment_post_ID':
1096 - $orderby_field = 'comment_post_ID';
1097 - break;
1098 -
1099 - case 'comment_type':
1100 - $orderby_field = 'comment_type.raw';
1101 - break;
1102 -
1103 - case 'comment_post_type':
1104 - $orderby_field = 'comment_post_type.raw';
1105 - break;
1106 -
1107 - case 'user_id':
1108 - $orderby_field = 'user_id';
1109 - break;
1110 -
1111 - case 'meta_value':
1112 - if ( ! empty( $args['meta_key'] ) ) {
1113 - $orderby_field = 'meta.' . $args['meta_key'] . '.value';
1114 - }
1115 - break;
1116 -
1117 - case 'meta_value_num':
1118 - if ( ! empty( $args['meta_key'] ) ) {
1119 - $orderby_field = 'meta.' . $args['meta_key'] . '.long';
1120 - }
1121 - break;
1122 -
1123 - default:
1124 - $orderby_field = $orderby;
1125 - break;
1126 - }
1127 -
1128 - if ( ! empty( $orderby_field ) ) {
1129 - $sort[] = [
1130 - $orderby_field => [
1131 - 'order' => $order,
1132 - ],
1133 - ];
1134 - }
1135 -
1136 - return $sort;
1218 + $clauses['orderby'] = "{$wpdb->comments}.comment_ID DESC";
1219 + return $clauses;
1137 1220 }
1138 -
1139 1221 }