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 +184 -41 4.4.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 );
@@ -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
@@ -1063,8 +1170,17 @@
1063 1170 $from_to['meta_value_num'] = 'meta.' . $args['meta_key'] . '.long';
1064 1171 }
1065 1172 }
1066 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 +
1067 1183 $orderby = $from_to[ $orderby ] ?? $orderby;
1068 1184
1069 1185 $sort[] = array(
1070 1186 $orderby => array(
@@ -1074,5 +1190,32 @@
1074 1190
1075 1191 return $sort;
1076 1192 }
1077 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 + }
1078 1221 }