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.php +140 -41 4.6.05.3.5 View file →
@@ -9,11 +9,10 @@
9 9 */
10 10
11 11 namespace ElasticPress;
12 12
13 -use ElasticPress\Elasticsearch as Elasticsearch;
14 -use ElasticPress\SyncManager as SyncManager;
15 -use ElasticPress\QueryIntegration as QueryIntegration;
13 +use ElasticPress\Elasticsearch;
14 +use ElasticPress\SyncManager;
16 15
17 16 if ( ! defined( 'ABSPATH' ) ) {
18 17 exit; // Exit if accessed directly.
19 18 }
@@ -48,9 +47,9 @@
48 47 /**
49 48 * Instance of QueryIntegration. This should handle integrating with a default
50 49 * WP query.
51 50 *
52 - * @var QueryIntegration
51 + * @var object
53 52 * @since 3.0
54 53 */
55 54 public $query_integration;
56 55
@@ -76,9 +75,12 @@
76 75 *
77 76 * @since 4.5.0
78 77 * @var array
79 78 */
80 - public $labels = [];
79 + public $labels = [
80 + 'plural' => '',
81 + 'singular' => '',
82 + ];
81 83
82 84 /**
83 85 * Get number of bulk items to index per page
84 86 *
@@ -260,12 +262,16 @@
260 262 *
261 263 * @param int $object_id Object to index.
262 264 * @param boolean $blocking Blocking HTTP request or not.
263 265 * @since 3.0
264 - * @return boolean
266 + * @return object|boolean
265 267 */
266 268 public function index( $object_id, $blocking = false ) {
267 - $document = $this->prepare_document( $object_id );
269 + try {
270 + $document = $this->prepare_document( $object_id );
271 + } catch ( \Throwable $th ) {
272 + return false;
273 + }
268 274
269 275 if ( false === $document ) {
270 276 return false;
271 277 }
@@ -272,8 +278,9 @@
272 278
273 279 /**
274 280 * Conditionally kill indexing on a specific object
275 281 *
282 + * @deprecated 5.3.3 Use ep_{indexable_slug}_sync_kill instead
276 283 * @hook ep_{indexable_slug}_index_kill
277 284 * @param {bool} $kill True to not index
278 285 * @param {int} $object_id Id of object to index
279 286 * @since 3.0
@@ -278,13 +285,26 @@
278 285 * @param {int} $object_id Id of object to index
279 286 * @since 3.0
280 287 * @return {bool} New kill value
281 288 */
282 - if ( apply_filters( 'ep_' . $this->slug . '_index_kill', false, $object_id ) ) {
289 + if ( apply_filters_deprecated( 'ep_' . $this->slug . '_index_kill', [ false, $object_id ], 'ElasticPress 5.3.3', 'ep_' . $this->slug . '_sync_kill' ) ) {
283 290 return false;
284 291 }
285 292
286 293 /**
294 + * Conditionally kill indexing for an object.
295 + *
296 + * @hook ep_{$this->slug}_sync_kill
297 + * @param {bool} $kill True means dont sync
298 + * @param {int} $object_id Object ID
299 + * @return {bool} New value
300 + */
301 + $ep_indexable_sync_kill = apply_filters( 'ep_' . $this->slug . '_sync_kill', false, $object_id );
302 + if ( $ep_indexable_sync_kill ) {
303 + return false;
304 + }
305 +
306 + /**
287 307 * Filter document before index
288 308 *
289 309 * @hook ep_pre_index_{indexable_slug}
290 310 * @param {array} $document Document to index
@@ -299,9 +319,9 @@
299 319 * Fires after document is indexed
300 320 *
301 321 * @hook ep_after_index_{indexable_slug}
302 322 * @param {array} $document Document to index
303 - * @param {array|boolean} $return ES response on success, false on failure
323 + * @param {object|boolean} $return ES response on success, false on failure
304 324 * @since 3.0
305 325 */
306 326 do_action( 'ep_after_index_' . $this->slug, $document, $return );
307 327
@@ -328,8 +348,10 @@
328 348 */
329 349 public function bulk_index( $object_ids ) {
330 350 $body = '';
331 351
352 + $non_es_errors = [];
353 +
332 354 foreach ( $object_ids as $object_id ) {
333 355 $action_args = array(
334 356 'index' => array(
335 357 '_id' => absint( $object_id ),
@@ -335,9 +357,22 @@
335 357 '_id' => absint( $object_id ),
336 358 ),
337 359 );
338 360
339 - $document = $this->prepare_document( $object_id );
361 + try {
362 + $document = $this->prepare_document( $object_id );
363 + } catch ( \Throwable $th ) {
364 + $non_es_errors[] = [
365 + 'index' => [
366 + '_id' => absint( $object_id ),
367 + 'error' => [
368 + 'type' => 'prepare_document_error',
369 + 'reason' => $th->getMessage(),
370 + ],
371 + ],
372 + ];
373 + continue;
374 + }
340 375
341 376 /**
342 377 * Conditionally kill indexing on a specific object
343 378 *
@@ -354,8 +389,13 @@
354 389 }
355 390
356 391 $result = Elasticsearch::factory()->bulk_index( $this->get_index_name(), $this->slug, $body );
357 392
393 + if ( ! empty( $non_es_errors ) ) {
394 + $result['errors'] = true;
395 + $result['items'] = isset( $result['items'] ) ? array_merge( $result['items'], $non_es_errors ) : $non_es_errors;
396 + }
397 +
358 398 /**
359 399 * Perform actions after a bulk indexing is completed
360 400 *
361 401 * @hook ep_after_bulk_index
@@ -377,8 +417,10 @@
377 417 */
378 418 public function bulk_index_dynamically( $object_ids ) {
379 419 $documents = [];
380 420
421 + $non_es_errors = [];
422 +
381 423 foreach ( $object_ids as $object_id ) {
382 424 $action_args = array(
383 425 'index' => array(
384 426 '_id' => absint( $object_id ),
@@ -384,10 +426,27 @@
384 426 '_id' => absint( $object_id ),
385 427 ),
386 428 );
387 429
388 - $document = $this->prepare_document( $object_id );
430 + try {
431 + $document = $this->prepare_document( $object_id );
432 + } catch ( \Throwable $th ) {
433 + $non_es_errors[] = [
434 + 'index' => [
435 + '_id' => absint( $object_id ),
436 + 'error' => [
437 + 'type' => 'prepare_document_error',
438 + 'reason' => $th->getMessage(),
439 + ],
440 + ],
441 + ];
442 + continue;
443 + }
389 444
445 + if ( empty( $document ) ) {
446 + continue;
447 + }
448 +
390 449 /**
391 450 * Conditionally kill indexing on a specific object
392 451 *
393 452 * @hook ep_bulk_index_action_args
@@ -402,10 +461,35 @@
402 461
403 462 $documents[] = $document_str;
404 463 }
405 464
465 + if ( empty( $documents ) ) {
466 + return ( ! empty( $non_es_errors ) ? [
467 + [
468 + 'errors' => true,
469 + 'items' => $non_es_errors,
470 + ],
471 + ] : [
472 + new \WP_Error(
473 + 'ep_bulk_index_no_documents',
474 + esc_html__( 'It was not possible to create a body request with the document IDs provided.', 'elasticpress' ),
475 + $object_ids
476 + ),
477 + ] );
478 + }
479 +
406 480 $results = $this->send_bulk_index_request( $documents );
407 481
482 + if ( ! empty( $non_es_errors ) ) {
483 + $results = [
484 + [
485 + 'errors' => true,
486 + 'items' => $non_es_errors,
487 + ],
488 + ...$results,
489 + ];
490 + }
491 +
408 492 /**
409 493 * Perform actions after a dynamic bulk indexing is completed
410 494 *
411 495 * @hook ep_after_bulk_index_dynamically
@@ -478,12 +562,12 @@
478 562 }
479 563
480 564 $results = [];
481 565
482 - $body = [];
566 + $body = [];
567 + $current_body_size = 0;
568 + $requests = 0;
483 569
484 - $requests = 0;
485 -
486 570 /*
487 571 * This script will use two main arrays: $body and $documents, being $body the
488 572 * documents to be sent in the next request and $documents the list of docs to be indexed.
489 573 * The do-while loop will stop if all documents are sent or if a request fails even sending
@@ -489,16 +573,18 @@
489 573 * The do-while loop will stop if all documents are sent or if a request fails even sending
490 574 * a buffer as small as possible.
491 575 */
492 576 do {
493 - $next_document = array_shift( $documents );
577 + $next_document = array_shift( $documents );
578 + $next_document_size = mb_strlen( $next_document );
579 + $has_buffered_documents = count( $body ) > 0;
494 580
495 581 // If the next document alone takes the entire current buffer size,
496 582 // let's add it back to the pipe and send what we have first
497 - if ( mb_strlen( $next_document ) > $current_buffer_size && count( $body ) > 0 ) {
583 + if ( $next_document_size > $current_buffer_size && $has_buffered_documents ) {
498 584 array_unshift( $documents, $next_document );
499 585 } else {
500 - if ( mb_strlen( $next_document ) > $max_buffer_size ) {
586 + if ( $next_document_size > $max_buffer_size ) {
501 587 /**
502 588 * Perform actions when a post is bigger than the max buffer size.
503 589 *
504 590 * @hook ep_dynamic_bulk_post_too_big
@@ -508,15 +594,22 @@
508 594 do_action( 'ep_dynamic_bulk_post_too_big', $next_document );
509 595 $results[] = new \WP_Error( 'ep_too_big_request_skipped', 'Indexable too big. Request not sent.' );
510 596 continue;
511 597 }
512 - $body[] = $next_document;
513 - if ( mb_strlen( implode( '', $body ) ) < $current_buffer_size && ! empty( $documents ) ) {
598 +
599 + $body[] = $next_document;
600 + $current_body_size += $next_document_size;
601 +
602 + $can_add_more_documents = ( $current_body_size < $current_buffer_size && ! empty( $documents ) );
603 + if ( $can_add_more_documents ) {
514 604 continue;
515 605 }
516 - if ( mb_strlen( implode( '', $body ) ) > $max_buffer_size ) {
606 +
607 + if ( $current_body_size > $max_buffer_size ) {
517 608 // The last document added to body made it too big, so let's give it back.
518 - array_unshift( $documents, array_pop( $body ) );
609 + $removed_document = array_pop( $body );
610 + $current_body_size -= mb_strlen( $removed_document );
611 + array_unshift( $documents, $removed_document );
519 612 }
520 613 }
521 614
522 615 // Try the request.
@@ -522,9 +615,9 @@
522 615 // Try the request.
523 616 timer_start();
524 617 $result = Elasticsearch::factory()->bulk_index( $this->get_index_name(), $this->slug, implode( '', $body ) );
525 618 $request_time = timer_stop();
526 - $requests++;
619 + ++$requests;
527 620
528 621 /**
529 622 * Perform actions before a new batch of documents is processed.
530 623 *
@@ -541,37 +634,42 @@
541 634 do_action( 'ep_after_send_dynamic_bulk_request', $result, $body, $documents, $min_buffer_size, $max_buffer_size, $current_buffer_size, $request_time );
542 635
543 636 // It failed, possibly adjust the buffer size and try again.
544 637 if ( is_wp_error( $result ) ) {
638 + $error_code = $result->get_error_code();
639 +
545 640 // Too many requests, wait and try again.
546 - if ( 429 === $result->get_error_code() ) {
641 + if ( 429 === $error_code ) {
547 642 sleep( 2 );
548 643 }
549 644
550 645 // If the error is not a "Request too big" then we really fail this batch of documents.
551 - if ( 413 !== $result->get_error_code() ) {
646 + if ( 413 !== $error_code ) {
552 647 $results[] = $result;
553 648 continue;
554 649 }
555 650
556 651 if ( count( $body ) === 1 ) {
557 - $max_buffer_size = min( $max_buffer_size, mb_strlen( implode( '', $body ) ) );
558 - $results[] = $result;
559 - $body = [];
652 + $max_buffer_size = min( $max_buffer_size, $current_body_size );
653 + $results[] = $result;
654 + $body = [];
655 + $current_body_size = 0;
560 656 continue;
561 657 }
562 658
563 659 // As the buffer is as small as possible, return the error.
564 - if ( mb_strlen( implode( '', $body ) ) === $min_buffer_size ) {
660 + if ( $current_body_size === $min_buffer_size ) {
565 661 $results[] = $result;
566 662 continue;
567 663 }
568 664
569 665 // We have a too big buffer. Remove one doc from the body, and set both max and current as its size.
570 - array_unshift( $documents, array_pop( $body ) );
666 + $removed_document = array_pop( $body );
667 + $current_body_size -= mb_strlen( $removed_document );
668 + array_unshift( $documents, $removed_document );
571 669
572 670 $max_buffer_size = count( $body ) ?
573 - max( $min_buffer_size, mb_strlen( implode( '', $body ) ) ) :
671 + max( $min_buffer_size, $current_body_size ) :
574 672 $min_buffer_size;
575 673
576 674 $current_buffer_size = $max_buffer_size;
577 675 continue;
@@ -577,15 +675,16 @@
577 675 continue;
578 676 }
579 677
580 678 // Things worked so we can try to bump the buffer size.
581 - if ( $current_buffer_size < $max_buffer_size && mb_strlen( implode( '', $body ) ) > $current_buffer_size ) {
679 + if ( $current_buffer_size < $max_buffer_size && $current_body_size > $current_buffer_size ) {
582 680 $current_buffer_size = min( ( $current_buffer_size + $incremental_step ), $max_buffer_size );
583 681 }
584 682
585 683 $results[] = $result;
586 684
587 - $body = [];
685 + $body = [];
686 + $current_body_size = 0;
588 687 } while ( ! empty( $documents ) );
589 688
590 689 /**
591 690 * Perform actions after a batch of documents was processed.
@@ -668,9 +767,8 @@
668 767 $prepared_meta[ $meta_key ] = array_map( array( $this, 'prepare_meta_value_types' ), $meta_values );
669 768 }
670 769
671 770 return $prepared_meta;
672 -
673 771 }
674 772
675 773 /**
676 774 * Prepare meta types for meta value
@@ -1105,9 +1203,9 @@
1105 1203 return ( (string) $new_mapping['settings']['index.number_of_shards'] === $stored_mapping[ $this->get_index_name() ]['settings']['index']['number_of_shards'] );
1106 1204 }
1107 1205
1108 1206 /**
1109 - * Utilitary function to check if the indexable is being fully reindexed, i.e.,
1207 + * Utility function to check if the indexable is being fully reindexed, i.e.,
1110 1208 * the index was deleted, a new mapping was sent and content is being reindexed.
1111 1209 *
1112 1210 * @param int|null $blog_id Blog ID
1113 1211 * @return boolean
@@ -1148,9 +1246,9 @@
1148 1246 * in a standardized format. This is necessary so we can genericize the index
1149 1247 * process across indexables.
1150 1248 *
1151 1249 * @param array $args Array to query DB against.
1152 - * @return boolean
1250 + * @return array
1153 1251 */
1154 1252 abstract public function query_db( $args );
1155 1253
1156 1254 /**
@@ -1173,9 +1271,9 @@
1173 1271 * @param array $search_fields Search fields
1174 1272 * @param array $query_vars Query vars
1175 1273 * @return SearchAlgorithm Instance of search algorithm to be used
1176 1274 */
1177 - public function get_search_algorithm( string $search_text, array $search_fields, array $query_vars ) : \ElasticPress\SearchAlgorithm {
1275 + public function get_search_algorithm( string $search_text, array $search_fields, array $query_vars ): \ElasticPress\SearchAlgorithm {
1178 1276 /**
1179 1277 * Filter the search algorithm to be used
1180 1278 *
1181 1279 * @hook ep_{$indexable_slug}_search_algorithm
@@ -1196,22 +1294,23 @@
1196 1294 *
1197 1295 * @since 4.3.0
1198 1296 * @param null|int $blog_id (Optional) The blog ID. Sending `null` will use the current blog ID.
1199 1297 * @return array
1298 + * @throws \Exception An exception if meta fields are not available.
1200 1299 */
1201 1300 public function get_distinct_meta_field_keys( $blog_id = null ) {
1202 1301 $mapping = $this->get_mapping();
1203 1302
1204 1303 try {
1205 - if ( version_compare( Elasticsearch::factory()->get_elasticsearch_version(), '7.0', '<' ) ) {
1206 - $meta_fields = $mapping[ $this->get_index_name( $blog_id ) ]['mappings']['post']['properties']['meta']['properties'];
1304 + if ( version_compare( (string) Elasticsearch::factory()->get_elasticsearch_version(), '7.0', '<' ) ) {
1305 + $meta_fields = (array) $mapping[ $this->get_index_name( $blog_id ) ]['mappings']['post']['properties']['meta']['properties'];
1207 1306 } else {
1208 - $meta_fields = $mapping[ $this->get_index_name( $blog_id ) ]['mappings']['properties']['meta']['properties'];
1307 + $meta_fields = (array) $mapping[ $this->get_index_name( $blog_id ) ]['mappings']['properties']['meta']['properties'];
1209 1308 }
1210 1309 $meta_keys = array_values( array_keys( $meta_fields ) );
1211 1310 sort( $meta_keys );
1212 1311 } catch ( \Throwable $th ) {
1213 - return new \Exception( 'Meta fields not available.', 0 );
1312 + throw new \Exception( 'Meta fields not available.', 0 );
1214 1313 }
1215 1314
1216 1315 return $meta_keys;
1217 1316 }
@@ -1277,9 +1376,9 @@
1277 1376 * @since 4.5.0
1278 1377 * @param array $mapping The mapping
1279 1378 * @return array
1280 1379 */
1281 - public function add_ngram_analyzer( array $mapping ) : array {
1380 + public function add_ngram_analyzer( array $mapping ): array {
1282 1381 $mapping['settings']['analysis']['analyzer']['edge_ngram_analyzer'] = array(
1283 1382 'type' => 'custom',
1284 1383 'tokenizer' => 'standard',
1285 1384 'filter' => array(