Skip to content

Commit

Permalink
Merge pull request #1531 from 10up/feature/comments-indexable
Browse files Browse the repository at this point in the history
Add a Comments Indexable
  • Loading branch information
felipeelia authored Jun 28, 2021
2 parents 3c3ff85 + 042e01c commit a01271b
Show file tree
Hide file tree
Showing 15 changed files with 5,325 additions and 6 deletions.
151 changes: 150 additions & 1 deletion docs/indexables.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
In ElasticPress 3.0, we’ve introduced the concept of Indexables. In the past, ElasticPress integrated with WordPress’ WP_Query API, which enabled redirection of WP_Query queries through Elasticsearch instead of MySQL. Indexables takes this a step further, enabling indexing, search, and queries on any queryable object in WordPress.

As of 3.0, ElasticPress ships with the following built-in Indexables: Posts, Terms, and Users. The Posts Indexable roughly corresponds to the previous WP_Query integration, and the Users Indexable adds support for WP_User_Query in ElasticPress. The Terms Indexable adds support for [WP_Term_Query](https://developer.wordpress.org/reference/classes/wp_term_query/). Future versions of ElasticPress will include additional WordPress APIs (such as WP_Comment_Query), and you can also create your own custom Indexables by extending the Indexable class.
As of 3.6, ElasticPress ships with the following built-in Indexables: Posts, Terms, Users, and Comments. The Posts Indexable roughly corresponds to the previous [WP_Query](https://developer.wordpress.org/reference/classes/wp_query/) integration, the Users Indexable adds support for [WP_User_Query](https://developer.wordpress.org/reference/classes/wp_user_query/) in ElasticPress, the Comments Indexable adds support for [WP_Comment_Query](https://developer.wordpress.org/reference/classes/WP_Comment_Query/), and the Terms Indexable adds support for [WP_Term_Query](https://developer.wordpress.org/reference/classes/wp_term_query/). Future versions of ElasticPress will include additional WordPress APIs, and you can also create your own custom Indexables by extending the Indexable class.

## Post Indexable

Expand Down Expand Up @@ -533,3 +533,152 @@ The User Indexable is only enabled if the User feature is activated. ElasticPres
* ```search_columns```

Specify columns in the user database table to be searched. NB: this is merged into ```search_fields``` before being sent to Elasticsearch with ```search_fields``` overwriting ```search_columns```.

## Comment Indexable
The Comment Indexable is only enabled if the Comment feature is activated. ElasticPress integrates with `WP_Comment_Query` if the `ep_integrate` or `s` (search) parameter passed (see below) to the query object. ElasticPress converts `WP_Comment_Query` arguments to Elasticsearch readable queries. Supported `WP_Comment_Query` parameters are listed and explained below.

### Supported WP_Comment_Query Parameters

* ```number``` (*int*)

The maximum returned number of results.

* ```offset``` (*int*)

Offset the returned results (needed in pagination).

* ```paged``` (*int*)

Defines the page of results to return. When used with ```$offset```, ```$offset``` takes precedence. Default 1.

* ```orderby``` (*string*)

Order results by a specific field name. Supports: ```comment_agent```, ```comment_approved```, `comment_author`, `comment_author_email`, ```comment_author_IP```, ```comment_author_url```, ```comment_content```, ```comment_date```, ```comment_date_gmt```, ```comment_ID```, ```comment_karma```, ```comment_parent```, ```comment_post_ID```, ```comment_type```, ```user_id```, ```meta_value``` and ```meta_value_num```; anything else will be interpretted as a document path i.e. `meta.my_key.long` or `meta.my_key.raw`. Default is ```comment_date_gmt```.

* ```order``` (*string*)

Which direction to order results in. Accepts ```ASC``` and ```DESC```. Default is ```DESC```.

* ```author_email``` (*string*)

Single email address that should match the comment author email address.

* ```author_url``` (*string*)

Single URL that should match the comment author URL value.

* ```user_id``` (*int*)

Only show comments that match a specific user ID.

* ```author__in``` (*array*)

An array of author IDs to include comments for.

* ```author__not_in``` (*array*)
An array of author IDs to exclude from results.

* ```comment_in``` (*array*)

An array of comment IDs to include comments for.

* ```comment__not_in``` (*array*)

An array of comment IDs to exclude from results.

* ```date_query``` (*array*)

```date_query``` accepts an array of keys and values (array|string|int) to find comments created on
specific dates/times. See the ```WP_Query``` ```date_query``` documentation for a list of supported fields.
* ```fields``` (*string*)
Which fields to return. Accepts ```ids``` or empty for all fields.
* ```count``` (*boolean*)
Return a comment count. Accepts ```true``` or ```false```. Default ```false```.
* ```karma``` (*int*)
Karma score to retrieve matching comments for.
* ```meta_key``` (*string*)
Allows you to query comment meta with the defined key.
* ```meta_value``` (*string*)
Include comments with a matching comment meta value. Requires ```$meta_key``` to be set.
* ```meta_query``` (*array*)
Filter comments by comment meta conditions. Meta arrays and objects are serialized due to limitations of Elasticsearch. See the ```WP_Query``` ```meta_query``` documentation for supported fields. Takes an array of form:
```php
new WP_Comment_Query( array(
'search' => 'search phrase',
'meta_query' => array(
array(
'key' => 'key_name',
'value' => 'meta value',
'compare' => '=',
),
),
) );
* ```hierarchical``` (*boolean|string*)
Whether to include comment descendants in the results. Accepted values are ```threaded```, ```flat``` or ```false```. Default ```false```. This parameter is ignored if ```$fields``` is set to ```ids```.
* ```parent``` (*int*)
Only show comments that match a specific parent ID.
* ```parent__in``` (*array*)
An array of parents IDs to include comments for.
* ```parent__not_in``` (*array*)
An array of parent IDs to exclude from results.
* ```post_author``` (*int*)
Only show comments that are associated with a specific post author ID.
* ```post_author__in``` (*array*)
An array of post author IDs to include comments for.
* ```post_author__not_in``` (*array*)
An array of post author IDs to exclude from results.
* ```post_id``` (*int*)
Only show comments that are associated with a specific post ID.
* ```post__in``` (*array*)
An array of post IDs to include comments for.
* ```post__not_in``` (*array*)
An array of post IDs to exclude from results.
* ```post_status``` (*string|array*)
Post status or array of post statuses to retrieve affiliated comments for. Pass 'any' to match any value.
* ```post_type``` (*string*)
Post type to retrieve affiliated comments for. Pass 'any' to match any value.
* ```post_name``` (*string*)
Post name to retrieve affiliated comments for.
* ```post_parent``` (*int*)
Post parent ID to retrieve affiliated comments for.
* ```search``` (*string*)
Search keyword. By default used to search against ```comment_author```, ```comment_author_email```, ```comment_author_url```, ```comment_author_IP``` and ```comment_content```.
* ```status``` (*string|array*)
Comment stati to limit results by. Accepts an array or space/comma-separated list of ```hold``` (```comment_status=0```), ```approve``` (```comment_status=1```), ```all```, or a custom comment status. Default ```all```.
* ```include_unapproved``` (*array*)
Array of IDs or email addresses of users whose unapproved comments will be returned by the query regardless of ```$status```.
* ```type``` (*string|array*)
Include comments of a given type, or array of types. Accepts ```comment```, ```pings```, or any custom type string.
* ```type__in``` (*array*)
An array of comment types to include comments for.
* ```type__not_in``` (*array*)
An array of comment types to exclude from results.
The following are special parameters that are only supported by ElasticPress.
* ```search_fields``` (*array*)
If not specified, defaults to ```array( 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_author_IP', 'comment_content' )```.
* ```comment_author``` (*string*)
Applies current search to comment author name.
* ```comment_author_email``` (*string*)
Applies current search to comment author email.
* ```comment_author_url``` (*string*)
Applies current search to comment author URL.
* ```comment_author_IP``` (*string*)
Applies current search to comment author IP address.
* ```comment_content``` (*string*)
Applies current search to comment content.
* ```meta``` (*string* => *array*/*string*)
Applies the current search to comment meta. The following will fuzzy search across ```comment_author```, ```comment_content```, and comment meta keys ```meta_key_1``` and ```meta_key_2```:
```php
new WP_Comment_Query( array(
'search' => 'meta search phrase',
'search_fields' => array(
'comment_author',
'comment_content',
'meta' => array( 'meta_key_1', 'meta_key_2' ),
),
) );
```

* ```sites``` (*int*/*string*/*array*)

This parameter only applies in a multi-site environment. It lets you search for comments on specific sites or across the network.
6 changes: 6 additions & 0 deletions elasticpress.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ function register_indexable_posts() {
new Feature\Documents\Documents()
);

if ( version_compare( $wp_version, '5.3', '>=' ) || 0 === stripos( $wp_version, '5.3-' ) ) {
Features::factory()->register_feature(
new Feature\Comments\Comments()
);
}

if ( version_compare( $wp_version, '5.3', '>=' ) || 0 === stripos( $wp_version, '5.3-' ) ) {
Features::factory()->register_feature(
new Feature\Terms\Terms()
Expand Down
131 changes: 131 additions & 0 deletions includes/classes/Feature/Comments/Comments.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php
/**
* Comments feature
*
* @since 3.6.0
* @package elasticpress
*/

namespace ElasticPress\Feature\Comments;

use ElasticPress\Feature as Feature;
use ElasticPress\Indexables as Indexables;
use ElasticPress\Indexable as Indexable;
use ElasticPress\FeatureRequirementsStatus as FeatureRequirementsStatus;

/**
* Comments feature class
*/
class Comments extends Feature {

/**
* Initialize feature, setting it's config
*
* @since 3.6.0
*/
public function __construct() {
$this->slug = 'comments';
$this->title = esc_html__( 'Comments', 'elasticpress' );
$this->requires_install_reindex = true;

parent::__construct();
}

/**
* Setup search functionality
*
* @since 3.6.0
*/
public function setup() {
Indexables::factory()->register( new Indexable\Comment\Comment() );

add_action( 'init', [ $this, 'search_setup' ] );
}

/**
* Setup search integration
*
* @since 3.6.0
*/
public function search_setup() {
$admin_integration = apply_filters( 'ep_admin_wp_query_integration', false );

if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
/**
* Filter to integrate with admin ajax queries
*
* @hook ep_ajax_wp_query_integration
* @param {bool} $integrate True to integrate
* @return {bool} New value
*/
if ( ! apply_filters( 'ep_ajax_wp_query_integration', false ) ) {
return;
} else {
$admin_integration = true;
}
}

if ( is_admin() && ! $admin_integration ) {
return;
}

add_filter( 'ep_elasticpress_enabled', [ $this, 'integrate_search_queries' ], 10, 2 );
}

/**
* Output feature box summary
*
* @since 3.6.0
*/
public function output_feature_box_summary() {
?>
<p><?php esc_html_e( 'Improve comment search relevancy and query performance.', 'elasticpress' ); ?></p>
<?php
}

/**
* Output feature box long text
*
* @since 3.6.0
*/
public function output_feature_box_long() {
?>
<p><?php esc_html_e( 'This feature will empower your website to overcome traditional WordPress comment search and query limitations that can present themselves at scale.', 'elasticpress' ); ?></p>
<?php
}

/**
* Enable integration on search queries
*
* @param bool $enabled Whether EP is enabled
* @param \WP_Comment_Query $query Current query object.
* @since 3.6.0
* @return bool
*/
public function integrate_search_queries( $enabled, $query ) {
if ( ! is_a( $query, 'WP_Comment_Query' ) ) {
return $enabled;
}

if ( isset( $query->query_vars['ep_integrate'] ) && false === $query->query_vars['ep_integrate'] ) {
$enabled = false;
} elseif ( ! empty( $query->query_vars['search'] ) ) {
$enabled = true;
}

return $enabled;
}

/**
* Determine feature reqs status
*
* @since 3.6.0
* @return FeatureRequirementsStatus
*/
public function requirements_status() {
$status = new FeatureRequirementsStatus( 1 );

return $status;
}

}
61 changes: 61 additions & 0 deletions includes/classes/Feature/ProtectedContent/ProtectedContent.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,17 @@ public function setup() {
add_filter( 'ep_indexable_post_status', [ $this, 'get_statuses' ] );
add_filter( 'ep_indexable_post_types', [ $this, 'post_types' ], 10, 1 );

if ( Features::factory()->get_registered_feature( 'comments' )->is_active() ) {
add_filter( 'ep_indexable_comment_status', [ $this, 'get_comment_statuses' ] );
}

if ( is_admin() ) {
add_filter( 'ep_admin_wp_query_integration', '__return_true' );
add_action( 'pre_get_posts', [ $this, 'integrate' ] );

if ( Features::factory()->get_registered_feature( 'comments' )->is_active() ) {
add_action( 'pre_get_comments', [ $this, 'integrate_comments_query' ] );
}
}
}

Expand Down Expand Up @@ -175,6 +183,48 @@ public function integrate( $query ) {
remove_filter( 'ep_formatted_args', [ $search_feature, 'weight_recent' ], 10 );
}

/**
* Integrate EP into comment queries
*
* @param WP_Comment_Query $comment_query WP Comment Query
* @since 3.6.0
*/
public function integrate_comments_query( $comment_query ) {
// Lets make sure this doesn't interfere with the CLI
if ( defined( 'WP_CLI' ) && WP_CLI ) {
return;
}

$comment_types = array( 'comment', 'review' );

/**
* Filter protected content supported comment types.
*
* @hook ep_pc_supported_comment_types
* @since 3.6.0
* @param {array} $comment_types Comment types
* @return {array} New comment types
*/
$supported_comment_types = apply_filters( 'ep_pc_supported_comment_types', $comment_types );

$comment_type = $comment_query->query_vars['type'];

if ( is_array( $comment_type ) ) {
foreach ( $comment_type as $comment_type_value ) {
if ( ! in_array( $comment_type_value, $supported_comment_types, true ) ) {
return;
}
}

$comment_query->query_vars['ep_integrate'] = true;
} else {
if ( in_array( $comment_type, $supported_comment_types, true ) ) {
$comment_query->query_vars['ep_integrate'] = true;
}
}

}

/**
* Output feature box summary
*
Expand Down Expand Up @@ -212,6 +262,17 @@ public function get_statuses( $statuses ) {
return array_unique( array_merge( $statuses, array_values( $post_statuses ) ) );
}

/**
* Fetches all comment statuses we need to index
*
* @since 3.6.0
* @param array $comment_statuses Post statuses array
* @return array
*/
public function get_comment_statuses( $comment_statuses ) {
return [ 'all' ];
}

/**
* Determine feature reqs status
*
Expand Down
Loading

0 comments on commit a01271b

Please sign in to comment.