Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Basic VIP Enterprise Search Adapter #8

Merged
merged 13 commits into from
Apr 28, 2022
5 changes: 0 additions & 5 deletions .phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,6 @@
<!-- Exclude a few directories and autogenerated files. -->
<exclude-pattern>vendor/</exclude-pattern>

<!-- The adapter pattern relies on several files exporting the same class name. -->
<rule ref="WordPress.Files.FileName.InvalidClassFileName">
<exclude-pattern>adapters/</exclude-pattern>
</rule>

<!-- The version set here matches the minimum version tested in buddy.yml. -->
<config name="minimum_supported_wp_version" value="5.9"/>
</ruleset>
17 changes: 16 additions & 1 deletion elasticsearch-extensions.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@
* @package Elasticsearch_Extensions
*/

namespace Elasticsearch_Extensions;
use Elasticsearch_Extensions\Controller;
use Elasticsearch_Extensions\Adapters\VIP_Enterprise_Search;

require_once __DIR__ . '/lib/autoload.php';

// Load adapter automatically based on environment settings.
if ( defined( 'VIP_ENABLE_VIP_SEARCH' ) && VIP_ENABLE_VIP_SEARCH ) {
Controller::instance()->load_adapter( VIP_Enterprise_Search::instance() );
}

/**
* A helper function for getting the instance of the controller class.
*
* @return Controller
*/
function elasticsearch_extensions(): Controller {
return Controller::instance();
}
116 changes: 116 additions & 0 deletions lib/adapters/class-adapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php
/**
* Elasticsearch Extensions Adapters: Adapter Abstract Class
*
* @package Elasticsearch_Extensions
*/

namespace Elasticsearch_Extensions\Adapters;

/**
* An abstract class that establishes base functionality and sets requirements
* for implementing classes.
*
* @package Elasticsearch_Extensions
*/
abstract class Adapter {

/**
* Whether post type aggregations are active or not.
*
* @var bool
*/
private bool $aggregate_post_types = false;

/**
* Stores an array of taxonomy slugs that should be added to aggregations.
*
* @var array
*/
private array $aggregate_taxonomies = [];

/**
* Stores aggregation data from the Elasticsearch response.
*
* @var array
*/
private array $aggregations = [];

/**
* Holds a reference to the singleton instance.
*
* @var Adapter
*/
private static Adapter $instance;

/**
* Enables an aggregation based on post type.
*/
public function enable_post_type_aggregation(): void {
$this->aggregate_post_types = true;
}

/**
* A function to enable an aggregation for a specific taxonomy.
*
* @param string $taxonomy The taxonomy slug for which to enable an aggregation.
*/
public function enable_taxonomy_aggregation( string $taxonomy ): void {
$this->aggregate_taxonomies[] = $taxonomy;
}

/**
* Gets the flag for whether post types should be aggregated or not.
*
* @return bool Whether post types should be aggregated or not.
*/
protected function get_aggregate_post_types(): bool {
return $this->aggregate_post_types;
}

/**
* Gets the list of taxonomies to be aggregated.
*
* @return array The list of taxonomy slugs to be aggregated.
*/
protected function get_aggregate_taxonomies(): array {
return $this->aggregate_taxonomies;
}

/**
* Gets aggregation results.
*
* @return array The aggregation results.
*/
protected function get_aggregations(): array {
return $this->aggregations;
}

/**
* Get an instance of the class.
*
* @return Adapter
*/
public static function instance(): Adapter {
$class_name = get_called_class();
if ( ! isset( self::$instance ) ) {
self::$instance = new $class_name();
self::$instance->setup();
}
return self::$instance;
}

/**
* Sets aggregation results.
*
* @param array $aggregations An array of aggregation results to be stored.
*/
protected function set_aggregations( array $aggregations ): void {
$this->aggregations = $aggregations;
}

/**
* Sets up the singleton by registering action and filter hooks.
*/
abstract public function setup(): void;
}
55 changes: 55 additions & 0 deletions lib/adapters/class-vip-enterprise-search.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/**
* Elasticsearch Extensions Adapters: VIP Enterprise Search Adapter
*
* @package Elasticsearch_Extensions
*/

namespace Elasticsearch_Extensions\Adapters;

/**
* An adapter for WordPress VIP Enterprise Search.
*
* @package Elasticsearch_Extensions
*/
class VIP_Enterprise_Search extends Adapter {

/**
* Filters ElasticPress request query args to apply registered customizations.
*
* @param array $request_args Request arguments.
* @param string $path Request path.
* @param string $index Index name.
* @param string $type Index type.
*
* @return array New request arguments.
*/
public function filter_ep_query_request_args( $request_args, $path, $index, $type ): array {
// Try to convert the request body to an array so we can work with it.
$dsl = json_decode( $request_args['body'], true );
if ( ! is_array( $dsl ) ) {
return $request_args;
}

// Add our aggregations.
if ( $this->get_aggregate_post_types() ) {
$dsl['aggs']['post_type'] = [
'terms' => [
'field' => 'post_type.raw',
],
];
}

// Re-encode the body into the request args.
$request_args['body'] = wp_json_encode( $dsl );

return $request_args;
}

/**
* Setup function. Registers action and filter hooks.
*/
public function setup(): void {
add_filter( 'ep_query_request_args', [ $this, 'filter_ep_query_request_args' ], 10, 4 );
}
}
2 changes: 1 addition & 1 deletion lib/autoload.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function autoload( string $class ) {
$dirs = explode( '\\', $class );
$class = array_pop( $dirs );

require_once dirname( __DIR__ ) . '/' . implode( '/', $dirs ) . '/class-' . $class . '.php';
require_once __DIR__ . '/' . implode( '/', $dirs ) . '/class-' . $class . '.php';
}

spl_autoload_register( '\Elasticsearch_Extensions\autoload' );
82 changes: 82 additions & 0 deletions lib/class-controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php
/**
* Elasticsearch Extensions: Controller
*
* @package Elasticsearch_Extensions
*/

namespace Elasticsearch_Extensions;

use Elasticsearch_Extensions\Adapters\Adapter;

/**
* The controller class, which is responsible for loading adapters and
* configuration.
*
* @package Elasticsearch_Extensions
*/
class Controller {

/**
* The active adapter.
*
* @var Adapter
*/
private Adapter $adapter;

/**
* Holds a reference to the singleton instance.
*
* @var Controller
*/
private static Controller $instance;

/**
* Enables an aggregation based on post type.
*
* @return Controller The instance of the class to allow for chaining.
*/
public function enable_post_type_aggregation(): Controller {
if ( isset( $this->adapter ) ) {
$this->adapter->enable_post_type_aggregation();
}

return $this;
}

/**
* A function to enable an aggregation for a specific taxonomy.
*
* @param string $taxonomy The taxonomy slug for which to enable an aggregation.
*
* @return Controller The instance of the class to allow for chaining.
*/
public function enable_taxonomy_aggregation( string $taxonomy ): Controller {
if ( isset( $this->adapter ) ) {
$this->adapter->enable_taxonomy_aggregation( $taxonomy );
}

return $this;
}

/**
* Get an instance of the class.
*
* @return Controller
*/
public static function instance(): Controller {
if ( ! isset( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}

/**
* Loads an instance of an Adapter into the controller.
*
* @param Adapter $adapter The adapter to load.
*/
public function load_adapter( Adapter $adapter ): void {
$this->adapter = $adapter;
}
}