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 +181 -46 4.6.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,18 +32,15 @@
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() {
41 - $this->labels = [
42 - 'plural' => esc_html__( 'Comments', 'elasticpress' ),
43 - 'singular' => esc_html__( 'Comment', 'elasticpress' ),
44 - ];
45 - }
42 + public $support_indexing_advanced_pagination = true;
46 43
47 44 /**
48 45 * Instantiate the indexable SyncManager and QueryIntegration, the main responsibles for the WP integration.
49 46 *
@@ -50,8 +47,13 @@
50 47 * @since 4.5.0
51 48 * @return void
52 49 */
53 50 public function setup() {
51 + $this->labels = [
52 + 'plural' => esc_html__( 'Comments', 'elasticpress' ),
53 + 'singular' => esc_html__( 'Comment', 'elasticpress' ),
54 + ];
55 +
54 56 $this->sync_manager = new SyncManager( $this->slug );
55 57 $this->query_integration = new QueryIntegration();
56 58 }
57 59
@@ -100,9 +102,9 @@
100 102
101 103 /**
102 104 * Support `paged` query var
103 105 *
104 - * If `offset` is used, that takes precendence
106 + * If `offset` is used, that takes precedence
105 107 * over this.
106 108 */
107 109 if ( isset( $query_vars['paged'] ) && empty( $query_vars['offset'] ) && $query_vars['paged'] > 1 ) {
108 110 $formatted_args['from'] = $number * ( $query_vars['paged'] - 1 );
@@ -700,14 +702,14 @@
700 702 */
701 703 $es_version = apply_filters( 'ep_fallback_elasticsearch_version', '2.0' );
702 704 }
703 705
704 - $mapping_file = 'initial.php';
706 + $es_version = (string) $es_version;
705 707
706 - if ( version_compare( $es_version, '5.0', '<' ) ) {
707 - $mapping_file = 'pre-5-0.php';
708 - } elseif ( version_compare( $es_version, '7.0', '>=' ) ) {
709 - $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';
710 712 }
711 713
712 714 /**
713 715 * Filter comment indexable mapping file
@@ -783,18 +785,19 @@
783 785 * @since 3.6.0
784 786 * @return array
785 787 */
786 788 public function query_db( $args ) {
787 -
788 789 $defaults = [
789 - 'type' => $this->get_indexable_comment_types(),
790 - 'status' => $this->get_indexable_comment_status(),
791 - 'post_type' => Indexables::factory()->get( 'post' )->get_indexable_post_types(),
792 - 'post_status' => Indexables::factory()->get( 'post' )->get_indexable_post_status(),
793 - 'number' => $this->get_bulk_items_per_page(),
794 - 'offset' => 0,
795 - 'orderby' => 'comment_ID',
796 - '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,
797 800 ];
798 801
799 802 if ( isset( $args['per_page'] ) ) {
800 803 $args['number'] = $args['per_page'];
@@ -799,8 +802,16 @@
799 802 if ( isset( $args['per_page'] ) ) {
800 803 $args['number'] = $args['per_page'];
801 804 }
802 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 +
803 814 /**
804 815 * Filter database arguments for comment query
805 816 *
806 817 * @hook ep_comment_query_db_args
@@ -815,25 +826,44 @@
815 826 unset( $all_query_args['number'] );
816 827 unset( $all_query_args['offset'] );
817 828 $all_query_args['count'] = true;
818 829
819 - /**
820 - * Filter database arguments for comment count query
821 - *
822 - * @hook ep_comment_all_query_db_args
823 - * @param {array} $args Query arguments based to WP_Comment_Query
824 - * @since 3.6.0
825 - * @return {array} New arguments
826 - */
827 - $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 + }
828 834
829 - if ( ! empty( $args['offset'] ) ) {
830 - if ( (int) $args['offset'] >= $total_objects ) {
831 - $total_objects = 0;
832 - }
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;
833 863 }
834 864
835 - $query = new WP_Comment_Query( $args );
865 + remove_filter( 'comments_clauses', [ $this, 'set_orderby' ], 9999, 2 );
836 866
837 867 if ( is_array( $query->comments ) ) {
838 868 array_walk( $query->comments, [ $this, 'remap_comments' ] );
839 869 }
@@ -844,8 +874,79 @@
844 874 ];
845 875 }
846 876
847 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 + /**
848 949 * Prepare a comment document for indexing
849 950 *
850 951 * @param int $comment_id Comment ID
851 952 * @since 3.6.0
@@ -986,13 +1087,11 @@
986 1087
987 1088 if ( true === $allowed_protected_keys || in_array( $key, $allowed_protected_keys, true ) ) {
988 1089 $allow_index = true;
989 1090 }
990 - } else {
1091 + } elseif ( true !== $excluded_public_keys && ! in_array( $key, $excluded_public_keys, true ) ) {
991 1092
992 - if ( true !== $excluded_public_keys && ! in_array( $key, $excluded_public_keys, true ) ) {
993 1093 $allow_index = true;
994 - }
995 1094 }
996 1095
997 1096 /**
998 1097 * Filter force allow a meta key
@@ -1071,8 +1170,17 @@
1071 1170 $from_to['meta_value_num'] = 'meta.' . $args['meta_key'] . '.long';
1072 1171 }
1073 1172 }
1074 1173
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 + }
1182 +
1075 1183 $orderby = $from_to[ $orderby ] ?? $orderby;
1076 1184
1077 1185 $sort[] = array(
1078 1186 $orderby => array(
@@ -1082,5 +1190,32 @@
1082 1190
1083 1191 return $sort;
1084 1192 }
1085 1193
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 + }
1206 +
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;
1217 +
1218 + $clauses['orderby'] = "{$wpdb->comments}.comment_ID DESC";
1219 + return $clauses;
1220 + }
1086 1221 }