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/Post/QueryIntegration.php +116 -32 4.2.05.3.5 View file →
@@ -7,11 +7,11 @@
7 7 */
8 8
9 9 namespace ElasticPress\Indexable\Post;
10 10
11 -use ElasticPress\Indexables as Indexables;
12 -use \WP_Query as WP_Query;
13 -use ElasticPress\Utils as Utils;
11 +use WP_Query;
12 +use ElasticPress\Indexables;
13 +use ElasticPress\Utils;
14 14
15 15 if ( ! defined( 'ABSPATH' ) ) {
16 16 // @codeCoverageIgnoreStart
17 17 exit; // Exit if accessed directly.
@@ -38,11 +38,23 @@
38 38 * @since 0.9
39 39 * @since 3.6.0 Added $indexable_slug
40 40 */
41 41 public function __construct( $indexable_slug = 'post' ) {
42 + /**
43 + * Filter whether to enable query integration during indexing
44 + *
45 + * @since 4.5.2
46 + * @hook ep_enable_query_integration_during_indexing
47 + *
48 + * @param {bool} $enable To allow query integration during indexing
49 + * @param {string} $indexable_slug Indexable slug
50 + * @return {bool} New value
51 + */
52 + $allow_query_integration_during_indexing = apply_filters( 'ep_enable_query_integration_during_indexing', false, $indexable_slug );
53 +
42 54 // Ensure that we are currently allowing ElasticPress to override the normal WP_Query
43 55 // Indexable->is_full_reindexing() is not available at this point yet, so using the IndexHelper version of it.
44 - if ( \ElasticPress\IndexHelper::factory()->is_full_reindexing( $indexable_slug, get_current_blog_id() ) ) {
56 + if ( \ElasticPress\IndexHelper::factory()->is_full_reindexing( $indexable_slug, get_current_blog_id() ) && ! $allow_query_integration_during_indexing ) {
45 57 return;
46 58 }
47 59
48 60 // Add header
@@ -51,9 +63,9 @@
51 63 // Query ES for posts
52 64 add_filter( 'posts_pre_query', array( $this, 'get_es_posts' ), 10, 2 );
53 65
54 66 // Properly restore blog if necessary
55 - add_action( 'loop_end', array( $this, 'maybe_restore_blog' ), 10, 1 );
67 + add_action( 'loop_end', array( $this, 'maybe_restore_blog' ), 10 );
56 68
57 69 // Properly switch to blog if necessary
58 70 add_action( 'the_post', array( $this, 'maybe_switch_to_blog' ), 10, 2 );
59 71
@@ -85,9 +97,9 @@
85 97 return $query->num_posts;
86 98 }
87 99
88 100 /**
89 - * Disables cache_results, adds header.
101 + * Adds header.
90 102 *
91 103 * @param WP_Query $query WP_Query instance
92 104 * @since 0.9
93 105 */
@@ -103,18 +115,8 @@
103 115 if ( ! Indexables::factory()->get( 'post' )->elasticpress_enabled( $query ) || apply_filters( 'ep_skip_query_integration', false, $query ) ) {
104 116 return;
105 117 }
106 118
107 - /**
108 - * `cache_results` defaults to false but can be enabled.
109 - *
110 - * @since 1.5
111 - */
112 - $query->set( 'cache_results', false );
113 - if ( ! empty( $query->query['cache_results'] ) ) {
114 - $query->set( 'cache_results', true );
115 - }
116 -
117 119 if ( ! headers_sent() ) {
118 120 /**
119 121 * Manually setting a header as $wp_query isn't yet initialized when we
120 122 * call: add_filter('wp_headers', 'filter_wp_headers');
@@ -176,18 +178,18 @@
176 178
177 179 $this->switched = false;
178 180 }
179 181 }
180 -
181 182 }
182 183
183 184 /**
184 185 * Make sure the correct blog is restored
185 186 *
186 - * @param WP_Query $query WP_Query instance
187 + * @param WP_Query $query WP_Query instance
188 + *
187 189 * @since 0.9
188 190 */
189 - public function maybe_restore_blog( $query ) {
191 + public function maybe_restore_blog( $query ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
190 192 if ( ! is_multisite() ) {
191 193 // @codeCoverageIgnoreStart
192 194 return;
193 195 // @codeCoverageIgnoreEnd
@@ -208,10 +210,8 @@
208 210 * @since 3.0
209 211 * @return string
210 212 */
211 213 public function get_es_posts( $posts, $query ) {
212 - global $wpdb;
213 -
214 214 /**
215 215 * Filter to skip WP Query integration
216 216 *
217 217 * @hook ep_skip_query_integration
@@ -233,9 +233,9 @@
233 233 * @param {string|array} $post_types Post types
234 234 * @param {WP_Query} $query WP Query object
235 235 * @return {string|array} New post types
236 236 */
237 - $query_vars['post_type'] = apply_filters( 'ep_query_post_type', $query_vars['post_type'], $query );
237 + $query_vars['post_type'] = apply_filters( 'ep_query_post_type', $query_vars['post_type'] ?? '', $query );
238 238
239 239 if ( 'any' === $query_vars['post_type'] ) {
240 240 unset( $query_vars['post_type'] );
241 241 }
@@ -240,12 +240,16 @@
240 240 unset( $query_vars['post_type'] );
241 241 }
242 242
243 243 /**
244 - * If not search and not set default to post. If not set and is search, use searchable post types
244 + * If not search and not set, default to post. If not set and is search, use searchable post types.
245 245 */
246 246 if ( empty( $query_vars['post_type'] ) ) {
247 - if ( empty( $query_vars['s'] ) ) {
247 + $tax_post_type = Utils\get_post_types_for_tax_query( $query );
248 +
249 + if ( ! empty( $tax_post_type ) ) {
250 + $query_vars['post_type'] = $tax_post_type;
251 + } elseif ( empty( $query_vars['s'] ) ) {
248 252 $query_vars['post_type'] = 'post';
249 253 } else {
250 254 $query_vars['post_type'] = array_values( get_post_types( array( 'exclude_from_search' => false ) ) );
251 255 }
@@ -272,12 +276,30 @@
272 276
273 277 if ( count( $new_posts ) < 1 ) {
274 278
275 279 $scope = 'current';
280 +
281 + $site__in = '';
282 + $site__not_in = '';
283 +
276 284 if ( ! empty( $query_vars['sites'] ) ) {
277 - $scope = $query_vars['sites'];
285 + _deprecated_argument( __FUNCTION__, '4.4.0', esc_html__( 'sites is deprecated. Use site__in instead.', 'elasticpress' ) );
278 286 }
279 287
288 + if ( ! empty( $query_vars['site__in'] ) || ! empty( $query_vars['sites'] ) ) {
289 + $site__in = ! empty( $query_vars['site__in'] ) ? (array) $query_vars['site__in'] : (array) $query_vars['sites'];
290 +
291 + if ( in_array( 'all', $site__in, true ) ) {
292 + $scope = 'all';
293 + } elseif ( in_array( 'current', $site__in, true ) ) {
294 + $site__in = (array) get_current_blog_id();
295 + }
296 + }
297 +
298 + if ( ! empty( $query_vars['site__not_in'] ) ) {
299 + $site__not_in = (array) $query_vars['site__not_in'];
300 + }
301 +
280 302 $formatted_args = Indexables::factory()->get( 'post' )->format_args( $query_vars, $query );
281 303
282 304 /**
283 305 * Filter post query scope
@@ -294,22 +316,44 @@
294 316 $scope = 'current';
295 317 // @codeCoverageIgnoreEnd
296 318 }
297 319
320 + /**
321 + * Disable the post cache when using a persistent object cache or querying
322 + * across sites, where identical post IDs can cause cache collisions.
323 + */
324 + if ( wp_using_ext_object_cache() || 'all' === $scope || ! empty( $site__in ) || ! empty( $site__not_in ) ) {
325 + $query->set( 'cache_results', false );
326 + }
327 +
298 328 $index = null;
299 329
300 330 if ( 'all' === $scope ) {
301 331 $index = Indexables::factory()->get( 'post' )->get_network_alias();
302 - } elseif ( is_numeric( $scope ) ) {
303 - $index = Indexables::factory()->get( 'post' )->get_index_name( (int) $scope );
304 - } elseif ( is_array( $scope ) ) {
332 + } elseif ( ! empty( $site__in ) ) {
305 333 $index = [];
306 334
307 - foreach ( $scope as $site_id ) {
335 + foreach ( $site__in as $site_id ) {
308 336 $index[] = Indexables::factory()->get( 'post' )->get_index_name( $site_id );
309 337 }
310 338
311 339 $index = implode( ',', $index );
340 + } elseif ( ! empty( $site__not_in ) ) {
341 +
342 + $sites = \get_sites(
343 + array(
344 + 'fields' => 'ids',
345 + 'site__not_in' => $site__not_in,
346 + )
347 + );
348 + foreach ( $sites as $site_id ) {
349 + if ( ! Utils\is_site_indexable( $site_id ) ) {
350 + continue;
351 + }
352 + $index[] = Indexables::factory()->get( 'post' )->get_index_name( $site_id );
353 + }
354 +
355 + $index = implode( ',', $index );
312 356 }
313 357
314 358 $ep_query = Indexables::factory()->get( 'post' )->query_es( $formatted_args, $query->query_vars, $index, $query );
315 359
@@ -323,9 +367,10 @@
323 367
324 368 $found_documents = is_array( $ep_query['found_documents'] ) ? $ep_query['found_documents']['value'] : $ep_query['found_documents']; // 7.0+ have this as an array rather than int
325 369 $query->found_posts = $found_documents;
326 370 $query->num_posts = $query->found_posts;
327 - $query->max_num_pages = ceil( $found_documents / $query->get( 'posts_per_page' ) );
371 + $query->max_num_pages = -1 === $query->get( 'posts_per_page' ) ? 0 : ceil( $found_documents / $query->get( 'posts_per_page' ) );
372 + $query->suggested_terms = $this->maybe_sanitize_suggestion( $ep_query );
328 373 $query->elasticsearch_success = true;
329 374
330 375 // Determine how we should format the results from ES based on the fields parameter.
331 376 $fields = $query->get( 'fields', '' );
@@ -435,11 +480,17 @@
435 480 );
436 481
437 482 foreach ( $post_return_args as $key ) {
438 483 if ( 'post_author' === $key ) {
439 - $post->$key = $post_array[ $key ]['id'];
484 + if ( isset( $post_array[ $key ]['id'] ) ) {
485 + $post->$key = $post_array[ $key ]['id'];
486 + }
440 487 } elseif ( isset( $post_array[ $key ] ) ) {
441 - $post->$key = $post_array[ $key ];
488 + if ( in_array( $key, [ 'terms', 'meta', 'post_meta' ], true ) && is_array( $post_array[ $key ] ) ) {
489 + $post->$key = wp_json_encode( $post_array[ $key ] );
490 + } else {
491 + $post->$key = $post_array[ $key ];
492 + }
442 493 }
443 494 }
444 495
445 496 /**
@@ -513,6 +564,39 @@
513 564 $post->elasticsearch = true; // Super useful for debugging
514 565 $new_posts[] = $post;
515 566 }
516 567 return $new_posts;
568 + }
569 +
570 + /**
571 + * Remove any suggestion that has a score lower than the minimum score.
572 + *
573 + * @since 4.6.0
574 + * @param array $ep_query The query array.
575 + * @return array
576 + */
577 + protected function maybe_sanitize_suggestion( $ep_query ) {
578 + if ( ! isset( $ep_query['suggest']['ep_suggestion'], $ep_query['suggest']['ep_suggestion'][0] ) ) {
579 + return [];
580 + }
581 +
582 + $suggestion = $ep_query['suggest']['ep_suggestion'][0];
583 +
584 + /**
585 + * Filter the score for a suggestion. If the score is lower than this, it will be removed.
586 + *
587 + * @since 4.6.0
588 + * @param float $min_score The minimum score allowed.
589 + * @return float
590 + */
591 + $min_score = (float) apply_filters( 'ep_suggestion_minimum_score', 0.0001 );
592 +
593 + $suggestion['options'] = array_filter(
594 + $suggestion['options'],
595 + function ( $option ) use ( $min_score ) {
596 + return number_format( $option['score'], 10 ) > $min_score;
597 + }
598 + );
599 +
600 + return $suggestion;
517 601 }
518 602 }