PluginProbe
ElasticPress / 4.5.1
ElasticPress v4.5.1
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.5.1, at includes/classes/Indexable/Comment/QueryIntegration.php

363 lines 9.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 $new_comments = [];
117
118 $formatted_args = $this->indexable->format_args( $query->query_vars );
119
120 $scope = 'current';
121
122 $site__in = [];
123 $site__not_in = [];
124
125 if ( ! empty( $query->query_vars['sites'] ) ) {
126 _deprecated_argument( __FUNCTION__, '4.4.0', esc_html__( 'sites is deprecated. Use site__in instead.', 'elasticpress' ) );
127 }
128
129 if ( ! empty( $query->query_vars['site__in'] ) || ! empty( $query->query_vars['sites'] ) ) {
130 $site__in = ! empty( $query->query_vars['site__in'] ) ? (array) $query->query_vars['site__in'] : (array) $query->query_vars['sites'];
131
132 if ( in_array( 'all', $site__in, true ) ) {
133 $scope = 'all';
134 } elseif ( in_array( 'current', $site__in, true ) ) {
135 $site__in = (array) get_current_blog_id();
136 }
137 }
138
139 if ( ! empty( $query->query_vars['site__not_in'] ) ) {
140 $site__not_in = (array) $query->query_vars['site__not_in'];
141 }
142
143 /**
144 * Filter search scope
145 *
146 * @since 3.6.0
147 *
148 * @param mixed $scope The search scope. Accepts `all` (string), a single
149 * site id (int or string), or an array of site ids (array).
150 */
151 $scope = apply_filters( 'ep_comment_search_scope', $scope );
152
153 if ( ! defined( 'EP_IS_NETWORK' ) || ! EP_IS_NETWORK ) {
154 $scope = 'current';
155 }
156
157 $this->index = null;
158
159 if ( 'all' === $scope ) {
160 $this->index = $this->indexable->get_network_alias();
161 } elseif ( ! empty( $site__in ) ) {
162 $this->index = [];
163
164 foreach ( $site__in as $site_id ) {
165 $this->index[] = $this->indexable->get_index_name( $site_id );
166 }
167
168 $this->index = implode( ',', $this->index );
169 } elseif ( ! empty( $site__not_in ) ) {
170
171 $sites = get_sites(
172 array(
173 'fields' => 'ids',
174 'site__not_in' => $site__not_in,
175 )
176 );
177 foreach ( $sites as $site_id ) {
178 if ( ! Utils\is_site_indexable( $site_id ) ) {
179 continue;
180 }
181 $index[] = Indexables::factory()->get( 'comment' )->get_index_name( $site_id );
182 }
183
184 $this->index = implode( ',', $index );
185
186 }
187
188 $ep_query = $this->indexable->query_es( $formatted_args, $query->query_vars, $this->index, $query );
189
190 if ( false === $ep_query ) {
191 $query->elasticsearch_success = false;
192 return $results;
193 }
194
195 if ( ! empty( $query->query_vars['count'] ) ) {
196 return count( $ep_query['documents'] );
197 }
198
199 $query->found_comments = $ep_query['found_documents']['value'];
200 $query->max_num_pages = $query->query_vars['number'] <= 0 ? 1 : max( 1, ceil( $query->found_comments / absint( $query->query_vars['number'] ) ) );
201 $query->elasticsearch_success = true;
202
203 // Determine how we should format the results from ES based on the fields parameter.
204 $fields = $query->query_vars['fields'];
205
206 switch ( $fields ) {
207 case 'count':
208 $new_comments = count( $ep_query['documents'] );
209 break;
210
211 case 'ids':
212 $new_comments = $this->format_hits_as_ids( $ep_query['documents'], $new_comments );
213 break;
214
215 default:
216 $new_comments = $this->format_hits_as_comments( $ep_query['documents'], $new_comments, $query->query_vars );
217 break;
218 }
219
220 return $new_comments;
221 }
222
223 /**
224 * Format the ES hits/results as comments objects.
225 *
226 * @param array $comments The comments that should be formatted.
227 * @param array $new_comments Array of comments from cache.
228 * @param array $query_vars Query variables.
229 * @since 3.6.0
230 * @return array
231 */
232 protected function format_hits_as_comments( $comments, $new_comments, $query_vars ) {
233 $hierarchical = isset( $query_vars['hierarchical'] ) ? $query_vars['hierarchical'] : false;
234
235 foreach ( $comments as $comment_array ) {
236 $comment = new \WP_Comment( (object) $comment_array );
237 if ( ! empty( $comment_array['site_id'] ) ) {
238 $comment->site_id = $comment_array['site_id'];
239 } else {
240 $comment->site_id = get_current_blog_id();
241 }
242
243 $comment->elasticsearch = true; // Super useful for debugging
244
245 if ( $comment ) {
246 $new_comments[] = $comment;
247 }
248 }
249
250 if ( $hierarchical ) {
251 $new_comments = $this->fill_descendants( $new_comments, $query_vars );
252 }
253
254 return $new_comments;
255 }
256
257 /**
258 * Format the ES hits/results as an array of ids.
259 *
260 * @param array $comments The comments that should be formatted.
261 * @param array $new_comments Array of comments from cache.
262 * @since 3.6.0
263 * @return array
264 */
265 protected function format_hits_as_ids( $comments, $new_comments ) {
266 foreach ( $comments as $comment_array ) {
267 $new_comments[] = $comment_array['comment_ID'];
268 }
269
270 return $new_comments;
271 }
272
273 /**
274 * Fetch descendants for located comments.
275 *
276 * @param array $comments Array of top-level comments whose descendants should be filled in.
277 * @param array $query_vars Current query vars.
278 * @since 3.6.0
279 * @return array
280 */
281 protected function fill_descendants( $comments, $query_vars ) {
282 $levels = [
283 0 => $comments,
284 ];
285
286 // Fetch an entire level of the descendant tree at a time.
287 $level = 0;
288 $exclude_keys = [ 'parent', 'parent__in', 'parent__not_in' ];
289
290 do {
291 $child_comments = [];
292 $_parent_ids = wp_list_pluck( $levels[ $level ], 'comment_ID' );
293
294 if ( $_parent_ids ) {
295 $parent_query_args = $query_vars;
296
297 foreach ( $exclude_keys as $exclude_key ) {
298 $parent_query_args[ $exclude_key ] = '';
299 }
300
301 $parent_query_args['parent__in'] = $_parent_ids;
302 $parent_query_args['hierarchical'] = false;
303 $parent_query_args['offset'] = 0;
304 $parent_query_args['number'] = 0;
305
306 $formatted_args = $this->indexable->format_args( $parent_query_args );
307 $ep_query = $this->indexable->query_es( $formatted_args, $query_vars, $this->index );
308
309 if ( false === $ep_query ) {
310 $level_comments = [];
311 } else {
312 $level_comments = $this->format_hits_as_comments( $ep_query['documents'], [], [] );
313 }
314
315 foreach ( $level_comments as $level_comment ) {
316 $child_comments[] = $level_comment;
317 }
318 }
319
320 $level ++;
321 $levels[ $level ] = $child_comments;
322 } while ( $child_comments );
323
324 // Pull out just the descendant comments
325 $descendants = [];
326 for ( $i = 1, $c = count( $levels ); $i < $c; $i++ ) {
327 $descendants = array_merge( $descendants, $levels[ $i ] );
328 }
329
330 // Assemble a flat array of all comments + descendants.
331 $all_comments = $comments;
332 foreach ( $descendants as $descendant ) {
333 $all_comments[] = $descendant;
334 }
335
336 // If a threaded representation was requested, build the tree.
337 if ( 'threaded' === $query_vars['hierarchical'] ) {
338 $threaded_comments = [];
339 $ref = [];
340
341 foreach ( $all_comments as $c ) {
342 // If the comment isn't in the reference array, it goes in the top level of the thread.
343 if ( ! isset( $ref[ $c->comment_parent ] ) ) {
344 $threaded_comments[ $c->comment_ID ] = $c;
345 $ref[ $c->comment_ID ] = $threaded_comments[ $c->comment_ID ];
346
347 // Otherwise, set it as a child of its parent.
348 } else {
349 $ref[ $c->comment_parent ]->add_child( $c );
350 $ref[ $c->comment_ID ] = $c;
351 }
352 }
353
354 $comments = $threaded_comments;
355 } else {
356 $comments = $all_comments;
357 }
358
359 return $comments;
360 }
361
362 }
363