Skip to content

Commit

Permalink
Extract quite some code to core, prepare for support of Fluent (#67)
Browse files Browse the repository at this point in the history
* Extract quite some code to core, prepare for support of Fluent

* Update to use the dev-main of searchbackend for tests

* Fixup some minor methods to the base class

* Match the abstract from core

* Remove add/set/get class methods to move to core

* Test the get/set ClientQuery methods

* Make the clientQuery an array as default
  • Loading branch information
Firesphere authored Oct 22, 2023
1 parent e91a1ca commit c53a9fc
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 468 deletions.
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@
"require-dev": {
"silverstripe/recipe-cms": "^4|^5",
"phpunit/phpunit": "^9.5",
"friendsofphp/php-cs-fixer": "^3.35"
"friendsofphp/php-cs-fixer": "^3.35",
"firesphere/searchbackend": "dev-main as 1.999999999"
},
"extra": {
"branch-alias": {
"dev-main": "1.0.x-dev"
"dev-main": "2.0.x-dev"
}
},
"autoload": {
Expand Down
58 changes: 30 additions & 28 deletions src/Extensions/DataObjectElasticExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataExtension;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\ValidationException;
use SilverStripe\Versioned\Versioned;

/**
Expand All @@ -36,6 +35,7 @@
class DataObjectElasticExtension extends DataExtension
{
protected $deletedFromElastic;

/**
* @throws NotFoundExceptionInterface
*/
Expand Down Expand Up @@ -131,11 +131,39 @@ public function onAfterWrite()

if ($owner->hasField('ShowInSearch') &&
$owner->isChanged('ShowInSearch') &&
!$owner->ShowInSearch) {
!$owner->ShowInSearch
) {
$this->deletedFromElastic = $this->deleteFromElastic();
}
}

/**
* Check if:
* - Owner has Versioned
* - The versioned object is published
* - The owner has the "ShowInSearch" Field
* - And if so, is it set.
* @param SiteTree|DataObjectSearchExtension|DataObjectElasticExtension|Versioned|DataObject $owner
* @return bool
*/
public function shouldPush(DataObject $owner): bool
{
$showInSearch = true;
$versioned = $owner->hasExtension(Versioned::class);
if ($versioned) {
$versioned = $owner->isPublished();
} else {
// The owner is not versioned, so no publishing check
$versioned = true;
}
$hasField = $owner->hasField('ShowInSearch');
if ($hasField) {
$showInSearch = $owner->ShowInSearch;
}

return ($versioned && $showInSearch);
}

/**
* This is a separate method from the delete action, as it's a different route
* and query components.
Expand Down Expand Up @@ -175,30 +203,4 @@ public function isDeletedFromElastic()
{
return $this->deletedFromElastic;
}

/**
* Check if:
* - Owner has Versioned
* - The versioned object is published
* - The owner has the "ShowInSearch" Field
* - And if so, is it set.
* @param SiteTree|DataObjectSearchExtension|DataObjectElasticExtension|Versioned|DataObject $owner
* @return bool
*/
public function shouldPush(DataObject $owner): bool
{
$showInSearch = true;
$versioned = $owner->hasExtension(Versioned::class);
if ($versioned) {
$versioned = $owner->isPublished();
} else {
// The owner is not versioned, so no publishing check
$versioned = true;
}
$hasField = $owner->hasField('ShowInSearch');
if ($hasField) {
$showInSearch = $owner->ShowInSearch;
}
return ($versioned && $showInSearch);
}
}
4 changes: 0 additions & 4 deletions src/Extensions/ElasticSynonymExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,11 @@

namespace Firesphere\ElasticSearch\Extensions;

use Elastic\Elasticsearch\Exception\ClientResponseException;
use Elastic\Elasticsearch\Exception\MissingParameterException;
use Elastic\Elasticsearch\Exception\ServerResponseException;
use Firesphere\ElasticSearch\Models\SynonymSet;
use Firesphere\ElasticSearch\Services\ElasticCoreService;
use Firesphere\SearchBackend\Models\SearchSynonym;
use Psr\Container\NotFoundExceptionInterface;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\FieldList;
use SilverStripe\ORM\DataExtension;

/**
Expand Down
16 changes: 12 additions & 4 deletions src/Factories/DocumentFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
use Firesphere\SearchBackend\Extensions\DataObjectSearchExtension;
use Firesphere\SearchBackend\Factories\DocumentCoreFactory;
use Firesphere\SearchBackend\Services\BaseService;
use Psr\Container\NotFoundExceptionInterface;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Extensible;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\FieldType\DBDate;
use SilverStripe\ORM\FieldType\DBField;

/**
Expand All @@ -42,7 +42,7 @@ class DocumentFactory extends DocumentCoreFactory
* @param ElasticBaseIndex $index Index to push the documents to
* @param null $update Elastic doesn't have an "Update" object
* @return array Documents to be pushed
* @throws Exception
* @throws NotFoundExceptionInterface
*/
public function buildItems($fields, $index, $update = null): array
{
Expand All @@ -61,7 +61,9 @@ public function buildItems($fields, $index, $update = null): array
}
$doc = [];
$doc = $this->buildFields($fields, $doc, $item);
$doc['_text'] = $this->recursiveImplode($doc);
foreach ($index->getCopyFields() as $copyFieldName => $copyFields) {
$doc[$copyFieldName] = $this->recursiveImplode($doc);
}
$doc = $this->addDefaultFields($doc, $item);

$docs[] = $doc;
Expand Down Expand Up @@ -135,7 +137,13 @@ protected function addToDoc(&$doc, $options, $value): void
$doc[$name] = $value;//, $options['boost'], Document::MODIFIER_SET);
}

protected function recursiveImplode($arr)
/**
* Recursively implode the array with values
* to a single text blob to use as the main indexed
* @param array $arr
* @return string
*/
protected function recursiveImplode($arr): string
{
$return = [];
foreach ($arr as $key => $value) {
Expand Down
137 changes: 45 additions & 92 deletions src/Indexes/ElasticIndex.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,15 @@
use Firesphere\ElasticSearch\Queries\ElasticQuery;
use Firesphere\ElasticSearch\Results\SearchResult;
use Firesphere\ElasticSearch\Services\ElasticCoreService;
use Firesphere\ElasticSearch\Traits\IndexTraits\BaseIndexTrait;
use Firesphere\SearchBackend\Indexes\CoreIndex;
use Firesphere\SearchBackend\Traits\LoggerTrait;
use Firesphere\SearchBackend\Traits\QueryTraits\QueryFilterTrait;
use LogicException;
use Psr\Container\NotFoundExceptionInterface;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Extensible;
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\Deprecation;

/**
* Base for managing a Elastic core.
Expand All @@ -43,19 +40,25 @@ abstract class ElasticIndex extends CoreIndex
use Configurable;
use Injectable;
use QueryFilterTrait;
use BaseIndexTrait;
use LoggerTrait;

/**
* @var array
*/
protected $clientQuery;

protected $clientQuery = [];
/**
* @var array Fulltext fields
*/
protected $fulltextFields = [];
/**
* @var array Classes to index
* @var array Filterable fields
*/
protected $class = [];
protected $filterFields = [];

/**
* Set-up of core and fields through init
* @throws NotFoundExceptionInterface
*/
public function __construct()
{
$this->client = Injector::inst()->get(ElasticCoreService::class)->getClient();
Expand All @@ -65,34 +68,8 @@ public function __construct()
$this->extend('onAfterInit');
}


/**
* Required to initialise the fields.
* It's loaded in to the non-static properties for backward compatibility with FTS
* Also, it's a tad easier to use this way, loading the other way around would be very
* memory intensive, as updating the config for each item is not efficient
*/
public function init()
{
$config = self::config()->get($this->getIndexName());
if (!$config) {
Deprecation::notice('5', 'Please set an index name and use a config yml');
}

if (!empty($this->getClasses())) {
if (!$this->usedAllFields) {
Deprecation::notice('5', 'It is advised to use a config YML for most cases');
}

return;
}

$this->initFromConfig($config);
}


/**
* @param HTTPRequest|null $request
* @param HTTPRequest $request
* @return bool
* @throws ClientResponseException
* @throws MissingParameterException
Expand Down Expand Up @@ -139,94 +116,70 @@ public function indexExists(): bool
->asBool();
}

abstract public function getIndexName();

/**
* Get classes
*
* @return array
* {@inheritDoc}
* @param ElasticQuery $query
* @return SearchResult
* @throws ClientResponseException
* @throws ServerResponseException
*/
public function getClasses(): array
public function doSearch($query)
{
return $this->class;
$this->clientQuery = QueryBuilder::buildQuery($query, $this);

$result = $this->client->search($this->clientQuery);

return new SearchResult($result, $query, $this);
}

/**
* Generate the config from yml if possible
* @param array|null $config
* Get current client query array
* @return array
*/
protected function initFromConfig($config): void
public function getClientQuery(): array
{
if (!$config || !array_key_exists('Classes', $config)) {
throw new LogicException('No classes or config to index found!');
}

$this->setClasses($config['Classes']);

// For backward compatibility, copy the config to the protected values
// Saves doubling up further down the line
foreach (parent::$fieldTypes as $type) {
if (array_key_exists($type, $config)) {
$method = 'set' . $type;
if (method_exists($this, $method)) {
$this->$method($config[$type]);
}
}
}
return $this->clientQuery;
}

/**
* Set the classes
* Gives the option to completely override the client query set
*
* @param array $class
* @param array $clientQuery
* @return $this
*/
public function setClasses($class): self
public function setClientQuery(array $clientQuery): self
{
$this->class = $class;
$this->clientQuery = $clientQuery;

return $this;
}

/**
* @param ElasticQuery $query
* @return SearchResult
* @throws ClientResponseException
* @throws ServerResponseException
* Add a filterable field
* Compatibility stub for Solr
*
* @param $filterField
* @return $this
*/
public function doSearch(ElasticQuery $query)
public function addFilterField($filterField): self
{
$this->clientQuery = QueryBuilder::buildQuery($query, $this);

$result = $this->client->search($this->clientQuery);

$result = new SearchResult($result, $query, $this);
$this->filterFields[] = $filterField;
$this->addFulltextField($filterField);

return $result;
return $this;
}

/**
* Add a class to index or query
* $options is not used anymore, added for backward compatibility
* Add a single Fulltext field
*
* @param $class
* @param array $options unused
* @param string $fulltextField
* @param array $options
* @return $this
*/
public function addClass($class, $options = []): self
public function addFulltextField($fulltextField, $options = []): self
{
$this->class[] = $class;
$this->fulltextFields[] = $fulltextField;

return $this;
}

public function getClientQuery(): array
{
return $this->clientQuery;
}

public function setClientQuery(array $clientQuery): void
{
$this->clientQuery = $clientQuery;
}
}
2 changes: 1 addition & 1 deletion src/Queries/ElasticQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function addTerm(string $term, array $fields = [], int $boost = 1, $fuzzy
'text' => $term,
'fields' => $fields,
'boost' => $boost,
'fuzzy' => $fuzzy
'fuzzy' => $fuzzy
];

return $this;
Expand Down
Loading

0 comments on commit c53a9fc

Please sign in to comment.