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 / QueryIntegration.php

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

328 lines 8.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Integrate with WP_Comment_Query
4 *
5 * @since 3.6.0
6 * @package elasticpress
7 */
8
9 namespace ElasticPress\Indexable\Comment;
10
11 use ElasticPress\Indexables as Indexables;
12 use \WP_Comment_Query as WP_Comment_Query;
13 use ElasticPress\Utils as Utils;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit; // Exit if accessed directly.
17 }
18
19 /**
20 * Query integration class
21 */
22 class QueryIntegration {
23
24 /**
25 * Comment indexable
26 *
27 * @var Comment
28 */
29 public $indexable = '';
30
31 /**
32 * Index name
33 *
34 * @var string
35 */
36 public $index = '';
37
38 /**
39 * Sets up the appropriate actions and filters.
40 *
41 * @since 3.6.0
42 */
43 public function __construct() {
44 // Check if we are currently indexing
45 if ( Utils\is_indexing() ) {
46 return;
47 }
48
49 // Add header
50 add_action( 'pre_get_comments', array( $this, 'action_pre_get_comments' ), 5 );
51
52 // Filter comment query
53 add_filter( 'comments_pre_query', [ $this, 'maybe_filter_query' ], 10, 2 );
54 }
55
56 /**
57 * Add EP header
58 *
59 * @param WP_Comment_Query $query Query object
60 * @since 3.6.0
61 * @return void
62 */
63 public function action_pre_get_comments( WP_Comment_Query $query ) {
64 /**
65 * Filter to skip WP_Comment_Query integration
66 *
67 * @hook ep_skip_comment_query_integration
68 * @since 3.6.0
69 * @param {bool} $skip True to skip
70 * @param {WP_Comment_Query} $query WP_Comment_Query to evaluate
71 * @return {bool} New skip value
72 */
73 if ( ! Indexables::factory()->get( 'comment' )->elasticpress_enabled( $query ) || apply_filters( 'ep_skip_comment_query_integration', false, $query ) ) {
74 return;
75 }
76
77 if ( ! headers_sent() ) {
78 /**
79 * Manually setting a header as $wp_query isn't yet initialized
80 * when we call: add_filter('wp_headers', 'filter_wp_headers');
81 */
82 header( 'X-ElasticPress-Search: true' );
83 }
84 }
85
86 /**
87 * If WP_Comment_Query meets certain conditions, query results from ES
88 *
89 * @param array $results Query results.
90 * @param WP_Comment_Query $query Current query.
91 * @since 3.6.0
92 * @return array
93 */
94 public function maybe_filter_query( $results, WP_Comment_Query $query ) {
95 $this->indexable = Indexables::factory()->get( 'comment' );
96
97 if ( ! $this->indexable->elasticpress_enabled( $query ) || apply_filters( 'ep_skip_comment_query_integration', false, $query ) ) {
98 return $results;
99 }
100
101 /**
102 * Filter cached comments pre-post query
103 *
104 * @hook ep_wp_query_cached_comments
105 * @since 3.6.0
106 * @param {mixed} $comments Comments or null
107 * @param {WP_Comment_Query} $query WP_Comment_Query object
108 * @return {array} New cached comments
109 */
110 $new_comments = apply_filters( 'ep_wp_query_cached_comments', null, $query );
111
112 if ( null !== $new_comments ) {
113 return $new_comments;
114 }
115
116 $formatted_args = $this->indexable->format_args( $query->query_vars );
117
118 $scope = 'current';
119 if ( ! empty( $query->query_vars['sites'] ) ) {
120 $scope = $query->query_vars['sites'];
121 }
122
123 /**
124 * Filter search scope
125 *
126 * @since 3.6.0
127 *
128 * @param mixed $scope The search scope. Accepts `all` (string), a single
129 * site id (int or string), or an array of site ids (array).
130 */
131 $scope = apply_filters( 'ep_comment_search_scope', $scope );
132
133 if ( ! defined( 'EP_IS_NETWORK' ) || ! EP_IS_NETWORK ) {
134 $scope = 'current';
135 }
136
137 $this->index = null;
138
139 if ( 'all' === $scope ) {
140 $this->index = $this->indexable->get_network_alias();
141 } elseif ( is_numeric( $scope ) ) {
142 $this->index = $this->indexable->get_index_name( (int) $scope );
143 } elseif ( is_array( $scope ) ) {
144 $this->index = [];
145
146 foreach ( $scope as $site_id ) {
147 $this->index[] = $this->indexable->get_index_name( $site_id );
148 }
149
150 $this->index = implode( ',', $this->index );
151 }
152
153 $ep_query = $this->indexable->query_es( $formatted_args, $query->query_vars, $this->index, $query );
154
155 if ( false === $ep_query ) {
156 $query->elasticsearch_success = false;
157 return $results;
158 }
159
160 if ( ! empty( $query->query_vars['count'] ) ) {
161 return count( $ep_query['documents'] );
162 }
163
164 $query->found_comments = $ep_query['found_documents']['value'];
165 $query->max_num_pages = $query->query_vars['number'] <= 0 ? 1 : max( 1, ceil( $query->found_comments / absint( $query->query_vars['number'] ) ) );
166 $query->elasticsearch_success = true;
167
168 // Determine how we should format the results from ES based on the fields parameter.
169 $fields = $query->query_vars['fields'];
170
171 switch ( $fields ) {
172 case 'count':
173 $new_comments = count( $ep_query['documents'] );
174 break;
175
176 case 'ids':
177 $new_comments = $this->format_hits_as_ids( $ep_query['documents'], $new_comments );
178 break;
179
180 default:
181 $new_comments = $this->format_hits_as_comments( $ep_query['documents'], $new_comments, $query->query_vars );
182 break;
183 }
184
185 return $new_comments;
186 }
187
188 /**
189 * Format the ES hits/results as comments objects.
190 *
191 * @param array $comments The comments that should be formatted.
192 * @param array $new_comments Array of comments from cache.
193 * @param array $query_vars Query variables.
194 * @since 3.6.0
195 * @return array
196 */
197 protected function format_hits_as_comments( $comments, $new_comments, $query_vars ) {
198 $hierarchical = isset( $query_vars['hierarchical'] ) ? $query_vars['hierarchical'] : false;
199
200 foreach ( $comments as $comment_array ) {
201 $comment = new \WP_Comment( (object) $comment_array );
202 if ( ! empty( $comment_array['site_id'] ) ) {
203 $comment->site_id = $comment_array['site_id'];
204 } else {
205 $comment->site_id = get_current_blog_id();
206 }
207
208 $comment->elasticsearch = true; // Super useful for debugging
209
210 if ( $comment ) {
211 $new_comments[] = $comment;
212 }
213 }
214
215 if ( $hierarchical ) {
216 $new_comments = $this->fill_descendants( $new_comments, $query_vars );
217 }
218
219 return $new_comments;
220 }
221
222 /**
223 * Format the ES hits/results as an array of ids.
224 *
225 * @param array $comments The comments that should be formatted.
226 * @param array $new_comments Array of comments from cache.
227 * @since 3.6.0
228 * @return array
229 */
230 protected function format_hits_as_ids( $comments, $new_comments ) {
231 foreach ( $comments as $comment_array ) {
232 $new_comments[] = $comment_array['comment_ID'];
233 }
234
235 return $new_comments;
236 }
237
238 /**
239 * Fetch descendants for located comments.
240 *
241 * @param array $comments Array of top-level comments whose descendants should be filled in.
242 * @param array $query_vars Current query vars.
243 * @since 3.6.0
244 * @return array
245 */
246 protected function fill_descendants( $comments, $query_vars ) {
247 $levels = [
248 0 => $comments,
249 ];
250
251 // Fetch an entire level of the descendant tree at a time.
252 $level = 0;
253 $exclude_keys = [ 'parent', 'parent__in', 'parent__not_in' ];
254
255 do {
256 $child_comments = [];
257 $_parent_ids = wp_list_pluck( $levels[ $level ], 'comment_ID' );
258
259 if ( $_parent_ids ) {
260 $parent_query_args = $query_vars;
261
262 foreach ( $exclude_keys as $exclude_key ) {
263 $parent_query_args[ $exclude_key ] = '';
264 }
265
266 $parent_query_args['parent__in'] = $_parent_ids;
267 $parent_query_args['hierarchical'] = false;
268 $parent_query_args['offset'] = 0;
269 $parent_query_args['number'] = 0;
270
271 $formatted_args = $this->indexable->format_args( $parent_query_args );
272 $ep_query = $this->indexable->query_es( $formatted_args, $query_vars, $this->index );
273
274 if ( false === $ep_query ) {
275 $level_comments = [];
276 } else {
277 $level_comments = $this->format_hits_as_comments( $ep_query['documents'], [], [] );
278 }
279
280 foreach ( $level_comments as $level_comment ) {
281 $child_comments[] = $level_comment;
282 }
283 }
284
285 $level ++;
286 $levels[ $level ] = $child_comments;
287 } while ( $child_comments );
288
289 // Pull out just the descendant comments
290 $descendants = [];
291 for ( $i = 1, $c = count( $levels ); $i < $c; $i++ ) {
292 $descendants = array_merge( $descendants, $levels[ $i ] );
293 }
294
295 // Assemble a flat array of all comments + descendants.
296 $all_comments = $comments;
297 foreach ( $descendants as $descendant ) {
298 $all_comments[] = $descendant;
299 }
300
301 // If a threaded representation was requested, build the tree.
302 if ( 'threaded' === $query_vars['hierarchical'] ) {
303 $threaded_comments = [];
304 $ref = [];
305
306 foreach ( $all_comments as $c ) {
307 // If the comment isn't in the reference array, it goes in the top level of the thread.
308 if ( ! isset( $ref[ $c->comment_parent ] ) ) {
309 $threaded_comments[ $c->comment_ID ] = $c;
310 $ref[ $c->comment_ID ] = $threaded_comments[ $c->comment_ID ];
311
312 // Otherwise, set it as a child of its parent.
313 } else {
314 $ref[ $c->comment_parent ]->add_child( $c );
315 $ref[ $c->comment_ID ] = $c;
316 }
317 }
318
319 $comments = $threaded_comments;
320 } else {
321 $comments = $all_comments;
322 }
323
324 return $comments;
325 }
326
327 }
328