Skip to content

Commit

Permalink
Merge pull request #3661 from 10up/feature/es-error-data
Browse files Browse the repository at this point in the history
New ElasticsearchErrorInterpreter class + Info available for sync page
  • Loading branch information
felipeelia committed Oct 4, 2023
2 parents a54ae19 + 6f58ffd commit e8335fe
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 62 deletions.
109 changes: 109 additions & 0 deletions includes/classes/ElasticsearchErrorInterpreter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php
/**
* Error Interpreter Class File
*
* @since 5.0.0
* @package elasticpress
*/

namespace ElasticPress;

defined( 'ABSPATH' ) || exit;

/**
* ElasticsearchErrorInterpreter class
*
* @package ElasticPress
*/
class ElasticsearchErrorInterpreter {
/**
* Format the error message and provide a solution
*
* @param string $error Error message
* @return array Array containing `error` and `solution`
*/
public function maybe_suggest_solution_for_es( $error ) {
$sync_url = Utils\get_sync_url();

if ( preg_match( '/no such index \[(.*?)\]/', $error, $matches ) ) {
return [
'error' => 'no such index [???]',
'solution' => sprintf(
/* translators: 1. Index name; 2. Sync Page URL */
__( 'It seems the %1$s index is missing. <a href="%2$s">Delete all data and sync</a> to fix the issue.', 'elasticpress' ),
'<code>' . $matches[1] . '</code>',
$sync_url
),
];
}

if ( preg_match( '/No mapping found for \[(.*?)\] in order to sort on/', $error, $matches ) ) {
return [
'error' => 'No mapping found for [???] in order to sort on',
'solution' => sprintf(
/* translators: 1. Index name; 2. Sync Page URL */
__( 'The field %1$s was not found. Make sure it is added to the list of indexed fields and run <a href="%2$s">a new sync</a> to fix the issue.', 'elasticpress' ),
'<code>' . $matches[1] . '</code>',
$sync_url
),
];
}

/* translators: 1. Field name; 2. Sync Page URL */
$field_type_solution = __( 'It seems you saved a post without doing a full sync first because <code>%1$s</code> is missing the correct mapping type. <a href="%2$s">Delete all data and sync</a> to fix the issue.', 'elasticpress' );

if ( preg_match( '/Fielddata is disabled on text fields by default. Set fielddata=true on \[(.*?)\]/', $error, $matches ) ) {
return [
'error' => 'Fielddata is disabled on text fields by default. Set fielddata=true on [???]',
'solution' => sprintf( $field_type_solution, $matches[1], $sync_url ),
];
}

if ( preg_match( '/field \[(.*?)\] is of type \[(.*?)\], but only numeric types are supported./', $error, $matches ) ) {
return [
'error' => 'field [???] is of type [???], but only numeric types are supported.',
'solution' => sprintf( $field_type_solution, $matches[1], $sync_url ),
];
}

if ( preg_match( '/Alternatively, set fielddata=true on \[(.*?)\] in order to load field data by uninverting the inverted index./', $error, $matches ) ) {
return [
'error' => 'Alternatively, set fielddata=true on [???] in order to load field data by uninverting the inverted index.',
'solution' => sprintf( $field_type_solution, $matches[1], $sync_url ),
];
}

if ( preg_match( '/Limit of total fields \[(.*?)\] in index \[(.*?)\] has been exceeded/', $error, $matches ) ) {
return [
'error' => 'Limit of total fields [???] in index [???] has been exceeded',
'solution' => sprintf(
/* translators: Elasticsearch or ElasticPress.io; 2. Link to article; 3. Link to article */
__( 'Your website content has more public custom fields than %1$s is able to store. Check our articles about <a href="%2$s">Elasticsearch field limitations</a> and <a href="%3$s">how to index just the custom fields you need</a> and sync again.', 'elasticpress' ),
Utils\is_epio() ? __( 'ElasticPress.io', 'elasticpress' ) : __( 'Elasticsearch', 'elasticpress' ),
'https://elasticpress.zendesk.com/hc/en-us/articles/360051401212-I-get-the-error-Limit-of-total-fields-in-index-has-been-exceeded-',
'https://elasticpress.zendesk.com/hc/en-us/articles/360052019111'
),
];
}

if ( Utils\is_epio() ) {
return [
'error' => $error,
'solution' => sprintf(
/* translators: ElasticPress.io My Account URL */
__( 'We did not recognize this error. Please open an ElasticPress.io <a href="%s">support ticket</a> so we can troubleshoot further.', 'elasticpress' ),
'https://www.elasticpress.io/my-account/'
),
];
}

return [
'error' => $error,
'solution' => sprintf(
/* translators: New GitHub issue URL */
__( 'We did not recognize this error. Please consider opening a <a href="%s">GitHub Issue</a> so we can add it to our list of supported errors.', 'elasticpress' ),
'https://github.com/10up/ElasticPress/issues/new/choose'
),
];
}
}
31 changes: 31 additions & 0 deletions includes/classes/IndexHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,10 @@ protected function output( $message_text, $type = 'info', $context = '' ) {
'status' => $type,
];

if ( in_array( $type, [ 'warning', 'error' ], true ) ) {
$message['errors'] = $this->build_message_errors_data( $message_text );
}

if ( is_callable( $this->args['output_method'] ) ) {
call_user_func( $this->args['output_method'], $message, $this->args, $this->index_meta, $context );
}
Expand Down Expand Up @@ -1479,6 +1483,33 @@ protected function flush_messages_queue() {
}
}

/**
* Get data for a given error message(s)
*
* @since 5.0.0
* @param string|array $messages Messages
* @return array
*/
protected function build_message_errors_data( $messages ) : array {
$messages = (array) $messages;
$error_interpreter = new \ElasticPress\ElasticsearchErrorInterpreter();

$errors_list = [];
foreach ( $messages as $message ) {
$error = $error_interpreter->maybe_suggest_solution_for_es( $message );

if ( ! isset( $errors_list[ $error['error'] ] ) ) {
$errors_list[ $error['error'] ] = [
'solution' => $error['solution'],
'count' => 1,
];
} else {
$errors_list[ $error['error'] ]['count']++;
}
}
return $errors_list;
}

/**
* Return singleton instance of class.
*
Expand Down
66 changes: 5 additions & 61 deletions includes/classes/StatusReport/FailedQueries.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,78 +158,22 @@ public function analyze_log( $log ) {
$error = Utils\get_elasticsearch_error_reason( $log );

$solution = ( ! empty( $error ) ) ?
$this->maybe_suggest_solution_for_es( $error ) :
( new \ElasticPress\ElasticsearchErrorInterpreter() )->maybe_suggest_solution_for_es( $error )['solution'] :
'';

return [ $error, $solution ];
}

/**
* Given an Elasticsearch error, try to suggest a solution
* DEPRECATED. Given an Elasticsearch error, try to suggest a solution
*
* @deprecated 5.0.0
* @param string $error The error
* @return string
*/
protected function maybe_suggest_solution_for_es( $error ) {
$sync_url = Utils\get_sync_url();

if ( preg_match( '/no such index \[(.*?)\]/', $error, $matches ) ) {
return sprintf(
/* translators: 1. Index name; 2. Sync Page URL */
__( 'It seems the %1$s index is missing. <a href="%2$s">Delete all data and sync</a> to fix the issue.', 'elasticpress' ),
'<code>' . $matches[1] . '</code>',
$sync_url
);
}

if ( preg_match( '/No mapping found for \[(.*?)\] in order to sort on/', $error, $matches ) ) {
return sprintf(
/* translators: 1. Index name; 2. Sync Page URL */
__( 'The field %1$s was not found. Make sure it is added to the list of indexed fields and run <a href="%2$s">a new sync</a> to fix the issue.', 'elasticpress' ),
'<code>' . $matches[1] . '</code>',
$sync_url
);
}

/* translators: 1. Field name; 2. Sync Page URL */
$field_type_solution = __( 'It seems you saved a post without doing a full sync first because <code>%1$s</code> is missing the correct mapping type. <a href="%2$s">Delete all data and sync</a> to fix the issue.', 'elasticpress' );

if ( preg_match( '/Fielddata is disabled on text fields by default. Set fielddata=true on \[(.*?)\]/', $error, $matches ) ) {
return sprintf( $field_type_solution, $matches[1], $sync_url );
}

if ( preg_match( '/field \[(.*?)\] is of type \[(.*?)\], but only numeric types are supported./', $error, $matches ) ) {
return sprintf( $field_type_solution, $matches[1], $sync_url );
}

if ( preg_match( '/Alternatively, set fielddata=true on \[(.*?)\] in order to load field data by uninverting the inverted index./', $error, $matches ) ) {
return sprintf( $field_type_solution, $matches[1], $sync_url );
}

if ( preg_match( '/Limit of total fields \[(.*?)\] in index \[(.*?)\] has been exceeded/', $error, $matches ) ) {
return sprintf(
/* translators: Elasticsearch or ElasticPress.io; 2. Link to article; 3. Link to article */
__( 'Your website content has more public custom fields than %1$s is able to store. Check our articles about <a href="%2$s">Elasticsearch field limitations</a> and <a href="%3$s">how to index just the custom fields you need</a> and sync again.', 'elasticpress' ),
Utils\is_epio() ? __( 'ElasticPress.io', 'elasticpress' ) : __( 'Elasticsearch', 'elasticpress' ),
'https://elasticpress.zendesk.com/hc/en-us/articles/360051401212-I-get-the-error-Limit-of-total-fields-in-index-has-been-exceeded-',
'https://elasticpress.zendesk.com/hc/en-us/articles/360052019111'
);
}

// field limit

if ( Utils\is_epio() ) {
return sprintf(
/* translators: ElasticPress.io My Account URL */
__( 'We did not recognize this error. Please open an ElasticPress.io <a href="%s">support ticket</a> so we can troubleshoot further.', 'elasticpress' ),
'https://www.elasticpress.io/my-account/'
);
}
_deprecated_function( __METHOD__, '5.0.0', '\ElasticPress\ElasticsearchErrorInterpreter::maybe_suggest_solution_for_es()' );

return sprintf(
/* translators: New GitHub issue URL */
__( 'We did not recognize this error. Please consider opening a <a href="%s">GitHub Issue</a> so we can add it to our list of supported errors. ', 'elasticpress' ),
'https://github.com/10up/ElasticPress/issues/new/choose'
);
return ( new \ElasticPress\ElasticsearchErrorInterpreter() )->maybe_suggest_solution_for_es( $error )['solution'];
}
}
135 changes: 135 additions & 0 deletions tests/php/TestElasticsearchErrorInterpreter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?php
/**
* Test the ElasticsearchErrorInterpreter class methods
*
* @since 5.0.0
* @package elasticpress
*/

namespace ElasticPressTest;

use ElasticPress\ElasticsearchErrorInterpreter;
use ElasticPress\Utils;

/**
* TestElasticsearchErrorInterpreter test class
*/
class TestElasticsearchErrorInterpreter extends BaseTestCase {
/**
* Test the `maybe_suggest_solution_for_es` method
*
* @group elasticsearch-error-interpreter
*/
public function test_maybe_suggest_solution_for_es() {
$error_interpreter = new ElasticsearchErrorInterpreter();

$error = 'Not set';
$solution = 'We did not recognize this error. Please consider opening a <a href="https://github.com/10up/ElasticPress/issues/new/choose">GitHub Issue</a> so we can add it to our list of supported errors.';
$suggested = $error_interpreter->maybe_suggest_solution_for_es( $error );

$this->assertSame( $error, $suggested['error'] );
$this->assertSame( $solution, $suggested['solution'] );
}

/**
* Test the `maybe_suggest_solution_for_es` method when an index is missing
*
* @group elasticsearch-error-interpreter
*/
public function test_maybe_suggest_solution_for_es_no_index() {
$error_interpreter = new ElasticsearchErrorInterpreter();
$sync_url = Utils\get_sync_url();

$error = 'no such index [elasticpresstest-post-1]';
$solution = 'It seems the <code>elasticpresstest-post-1</code> index is missing. <a href="' . $sync_url . '">Delete all data and sync</a> to fix the issue.';
$suggested = $error_interpreter->maybe_suggest_solution_for_es( $error );

$this->assertSame( 'no such index [???]', $suggested['error'] );
$this->assertSame( $solution, $suggested['solution'] );
}

/**
* Test the `maybe_suggest_solution_for_es` method when sorting on a wrong field
*
* @group elasticsearch-error-interpreter
*/
public function test_maybe_suggest_solution_for_es_order() {
$error_interpreter = new ElasticsearchErrorInterpreter();
$sync_url = Utils\get_sync_url();

$error = 'No mapping found for [fieldname] in order to sort on';
$solution = 'The field <code>fieldname</code> was not found. Make sure it is added to the list of indexed fields and run <a href="' . $sync_url . '">a new sync</a> to fix the issue.';
$suggested = $error_interpreter->maybe_suggest_solution_for_es( $error );

$this->assertSame( 'No mapping found for [???] in order to sort on', $suggested['error'] );
$this->assertSame( $solution, $suggested['solution'] );
}

/**
* Test the `maybe_suggest_solution_for_es` method when using the wrong mapping (fielddata error)
*
* @group elasticsearch-error-interpreter
*/
public function test_maybe_suggest_solution_for_es_wrong_mapping_fielddata() {
$error_interpreter = new ElasticsearchErrorInterpreter();
$sync_url = Utils\get_sync_url();

$error = 'Fielddata is disabled on text fields by default. Set fielddata=true on [fieldname]';
$solution = 'It seems you saved a post without doing a full sync first because <code>fieldname</code> is missing the correct mapping type. <a href="' . $sync_url . '">Delete all data and sync</a> to fix the issue.';
$suggested = $error_interpreter->maybe_suggest_solution_for_es( $error );

$this->assertSame( 'Fielddata is disabled on text fields by default. Set fielddata=true on [???]', $suggested['error'] );
$this->assertSame( $solution, $suggested['solution'] );
}

/**
* Test the `maybe_suggest_solution_for_es` method when using the wrong mapping (numeric error)
*
* @group elasticsearch-error-interpreter
*/
public function test_maybe_suggest_solution_for_es_wrong_mapping_numeric() {
$error_interpreter = new ElasticsearchErrorInterpreter();
$sync_url = Utils\get_sync_url();

$error = 'field [fieldname] is of type [type], but only numeric types are supported.';
$solution = 'It seems you saved a post without doing a full sync first because <code>fieldname</code> is missing the correct mapping type. <a href="' . $sync_url . '">Delete all data and sync</a> to fix the issue.';
$suggested = $error_interpreter->maybe_suggest_solution_for_es( $error );

$this->assertSame( 'field [???] is of type [???], but only numeric types are supported.', $suggested['error'] );
$this->assertSame( $solution, $suggested['solution'] );
}

/**
* Test the `maybe_suggest_solution_for_es` method when using the wrong mapping (inverted error)
*
* @group elasticsearch-error-interpreter
*/
public function test_maybe_suggest_solution_for_es_wrong_mapping_inverted() {
$error_interpreter = new ElasticsearchErrorInterpreter();
$sync_url = Utils\get_sync_url();

$error = 'Alternatively, set fielddata=true on [fieldname] in order to load field data by uninverting the inverted index.';
$solution = 'It seems you saved a post without doing a full sync first because <code>fieldname</code> is missing the correct mapping type. <a href="' . $sync_url . '">Delete all data and sync</a> to fix the issue.';
$suggested = $error_interpreter->maybe_suggest_solution_for_es( $error );

$this->assertSame( 'Alternatively, set fielddata=true on [???] in order to load field data by uninverting the inverted index.', $suggested['error'] );
$this->assertSame( $solution, $suggested['solution'] );
}

/**
* Test the `maybe_suggest_solution_for_es` method when the fields limit is reached
*
* @group elasticsearch-error-interpreter
*/
public function test_maybe_suggest_solution_for_es_limit_fields() {
$error_interpreter = new ElasticsearchErrorInterpreter();
$sync_url = Utils\get_sync_url();

$error = 'Limit of total fields [1000] in index [elasticpresstest-post-1] has been exceeded';
$solution = 'Your website content has more public custom fields than Elasticsearch is able to store. Check our articles about <a href="https://elasticpress.zendesk.com/hc/en-us/articles/360051401212-I-get-the-error-Limit-of-total-fields-in-index-has-been-exceeded-">Elasticsearch field limitations</a> and <a href="https://elasticpress.zendesk.com/hc/en-us/articles/360052019111">how to index just the custom fields you need</a> and sync again.';
$suggested = $error_interpreter->maybe_suggest_solution_for_es( $error );

$this->assertSame( 'Limit of total fields [???] in index [???] has been exceeded', $suggested['error'] );
$this->assertSame( $solution, $suggested['solution'] );
}
}
2 changes: 1 addition & 1 deletion tests/php/TestStatusReport.php
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ function( $logs ) use ( $time_stamp, $random_no ) {
),
'recommended_solution' => array(
'label' => 'Recommended Solution',
'value' => 'We did not recognize this error. Please consider opening a <a href="https://github.com/10up/ElasticPress/issues/new/choose">GitHub Issue</a> so we can add it to our list of supported errors. ',
'value' => 'We did not recognize this error. Please consider opening a <a href="https://github.com/10up/ElasticPress/issues/new/choose">GitHub Issue</a> so we can add it to our list of supported errors.',
),
'es_req' => array(
'label' => 'Elasticsearch Request',
Expand Down

0 comments on commit e8335fe

Please sign in to comment.