Skip to content

Commit

Permalink
Merge pull request #50 from 10up/feature/ep-issue-3180
Browse files Browse the repository at this point in the history
Bring the Users feature to ElasticPress Labs
  • Loading branch information
felipeelia authored Feb 27, 2023
2 parents bfc744d + 3c94ffc commit 3da562b
Show file tree
Hide file tree
Showing 15 changed files with 3,860 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ jobs:
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Setup Elasticsearch
uses: getong/elasticsearch-action@v1.2
with:
elasticsearch version: '7.5.0'

- name: Set standard 10up cache directories
run: |
composer config -g cache-dir "${{ env.COMPOSER_CACHE }}"
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@
"phpunit/phpunit": "^9",
"10up/wp_mock": "dev-trunk",
"10up/phpcs-composer": "dev-master",
"10up/elasticpress": "*",
"10up/elasticpress": "dev-develop",
"yoast/phpunit-polyfills": "^1.0"
},
"scripts": {
"lint": "phpcs .",
"lint-fix": "phpcbf .",
"test": "phpunit",
"setup-local-tests": "bash bin/install-wp-tests.sh epl_wp_test root password mysql trunk true"
"setup-local-tests": "bash bin/install-wp-tests.sh epl_wp_test root password 127.0.0.1 latest true"
},
"config": {
"allow-plugins": {
Expand Down
18 changes: 10 additions & 8 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

95 changes: 95 additions & 0 deletions includes/classes/Feature/Users.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php
/**
* Users feature
*
* @since 2.1.0
* @package ElasticPressLabs
*/

namespace ElasticPressLabs\Feature;

use ElasticPress\Feature;
use ElasticPress\Indexables;
use ElasticPress\FeatureRequirementsStatus;

/**
* Users feature class
*/
class Users extends Feature {
/**
* Initialize feature setting it's config
*/
public function __construct() {
$this->slug = 'users';

$this->title = esc_html__( 'Users', 'elasticpress' );

$this->summary = __( 'Improve user search relevancy and query performance.', 'elasticpress' );

$this->docs_url = __( 'https://elasticpress.zendesk.com/hc/en-us/articles/360050447492-Configuring-ElasticPress-via-the-Plugin-Dashboard#users', 'elasticpress' );

$this->requires_install_reindex = true;

Indexables::factory()->register( new \ElasticPressLabs\Indexable\User\User(), false );

parent::__construct();
}

/**
* Hook search functionality
*/
public function setup() {
Indexables::factory()->activate( 'user' );

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

/**
* Setup feature on each page load
*/
public function search_setup() {
add_filter( 'ep_elasticpress_enabled', [ $this, 'integrate_search_queries' ], 10, 2 );
}

/**
* Output feature box long text
*/
public function output_feature_box_long() {
?>
<p><?php esc_html_e( 'This feature will empower your website to overcome traditional WordPress user search and query limitations that can present themselves at scale.', 'elasticpress' ); ?></p>
<p><?php esc_html_e( 'Be aware that storing user data may bound you to certain legal obligations depending on your local government regulations.', 'elasticpress' ); ?></p>
<?php
}

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

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

return $enabled;
}

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

return $status;
}
}
207 changes: 207 additions & 0 deletions includes/classes/Indexable/User/QueryIntegration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
<?php
/**
* Integrate with WP_User_Query
*
* @since 2.1.0
* @package ElasticPressLabs
*/

namespace ElasticPressLabs\Indexable\User;

use ElasticPress\Indexables;
use \WP_User_Query;

if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}

/**
* Query integration class
*/
class QueryIntegration {

/**
* Checks to see if we should be integrating and if so, sets up the appropriate actions and filters.
*
* @param string $indexable_slug Indexable slug. Optional.
*/
public function __construct( $indexable_slug = 'user' ) {
// Ensure that we are currently allowing ElasticPress to override the normal WP_Query
// Indexable->is_full_reindexing() is not available at this point yet, so using the IndexHelper version of it.
if ( \ElasticPress\IndexHelper::factory()->is_full_reindexing( $indexable_slug ) ) {
return;
}

add_filter( 'users_pre_query', [ $this, 'maybe_filter_query' ], 10, 2 );

// Add header
add_action( 'pre_get_users', array( $this, 'action_pre_get_users' ), 5 );
}

/**
* If WP_User_Query meets certain conditions, query results from ES
*
* @param array $results Users array.
* @param WP_User_Query $query Current query.
* @return array
*/
public function maybe_filter_query( $results, WP_User_Query $query ) {
$user_indexable = Indexables::factory()->get( 'user' );

/**
* Filter to skip user query integration
*
* @hook ep_skip_user_query_integration
* @param {bool} $skip True meanas skip query
* @param {WP_User_Query} $query User query
* @since 2.1.0
* @return {boolean} New value
*/
if ( ! $user_indexable->elasticpress_enabled( $query ) || apply_filters( 'ep_skip_user_query_integration', false, $query ) ) {
return $results;
}

/**
* Filter cached user query users
*
* @hook ep_wp_query_search_cached_users
* @param {array} $users Array of users
* @param {WP_User_Query} $query User query
* @since 2.1.0
* @return {array} New users
*/
$new_users = apply_filters( 'ep_wp_query_search_cached_users', null, $query );

if ( null === $new_users ) {
$formatted_args = $user_indexable->format_args( $query->query_vars, $query );

$ep_query = $user_indexable->query_es( $formatted_args, $query->query_vars, null, $query );

if ( false === $ep_query ) {
return $results;
}

/**
* WP_User_Query does not let us set this property:
*
* $query->elasticsearch_success = true;
*/
$query->query_vars['elasticsearch_success'] = true;

$fields = $query->get( 'fields' );
$new_users = [];

if ( in_array( $fields, [ 'all', 'all_with_meta' ], true ) ) {
foreach ( $ep_query['documents'] as $document ) {
$new_users[] = $document['ID'];
}
} elseif ( is_array( $fields ) ) {
// WP_User_Query returns a stdClass.
foreach ( $ep_query['documents'] as $document ) {

$user = new \stdClass();
$user->elasticsearch = true; // Super useful for debugging.

foreach ( $fields as $field ) {
if ( 'id' === $field ) {
$field = 'ID';
}
$user->$field = $document[ $field ];
}

$new_users[] = $user;
}
} elseif ( is_string( $fields ) && ! empty( $fields ) ) {
foreach ( $ep_query['documents'] as $document ) {
$new_users[] = $document[ $fields ];
}
} else {
$new_users = $this->format_hits_as_users( $ep_query['documents'] );
}
}

$query->total_users = 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;

return $new_users;
}

/**
* Format the ES hits/results as WP_User objects.
*
* @param array $users The users that should be formatted.
* @return array
*/
protected function format_hits_as_users( $users ) {
$new_users = [];

foreach ( $users as $user_array ) {
$user = new \stdClass();

/**
* Filter arguments inserted into user object after search
*
* @hook ep_search_user_return_args
* @param {array} $args Array of arguments
* @since 2.1.0
* @return {array} New arguments
*/
$user_return_args = apply_filters(
'ep_search_user_return_args',
[
'ID',
'user_login',
'user_nicename',
'user_email',
'user_url',
'user_registered',
'user_status',
'display_name',
'spam',
'deleted',
'terms',
'meta',
]
);

foreach ( $user_return_args as $key ) {
if ( isset( $user_array[ $key ] ) ) {
$user->$key = $user_array[ $key ];
}
}

$user->elasticsearch = true; // Super useful for debugging.

$new_users[] = $user;
}

return $new_users;
}

/**
* Disables cache_results, adds header.
*
* @param WP_User_Query $query User query
*/
public function action_pre_get_users( $query ) {
/**
* Filter to skip user query integration
*
* @hook ep_skip_user_query_integration
* @param {bool} $skip True meanas skip query
* @param {WP_User_Query} $query User query
* @since 2.1.0
* @return {boolean} New value
*/
if ( ! Indexables::factory()->get( 'user' )->elasticpress_enabled( $query ) || apply_filters( 'ep_skip_user_query_integration', false, $query ) ) {
return;
}

if ( ! headers_sent() ) {
/**
* Manually setting a header as $wp_query isn't yet initialized
* when we call: add_filter('wp_headers', 'filter_wp_headers');
*/
header( 'X-ElasticPress-Search: true' );
}
}
}
Loading

0 comments on commit 3da562b

Please sign in to comment.