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

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