mirror of
https://github.com/Codappix/search_core.git
synced 2024-11-23 20:36:12 +01:00
Merge branch 'develop' into support/76
Conflicts: Classes/DataProcessing/ProcessorInterface.php Classes/Domain/Index/AbstractIndexer.php Classes/Integration/Form/Finisher/DataHandlerFinisher.php Makefile Tests/Functional/Connection/Elasticsearch/FilterTest.php Tests/Functional/Fixtures/BasicSetup.ts Tests/Unit/Integration/Form/Finisher/DataHandlerFinisherTest.php composer.json
This commit is contained in:
commit
ee3987a746
61 changed files with 1534 additions and 387 deletions
|
@ -35,6 +35,7 @@ services:
|
||||||
install: make install
|
install: make install
|
||||||
|
|
||||||
script:
|
script:
|
||||||
|
- make cgl
|
||||||
- make unitTests
|
- make unitTests
|
||||||
- make functionalTests
|
- make functionalTests
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,10 @@ class Elasticsearch implements Singleton, ConnectionInterface
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (\Elastica\Exception\NotFoundException $exception) {
|
} catch (\Elastica\Exception\NotFoundException $exception) {
|
||||||
$this->logger->debug('Tried to delete document in index, which does not exist.', [$documentType, $identifier]);
|
$this->logger->debug(
|
||||||
|
'Tried to delete document in index, which does not exist.',
|
||||||
|
[$documentType, $identifier]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,25 +45,26 @@ class Facet implements FacetInterface
|
||||||
*/
|
*/
|
||||||
protected $options = [];
|
protected $options = [];
|
||||||
|
|
||||||
public function __construct($name, array $aggregation, ConfigurationContainerInterface $configuration)
|
public function __construct(string $name, array $aggregation, ConfigurationContainerInterface $configuration)
|
||||||
{
|
{
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
$this->buckets = $aggregation['buckets'];
|
$this->buckets = $aggregation['buckets'];
|
||||||
$this->field = $configuration->getIfExists('searching.facets.' . $this->name . '.field') ?: '';
|
|
||||||
|
$config = $configuration->getIfExists('searching.facets.' . $this->name) ?: [];
|
||||||
|
foreach ($config as $configEntry) {
|
||||||
|
if (isset($configEntry['field'])) {
|
||||||
|
$this->field = $configEntry['field'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getName() : string
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getName()
|
|
||||||
{
|
{
|
||||||
return $this->name;
|
return $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getField() : string
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getField()
|
|
||||||
{
|
{
|
||||||
return $this->field;
|
return $this->field;
|
||||||
}
|
}
|
||||||
|
@ -73,7 +74,7 @@ class Facet implements FacetInterface
|
||||||
*
|
*
|
||||||
* @return array<FacetOptionInterface>
|
* @return array<FacetOptionInterface>
|
||||||
*/
|
*/
|
||||||
public function getOptions()
|
public function getOptions() : array
|
||||||
{
|
{
|
||||||
$this->initOptions();
|
$this->initOptions();
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,11 @@ class FacetOption implements FacetOptionInterface
|
||||||
*/
|
*/
|
||||||
protected $name = '';
|
protected $name = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $displayName = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
|
@ -40,21 +45,21 @@ class FacetOption implements FacetOptionInterface
|
||||||
public function __construct(array $bucket)
|
public function __construct(array $bucket)
|
||||||
{
|
{
|
||||||
$this->name = $bucket['key'];
|
$this->name = $bucket['key'];
|
||||||
|
$this->displayName = isset($bucket['key_as_string']) ? $bucket['key_as_string'] : $this->getName();
|
||||||
$this->count = $bucket['doc_count'];
|
$this->count = $bucket['doc_count'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getName() : string
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getName()
|
|
||||||
{
|
{
|
||||||
return $this->name;
|
return $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getDisplayName() : string
|
||||||
* @return int
|
{
|
||||||
*/
|
return $this->displayName;
|
||||||
public function getCount()
|
}
|
||||||
|
|
||||||
|
public function getCount() : int
|
||||||
{
|
{
|
||||||
return $this->count;
|
return $this->count;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,10 +24,14 @@ use Codappix\SearchCore\Connection\FacetInterface;
|
||||||
use Codappix\SearchCore\Connection\ResultItemInterface;
|
use Codappix\SearchCore\Connection\ResultItemInterface;
|
||||||
use Codappix\SearchCore\Connection\SearchRequestInterface;
|
use Codappix\SearchCore\Connection\SearchRequestInterface;
|
||||||
use Codappix\SearchCore\Connection\SearchResultInterface;
|
use Codappix\SearchCore\Connection\SearchResultInterface;
|
||||||
|
use Codappix\SearchCore\Domain\Model\QueryResultInterfaceStub;
|
||||||
|
use Codappix\SearchCore\Domain\Model\ResultItem;
|
||||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||||
|
|
||||||
class SearchResult implements SearchResultInterface
|
class SearchResult implements SearchResultInterface
|
||||||
{
|
{
|
||||||
|
use QueryResultInterfaceStub;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var SearchRequestInterface
|
* @var SearchRequestInterface
|
||||||
*/
|
*/
|
||||||
|
@ -104,7 +108,7 @@ class SearchResult implements SearchResultInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->result->getResults() as $result) {
|
foreach ($this->result->getResults() as $result) {
|
||||||
$this->results[] = new ResultItem($result);
|
$this->results[] = new ResultItem($result->getData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,41 +157,8 @@ class SearchResult implements SearchResultInterface
|
||||||
$this->position = 0;
|
$this->position = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extbase QueryResultInterface - Implemented to support Pagination of Fluid.
|
|
||||||
|
|
||||||
public function getQuery()
|
public function getQuery()
|
||||||
{
|
{
|
||||||
return $this->searchRequest;
|
return $this->searchRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFirst()
|
|
||||||
{
|
|
||||||
throw new \BadMethodCallException('Method is not implemented yet.', 1502195121);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toArray()
|
|
||||||
{
|
|
||||||
throw new \BadMethodCallException('Method is not implemented yet.', 1502195135);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsetExists($offset)
|
|
||||||
{
|
|
||||||
// Return false to allow Fluid to use appropriate getter methods.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsetGet($offset)
|
|
||||||
{
|
|
||||||
throw new \BadMethodCallException('Use getter to fetch properties.', 1502196933);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsetSet($offset, $value)
|
|
||||||
{
|
|
||||||
throw new \BadMethodCallException('You are not allowed to modify the result.', 1502196934);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsetUnset($offset)
|
|
||||||
{
|
|
||||||
throw new \BadMethodCallException('You are not allowed to modify the result.', 1502196936);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,15 +28,17 @@ interface FacetOptionInterface
|
||||||
/**
|
/**
|
||||||
* Returns the name of this option. Equivalent
|
* Returns the name of this option. Equivalent
|
||||||
* to value used for filtering.
|
* to value used for filtering.
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function getName();
|
public function getName() : string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a pre-rendered name is provided, this will be returned.
|
||||||
|
* Otherwise it's the same as getName().
|
||||||
|
*/
|
||||||
|
public function getDisplayName() : string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of found results for this option.
|
* Returns the number of found results for this option.
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
*/
|
||||||
public function getCount();
|
public function getCount() : int;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,15 +28,11 @@ interface FacetRequestInterface
|
||||||
/**
|
/**
|
||||||
* The identifier of the facet, used as key in arrays and to get the facet
|
* The identifier of the facet, used as key in arrays and to get the facet
|
||||||
* from search request, etc.
|
* from search request, etc.
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function getIdentifier();
|
public function getIdentifier() : string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The field to use for facet building.
|
* The config to use for facet building.
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function getField();
|
public function getConfig() : array;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,5 +25,12 @@ namespace Codappix\SearchCore\Connection;
|
||||||
*/
|
*/
|
||||||
interface ResultItemInterface extends \ArrayAccess
|
interface ResultItemInterface extends \ArrayAccess
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Returns every information as array.
|
||||||
|
*
|
||||||
|
* Provide key/column/field => data.
|
||||||
|
*
|
||||||
|
* Used e.g. for dataprocessing.
|
||||||
|
*/
|
||||||
|
public function getPlainData() : array;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ namespace Codappix\SearchCore\Connection;
|
||||||
* 02110-1301, USA.
|
* 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Codappix\SearchCore\Domain\Search\SearchService;
|
||||||
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
|
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
|
||||||
|
|
||||||
interface SearchRequestInterface extends QueryInterface
|
interface SearchRequestInterface extends QueryInterface
|
||||||
|
@ -40,4 +41,10 @@ interface SearchRequestInterface extends QueryInterface
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getFilter();
|
public function getFilter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Workaround for paginate widget support which will
|
||||||
|
* use the request to build another search.
|
||||||
|
*/
|
||||||
|
public function setSearchService(SearchService $searchService);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
<?php
|
||||||
|
namespace Codappix\SearchCore\DataProcessing;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
use TYPO3\CMS\Extbase\Service\TypoScriptService;
|
||||||
|
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes an existing TYPO3 DataProcessor on the given data.
|
||||||
|
*/
|
||||||
|
class ContentObjectDataProcessorAdapterProcessor implements ProcessorInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var TypoScriptService
|
||||||
|
*/
|
||||||
|
protected $typoScriptService;
|
||||||
|
|
||||||
|
public function __construct(TypoScriptService $typoScriptService)
|
||||||
|
{
|
||||||
|
$this->typoScriptService = $typoScriptService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processData(array $data, array $configuration) : array
|
||||||
|
{
|
||||||
|
$dataProcessor = GeneralUtility::makeInstance($configuration['_dataProcessor']);
|
||||||
|
$contentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class);
|
||||||
|
|
||||||
|
$contentObjectRenderer->data = $data;
|
||||||
|
if (isset($configuration['_table'])) {
|
||||||
|
$contentObjectRenderer->start($data, $configuration['_table']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dataProcessor->process(
|
||||||
|
$contentObjectRenderer,
|
||||||
|
[],
|
||||||
|
$this->typoScriptService->convertPlainArrayToTypoScriptArray($configuration),
|
||||||
|
$data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ namespace Codappix\SearchCore\DataProcessing;
|
||||||
*/
|
*/
|
||||||
class CopyToProcessor implements ProcessorInterface
|
class CopyToProcessor implements ProcessorInterface
|
||||||
{
|
{
|
||||||
public function processRecord(array $record, array $configuration) : array
|
public function processData(array $record, array $configuration) : array
|
||||||
{
|
{
|
||||||
$all = [];
|
$all = [];
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace Codappix\SearchCore\DataProcessing;
|
||||||
*/
|
*/
|
||||||
class GeoPointProcessor implements ProcessorInterface
|
class GeoPointProcessor implements ProcessorInterface
|
||||||
{
|
{
|
||||||
public function processRecord(array $record, array $configuration) : array
|
public function processData(array $record, array $configuration) : array
|
||||||
{
|
{
|
||||||
if (! $this->canApply($record, $configuration)) {
|
if (! $this->canApply($record, $configuration)) {
|
||||||
return $record;
|
return $record;
|
||||||
|
|
|
@ -21,14 +21,13 @@ namespace Codappix\SearchCore\DataProcessing;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All DataProcessing Processors should implement this interface, otherwise
|
* All DataProcessing Processors should implement this interface.
|
||||||
* they will not be executed.
|
|
||||||
*/
|
*/
|
||||||
interface ProcessorInterface
|
interface ProcessorInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Processes the given record.
|
* Processes the given data.
|
||||||
* Also retrieves the configuration for this processor instance.
|
* Also retrieves the configuration for this processor instance.
|
||||||
*/
|
*/
|
||||||
public function processRecord(array $record, array $configuration) : array;
|
public function processData(array $record, array $configuration) : array;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
*/
|
*/
|
||||||
class RemoveProcessor implements ProcessorInterface
|
class RemoveProcessor implements ProcessorInterface
|
||||||
{
|
{
|
||||||
public function processRecord(array $record, array $configuration) : array
|
public function processData(array $record, array $configuration) : array
|
||||||
{
|
{
|
||||||
if (!isset($configuration['fields'])) {
|
if (!isset($configuration['fields'])) {
|
||||||
return $record;
|
return $record;
|
||||||
|
|
56
Classes/DataProcessing/Service.php
Normal file
56
Classes/DataProcessing/Service.php
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
namespace Codappix\SearchCore\DataProcessing;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eases work with data processing.
|
||||||
|
*/
|
||||||
|
class Service
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ObjectManagerInterface
|
||||||
|
*/
|
||||||
|
protected $objectManager;
|
||||||
|
|
||||||
|
public function __construct(ObjectManagerInterface $objectManager)
|
||||||
|
{
|
||||||
|
$this->objectManager = $objectManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the dataprocessor depending on configuration and returns the result.
|
||||||
|
*
|
||||||
|
* @param array|string $configuration Either the full configuration or only the class name.
|
||||||
|
*/
|
||||||
|
public function executeDataProcessor($configuration, array $data) : array
|
||||||
|
{
|
||||||
|
if (is_string($configuration)) {
|
||||||
|
$configuration = [
|
||||||
|
'_typoScriptNodeValue' => $configuration,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->objectManager->get($configuration['_typoScriptNodeValue'])
|
||||||
|
->processData($data, $configuration);
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,6 +43,12 @@ abstract class AbstractIndexer implements IndexerInterface
|
||||||
*/
|
*/
|
||||||
protected $identifier = '';
|
protected $identifier = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Codappix\SearchCore\DataProcessing\Service
|
||||||
|
* @inject
|
||||||
|
*/
|
||||||
|
protected $dataProcessorService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \TYPO3\CMS\Core\Log\Logger
|
* @var \TYPO3\CMS\Core\Log\Logger
|
||||||
*/
|
*/
|
||||||
|
@ -135,18 +141,7 @@ abstract class AbstractIndexer implements IndexerInterface
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
foreach ($this->configuration->get('indexing.' . $this->identifier . '.dataProcessing') as $configuration) {
|
foreach ($this->configuration->get('indexing.' . $this->identifier . '.dataProcessing') as $configuration) {
|
||||||
$className = '';
|
$record = $this->dataProcessorService->executeDataProcessor($configuration, $record);
|
||||||
if (is_string($configuration)) {
|
|
||||||
$className = $configuration;
|
|
||||||
$configuration = [];
|
|
||||||
} else {
|
|
||||||
$className = $configuration['_typoScriptNodeValue'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$dataProcessor = GeneralUtility::makeInstance($className);
|
|
||||||
if ($dataProcessor instanceof ProcessorInterface) {
|
|
||||||
$record = $dataProcessor->processRecord($record, $configuration);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (InvalidArgumentException $e) {
|
} catch (InvalidArgumentException $e) {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
|
|
|
@ -167,7 +167,9 @@ class TcaTableService
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
$userDefinedWhere = $this->configuration->getIfExists('indexing.' . $this->getTableName() . '.additionalWhereClause');
|
$userDefinedWhere = $this->configuration->getIfExists(
|
||||||
|
'indexing.' . $this->getTableName() . '.additionalWhereClause'
|
||||||
|
);
|
||||||
if (is_string($userDefinedWhere)) {
|
if (is_string($userDefinedWhere)) {
|
||||||
$whereClause .= ' AND ' . $userDefinedWhere;
|
$whereClause .= ' AND ' . $userDefinedWhere;
|
||||||
}
|
}
|
||||||
|
@ -348,6 +350,9 @@ class TcaTableService
|
||||||
*/
|
*/
|
||||||
protected function getBlackListedRootLine() : array
|
protected function getBlackListedRootLine() : array
|
||||||
{
|
{
|
||||||
return GeneralUtility::intExplode(',', $this->configuration->getIfExists('indexing.' . $this->getTableName() . '.rootLineBlacklist'));
|
return GeneralUtility::intExplode(
|
||||||
|
',',
|
||||||
|
$this->configuration->getIfExists('indexing.' . $this->getTableName() . '.rootLineBlacklist')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,37 +30,27 @@ class FacetRequest implements FacetRequestInterface
|
||||||
protected $identifier = '';
|
protected $identifier = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $field = '';
|
protected $config = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Add validation / exception?
|
|
||||||
* As the facets come from configuration this might be a good idea to help
|
* As the facets come from configuration this might be a good idea to help
|
||||||
* integrators find issues.
|
* integrators find issues.
|
||||||
*
|
|
||||||
* @param string $identifier
|
|
||||||
* @param string $field
|
|
||||||
*/
|
*/
|
||||||
public function __construct($identifier, $field)
|
public function __construct(string $identifier, array $config)
|
||||||
{
|
{
|
||||||
$this->identifier = $identifier;
|
$this->identifier = $identifier;
|
||||||
$this->field = $field;
|
$this->config = $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getIdentifier() : string
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getIdentifier()
|
|
||||||
{
|
{
|
||||||
return $this->identifier;
|
return $this->identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getConfig() : array
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getField()
|
|
||||||
{
|
{
|
||||||
return $this->field;
|
return $this->config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
61
Classes/Domain/Model/QueryResultInterfaceStub.php
Normal file
61
Classes/Domain/Model/QueryResultInterfaceStub.php
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
namespace Codappix\SearchCore\Domain\Model;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As we have to stay compatible with QueryResultInterface
|
||||||
|
* of extbase but can and need not to provide all methods,
|
||||||
|
* this stub will provde the non implemented methods to
|
||||||
|
* keep real implementations clean.
|
||||||
|
*/
|
||||||
|
trait QueryResultInterfaceStub
|
||||||
|
{
|
||||||
|
public function getFirst()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502195121);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502195135);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetExists($offset)
|
||||||
|
{
|
||||||
|
// Return false to allow Fluid to use appropriate getter methods.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetGet($offset)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Use getter to fetch properties.', 1502196933);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetSet($offset, $value)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('You are not allowed to modify the result.', 1502196934);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetUnset($offset)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('You are not allowed to modify the result.', 1502196936);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Codappix\SearchCore\Connection\Elasticsearch;
|
namespace Codappix\SearchCore\Domain\Model;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 Daniel Siepmann <coding@daniel-siepmann.de>
|
* Copyright (C) 2017 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||||
|
@ -29,9 +29,14 @@ class ResultItem implements ResultItemInterface
|
||||||
*/
|
*/
|
||||||
protected $data = [];
|
protected $data = [];
|
||||||
|
|
||||||
public function __construct(\Elastica\Result $result)
|
public function __construct(array $result)
|
||||||
{
|
{
|
||||||
$this->data = $result->getData();
|
$this->data = $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlainData() : array
|
||||||
|
{
|
||||||
|
return $this->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function offsetExists($offset)
|
public function offsetExists($offset)
|
|
@ -23,6 +23,7 @@ namespace Codappix\SearchCore\Domain\Model;
|
||||||
use Codappix\SearchCore\Connection\ConnectionInterface;
|
use Codappix\SearchCore\Connection\ConnectionInterface;
|
||||||
use Codappix\SearchCore\Connection\FacetRequestInterface;
|
use Codappix\SearchCore\Connection\FacetRequestInterface;
|
||||||
use Codappix\SearchCore\Connection\SearchRequestInterface;
|
use Codappix\SearchCore\Connection\SearchRequestInterface;
|
||||||
|
use Codappix\SearchCore\Domain\Search\SearchService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a search request used to process an actual search.
|
* Represents a search request used to process an actual search.
|
||||||
|
@ -63,6 +64,11 @@ class SearchRequest implements SearchRequestInterface
|
||||||
*/
|
*/
|
||||||
protected $connection = null;
|
protected $connection = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SearchService
|
||||||
|
*/
|
||||||
|
protected $searchService = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $query
|
* @param string $query
|
||||||
*/
|
*/
|
||||||
|
@ -143,19 +149,30 @@ class SearchRequest implements SearchRequestInterface
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setSearchService(SearchService $searchService)
|
||||||
|
{
|
||||||
|
$this->searchService = $searchService;
|
||||||
|
}
|
||||||
|
|
||||||
// Extbase QueryInterface
|
// Extbase QueryInterface
|
||||||
// Current implementation covers only paginate widget support.
|
// Current implementation covers only paginate widget support.
|
||||||
public function execute($returnRawQueryResult = false)
|
public function execute($returnRawQueryResult = false)
|
||||||
{
|
{
|
||||||
if ($this->connection instanceof ConnectionInterface) {
|
if (! ($this->connection instanceof ConnectionInterface)) {
|
||||||
return $this->connection->search($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \InvalidArgumentException(
|
throw new \InvalidArgumentException(
|
||||||
'Connection was not set before, therefore execute can not work. Use `setConnection` before.',
|
'Connection was not set before, therefore execute can not work. Use `setConnection` before.',
|
||||||
1502197732
|
1502197732
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (! ($this->searchService instanceof SearchService)) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'SearchService was not set before, therefore execute can not work. Use `setSearchService` before.',
|
||||||
|
1520325175
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->searchService->processResult($this->connection->search($this));
|
||||||
|
}
|
||||||
|
|
||||||
public function setLimit($limit)
|
public function setLimit($limit)
|
||||||
{
|
{
|
||||||
|
|
129
Classes/Domain/Model/SearchResult.php
Normal file
129
Classes/Domain/Model/SearchResult.php
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
<?php
|
||||||
|
namespace Codappix\SearchCore\Domain\Model;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Codappix\SearchCore\Connection\ResultItemInterface;
|
||||||
|
use Codappix\SearchCore\Connection\SearchResultInterface;
|
||||||
|
use Codappix\SearchCore\Domain\Model\QueryResultInterfaceStub;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic model for mapping a concrete search result from a connection.
|
||||||
|
*/
|
||||||
|
class SearchResult implements SearchResultInterface
|
||||||
|
{
|
||||||
|
use QueryResultInterfaceStub;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SearchResultInterface
|
||||||
|
*/
|
||||||
|
protected $originalSearchResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $resultItems = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $results = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For Iterator interface.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $position = 0;
|
||||||
|
|
||||||
|
public function __construct(SearchResultInterface $originalSearchResult, array $resultItems)
|
||||||
|
{
|
||||||
|
$this->originalSearchResult = $originalSearchResult;
|
||||||
|
$this->resultItems = $resultItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<ResultItemInterface>
|
||||||
|
*/
|
||||||
|
public function getResults()
|
||||||
|
{
|
||||||
|
$this->initResults();
|
||||||
|
|
||||||
|
return $this->results;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initResults()
|
||||||
|
{
|
||||||
|
if ($this->results !== []) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->resultItems as $item) {
|
||||||
|
$this->results[] = new ResultItem($item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFacets()
|
||||||
|
{
|
||||||
|
return $this->originalSearchResult->getFacets();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCurrentCount()
|
||||||
|
{
|
||||||
|
return $this->originalSearchResult->getCurrentCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function count()
|
||||||
|
{
|
||||||
|
return $this->originalSearchResult->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function current()
|
||||||
|
{
|
||||||
|
return $this->getResults()[$this->position];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function next()
|
||||||
|
{
|
||||||
|
++$this->position;
|
||||||
|
|
||||||
|
return $this->current();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function key()
|
||||||
|
{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function valid()
|
||||||
|
{
|
||||||
|
return isset($this->getResults()[$this->position]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rewind()
|
||||||
|
{
|
||||||
|
$this->position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQuery()
|
||||||
|
{
|
||||||
|
return $this->originalSearchResult->getQuery();
|
||||||
|
}
|
||||||
|
}
|
|
@ -120,6 +120,10 @@ class QueryFactory
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (trim($searchRequest->getSearchTerm()) === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$boostQueryParts = [];
|
$boostQueryParts = [];
|
||||||
|
|
||||||
foreach ($fields as $fieldName => $boostValue) {
|
foreach ($fields as $fieldName => $boostValue) {
|
||||||
|
@ -162,7 +166,11 @@ class QueryFactory
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
|
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
|
||||||
'stored_fields' => GeneralUtility::trimExplode(',', $this->configuration->get('searching.fields.stored_fields'), true),
|
'stored_fields' => GeneralUtility::trimExplode(
|
||||||
|
',',
|
||||||
|
$this->configuration->get('searching.fields.stored_fields'),
|
||||||
|
true
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
} catch (InvalidArgumentException $e) {
|
} catch (InvalidArgumentException $e) {
|
||||||
// Nothing configured
|
// Nothing configured
|
||||||
|
@ -170,7 +178,10 @@ class QueryFactory
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$scriptFields = $this->configuration->get('searching.fields.script_fields');
|
$scriptFields = $this->configuration->get('searching.fields.script_fields');
|
||||||
$scriptFields = $this->configurationUtility->replaceArrayValuesWithRequestContent($searchRequest, $scriptFields);
|
$scriptFields = $this->configurationUtility->replaceArrayValuesWithRequestContent(
|
||||||
|
$searchRequest,
|
||||||
|
$scriptFields
|
||||||
|
);
|
||||||
$scriptFields = $this->configurationUtility->filterByCondition($scriptFields);
|
$scriptFields = $this->configurationUtility->filterByCondition($scriptFields);
|
||||||
if ($scriptFields !== []) {
|
if ($scriptFields !== []) {
|
||||||
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, ['script_fields' => $scriptFields]);
|
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, ['script_fields' => $scriptFields]);
|
||||||
|
@ -232,6 +243,18 @@ class QueryFactory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($config['raw'])) {
|
||||||
|
$filter = array_merge($config['raw'], $filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($config['type'] === 'range') {
|
||||||
|
return [
|
||||||
|
'range' => [
|
||||||
|
$config['field'] => $filter,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
return [$config['field'] => $filter];
|
return [$config['field'] => $filter];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,11 +263,7 @@ class QueryFactory
|
||||||
foreach ($searchRequest->getFacets() as $facet) {
|
foreach ($searchRequest->getFacets() as $facet) {
|
||||||
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
|
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
|
||||||
'aggs' => [
|
'aggs' => [
|
||||||
$facet->getIdentifier() => [
|
$facet->getIdentifier() => $facet->getConfig(),
|
||||||
'terms' => [
|
|
||||||
'field' => $facet->getField(),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,9 @@ use Codappix\SearchCore\Configuration\InvalidArgumentException;
|
||||||
use Codappix\SearchCore\Connection\ConnectionInterface;
|
use Codappix\SearchCore\Connection\ConnectionInterface;
|
||||||
use Codappix\SearchCore\Connection\SearchRequestInterface;
|
use Codappix\SearchCore\Connection\SearchRequestInterface;
|
||||||
use Codappix\SearchCore\Connection\SearchResultInterface;
|
use Codappix\SearchCore\Connection\SearchResultInterface;
|
||||||
|
use Codappix\SearchCore\DataProcessing\Service as DataProcessorService;
|
||||||
use Codappix\SearchCore\Domain\Model\FacetRequest;
|
use Codappix\SearchCore\Domain\Model\FacetRequest;
|
||||||
|
use Codappix\SearchCore\Domain\Model\SearchResult;
|
||||||
use TYPO3\CMS\Core\Utility\ArrayUtility;
|
use TYPO3\CMS\Core\Utility\ArrayUtility;
|
||||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||||
|
|
||||||
|
@ -49,19 +51,27 @@ class SearchService
|
||||||
*/
|
*/
|
||||||
protected $objectManager;
|
protected $objectManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DataProcessorService
|
||||||
|
*/
|
||||||
|
protected $dataProcessorService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ConnectionInterface $connection
|
* @param ConnectionInterface $connection
|
||||||
* @param ConfigurationContainerInterface $configuration
|
* @param ConfigurationContainerInterface $configuration
|
||||||
* @param ObjectManagerInterface $objectManager
|
* @param ObjectManagerInterface $objectManager
|
||||||
|
* @param DataProcessorService $dataProcessorService
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ConnectionInterface $connection,
|
ConnectionInterface $connection,
|
||||||
ConfigurationContainerInterface $configuration,
|
ConfigurationContainerInterface $configuration,
|
||||||
ObjectManagerInterface $objectManager
|
ObjectManagerInterface $objectManager,
|
||||||
|
DataProcessorService $dataProcessorService
|
||||||
) {
|
) {
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
$this->configuration = $configuration;
|
$this->configuration = $configuration;
|
||||||
$this->objectManager = $objectManager;
|
$this->objectManager = $objectManager;
|
||||||
|
$this->dataProcessorService = $dataProcessorService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,12 +80,15 @@ class SearchService
|
||||||
*/
|
*/
|
||||||
public function search(SearchRequestInterface $searchRequest)
|
public function search(SearchRequestInterface $searchRequest)
|
||||||
{
|
{
|
||||||
$searchRequest->setConnection($this->connection);
|
|
||||||
$this->addSize($searchRequest);
|
$this->addSize($searchRequest);
|
||||||
$this->addConfiguredFacets($searchRequest);
|
$this->addConfiguredFacets($searchRequest);
|
||||||
$this->addConfiguredFilters($searchRequest);
|
$this->addConfiguredFilters($searchRequest);
|
||||||
|
|
||||||
return $this->connection->search($searchRequest);
|
// Add connection to request to enable paginate widget support
|
||||||
|
$searchRequest->setConnection($this->connection);
|
||||||
|
$searchRequest->setSearchService($this);
|
||||||
|
|
||||||
|
return $this->processResult($this->connection->search($searchRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,15 +116,10 @@ class SearchService
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($facetsConfig as $identifier => $facetConfig) {
|
foreach ($facetsConfig as $identifier => $facetConfig) {
|
||||||
if (!isset($facetConfig['field']) || trim($facetConfig['field']) === '') {
|
|
||||||
// TODO: Finish throw
|
|
||||||
throw new \Exception('message', 1499171142);
|
|
||||||
}
|
|
||||||
|
|
||||||
$searchRequest->addFacet($this->objectManager->get(
|
$searchRequest->addFacet($this->objectManager->get(
|
||||||
FacetRequest::class,
|
FacetRequest::class,
|
||||||
$identifier,
|
$identifier,
|
||||||
$facetConfig['field']
|
$facetConfig
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,4 +146,30 @@ class SearchService
|
||||||
// Nothing todo, no filter configured.
|
// Nothing todo, no filter configured.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the result, e.g. applies configured data processing to result.
|
||||||
|
*/
|
||||||
|
public function processResult(SearchResultInterface $searchResult) : SearchResultInterface
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$newSearchResultItems = [];
|
||||||
|
foreach ($this->configuration->get('searching.dataProcessing') as $configuration) {
|
||||||
|
foreach ($searchResult as $resultItem) {
|
||||||
|
$newSearchResultItems[] = $this->dataProcessorService->executeDataProcessor(
|
||||||
|
$configuration,
|
||||||
|
$resultItem->getPlainData()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->objectManager->get(
|
||||||
|
SearchResult::class,
|
||||||
|
$searchResult,
|
||||||
|
$newSearchResultItems
|
||||||
|
);
|
||||||
|
} catch (InvalidArgumentException $e) {
|
||||||
|
return $searchResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,16 +82,6 @@ class DataHandler implements Singleton
|
||||||
$this->indexerFactory = $indexerFactory;
|
$this->indexerFactory = $indexerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $table
|
|
||||||
* @param array $record
|
|
||||||
*/
|
|
||||||
public function add($table, array $record)
|
|
||||||
{
|
|
||||||
$this->logger->debug('Record received for add.', [$table, $record]);
|
|
||||||
$this->getIndexer($table)->indexDocument($record['uid']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $table
|
* @param string $table
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -91,42 +91,36 @@ class DataHandler implements Singleton
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function processDatamap_afterAllOperations(CoreDataHandler $dataHandler)
|
||||||
* Called by CoreDataHandler on database operations, e.g. if new records were created or records were updated.
|
{
|
||||||
*
|
foreach ($dataHandler->datamap as $table => $record) {
|
||||||
* @param string $status
|
$uid = key($record);
|
||||||
* @param string $table
|
$fieldData = current($record);
|
||||||
* @param string|int $uid
|
|
||||||
* @param array $fieldArray
|
if (isset($fieldArray['uid'])) {
|
||||||
* @param CoreDataHandler $dataHandler
|
$uid = $fieldArray['uid'];
|
||||||
*
|
} elseif (isset($dataHandler->substNEWwithIDs[$uid])) {
|
||||||
* @return bool False if hook was not processed.
|
$uid = $dataHandler->substNEWwithIDs[$uid];
|
||||||
*/
|
}
|
||||||
public function processDatamap_afterDatabaseOperations($status, $table, $uid, array $fieldArray, CoreDataHandler $dataHandler)
|
|
||||||
|
$this->processRecord($table, $uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processRecord(string $table, int $uid) : bool
|
||||||
{
|
{
|
||||||
if (! $this->shouldProcessHookForTable($table)) {
|
if (! $this->shouldProcessHookForTable($table)) {
|
||||||
$this->logger->debug('Database update not processed.', [$table, $uid]);
|
$this->logger->debug('Indexing of record not processed.', [$table, $uid]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($status === 'new') {
|
|
||||||
$fieldArray['uid'] = $dataHandler->substNEWwithIDs[$uid];
|
|
||||||
$this->dataHandler->add($table, $fieldArray);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($status === 'update') {
|
|
||||||
$record = $this->getRecord($table, $uid);
|
$record = $this->getRecord($table, $uid);
|
||||||
if ($record !== null) {
|
if ($record !== null) {
|
||||||
$this->dataHandler->update($table, $record);
|
$this->dataHandler->update($table, $record);
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->logger->debug(
|
$this->logger->debug('Indexing of record not processed, as he was not found in Database.', [$table, $uid]);
|
||||||
'Database update not processed, cause status is unhandled.',
|
|
||||||
[$status, $table, $uid, $fieldArray]
|
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
8
Documentation/source/changelog.rst
Normal file
8
Documentation/source/changelog.rst
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
Changelog
|
||||||
|
=========
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
:glob:
|
||||||
|
|
||||||
|
changelog/*
|
|
@ -0,0 +1,40 @@
|
||||||
|
Breacking Change 120 "Pass facets configuration to elasticsearch"
|
||||||
|
=================================================================
|
||||||
|
|
||||||
|
In order to allow arbitrary facet configuration, we do not process the facet configuration anymore.
|
||||||
|
Instead integrators are able to configure facets for search service "as is". We just pipe the
|
||||||
|
configuration through.
|
||||||
|
|
||||||
|
Therefore the following, which worked before, does not work anymore:
|
||||||
|
|
||||||
|
.. code-block:: typoscript
|
||||||
|
:linenos:
|
||||||
|
:emphasize-lines: 4
|
||||||
|
|
||||||
|
plugin.tx_searchcore.settings.search {
|
||||||
|
facets {
|
||||||
|
category {
|
||||||
|
field = categories
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Instead you have to provide the full configuration yourself:
|
||||||
|
|
||||||
|
.. code-block:: typoscript
|
||||||
|
:linenos:
|
||||||
|
:emphasize-lines: 4,6
|
||||||
|
|
||||||
|
plugin.tx_searchcore.settings.search {
|
||||||
|
facets {
|
||||||
|
category {
|
||||||
|
terms {
|
||||||
|
field = categories
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
You need to add line 4 and 6, the additional level ``terms`` for elasticsearch.
|
||||||
|
|
||||||
|
See :issue:`120`.
|
|
@ -36,5 +36,7 @@ DataProcessing
|
||||||
|
|
||||||
Before data is transfered to search service, it can be processed by "DataProcessors" like already
|
Before data is transfered to search service, it can be processed by "DataProcessors" like already
|
||||||
known by :ref:`t3tsref:cobj-fluidtemplate-properties-dataprocessing` of :ref:`t3tsref:cobj-fluidtemplate`.
|
known by :ref:`t3tsref:cobj-fluidtemplate-properties-dataprocessing` of :ref:`t3tsref:cobj-fluidtemplate`.
|
||||||
|
The same is true for retrieved search results. They can be processed again by "DataProcessors" to
|
||||||
|
prepare data for display in Templates or further usage.
|
||||||
|
|
||||||
Configuration is done through TypoScript, see :ref:`dataProcessing`.
|
Configuration is done through TypoScript, see :ref:`dataProcessing`.
|
||||||
|
|
|
@ -5,9 +5,12 @@
|
||||||
Configuration
|
Configuration
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
Installation wide configuration is handled inside of the extension manager. Just check out the
|
||||||
|
options there, they all have labels.
|
||||||
|
|
||||||
The extension offers the following configuration options through TypoScript. If you overwrite them
|
The extension offers the following configuration options through TypoScript. If you overwrite them
|
||||||
through `setup` make sure to keep them in the `module` area as they will be accessed from backend
|
through `setup` make sure to keep them in the `module` area as they will be accessed from backend
|
||||||
mode of TYPO3. Do so by placing the following line at the end::
|
mode of TYPO3 for indexing. Do so by placing the following line at the end::
|
||||||
|
|
||||||
module.tx_searchcore < plugin.tx_searchcore
|
module.tx_searchcore < plugin.tx_searchcore
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
``Codappix\SearchCore\DataProcessing\ContentObjectDataProcessorAdapterProcessor``
|
||||||
|
=================================================================================
|
||||||
|
|
||||||
|
Will execute an existing TYPO3 data processor.
|
||||||
|
|
||||||
|
Possible Options:
|
||||||
|
|
||||||
|
``_dataProcessor``
|
||||||
|
Necessary, defined which data processor to apply. Provide the same as you would to call the
|
||||||
|
processor.
|
||||||
|
``_table``
|
||||||
|
Defines the "current" table as used by some processors, e.g.
|
||||||
|
``TYPO3\CMS\Frontend\DataProcessing\FilesProcessor``.
|
||||||
|
|
||||||
|
All further options are passed to the configured data processor. Therefore they are documented at
|
||||||
|
each data processor.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
plugin.tx_searchcore.settings.searching.dataProcessing {
|
||||||
|
1 = Codappix\SearchCore\DataProcessing\ContentObjectDataProcessorAdapterProcessor
|
||||||
|
1 {
|
||||||
|
_table = pages
|
||||||
|
_dataProcessor = TYPO3\CMS\Frontend\DataProcessing\FilesProcessor
|
||||||
|
|
||||||
|
references.fieldName = media
|
||||||
|
as = images
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
The above example will create a new field ``images`` with resolved FAL relations from ``media``
|
||||||
|
field.
|
|
@ -0,0 +1,36 @@
|
||||||
|
The following Processor are available:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
:glob:
|
||||||
|
|
||||||
|
/configuration/dataProcessing/ContentObjectDataProcessorAdapterProcessor
|
||||||
|
/configuration/dataProcessing/CopyToProcessor
|
||||||
|
/configuration/dataProcessing/GeoPointProcessor
|
||||||
|
/configuration/dataProcessing/RemoveProcessor
|
||||||
|
|
||||||
|
The following Processor are planned:
|
||||||
|
|
||||||
|
``Codappix\SearchCore\DataProcessing\ReplaceProcessor``
|
||||||
|
Will execute a search and replace on configured fields.
|
||||||
|
|
||||||
|
``Codappix\SearchCore\DataProcessing\RootLevelProcessor``
|
||||||
|
Will attach the root level to the record.
|
||||||
|
|
||||||
|
``Codappix\SearchCore\DataProcessing\ChannelProcessor``
|
||||||
|
Will add a configurable channel to the record, e.g. if you have different areas in your
|
||||||
|
website like "products" and "infos".
|
||||||
|
|
||||||
|
``Codappix\SearchCore\DataProcessing\RelationResolverProcessor``
|
||||||
|
Resolves all relations using the TCA.
|
||||||
|
|
||||||
|
Of course you are able to provide further processors. Just implement
|
||||||
|
``Codappix\SearchCore\DataProcessing\ProcessorInterface`` and use the FQCN (=Fully qualified
|
||||||
|
class name) as done in the examples above.
|
||||||
|
|
||||||
|
By implementing also the same interface as necessary for TYPO3
|
||||||
|
:ref:`t3tsref:cobj-fluidtemplate-properties-dataprocessing`, you are able to reuse the same code
|
||||||
|
also for Fluid to prepare the same record fetched from DB for your fluid.
|
||||||
|
|
||||||
|
Dependency injection is possible inside of processors, as we instantiate through extbase
|
||||||
|
``ObjectManager``.
|
|
@ -146,7 +146,7 @@ Example::
|
||||||
dataProcessing
|
dataProcessing
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Used by: All connections while indexing.
|
Used by: All connections while indexing, due to implementation inside ``AbstractIndexer``.
|
||||||
|
|
||||||
Configure modifications on each document before sending it to the configured connection. Same as
|
Configure modifications on each document before sending it to the configured connection. Same as
|
||||||
provided by TYPO3 for :ref:`t3tsref:cobj-fluidtemplate` through
|
provided by TYPO3 for :ref:`t3tsref:cobj-fluidtemplate` through
|
||||||
|
@ -170,38 +170,7 @@ Example::
|
||||||
|
|
||||||
The above example will copy all existing fields to the field ``search_spellcheck``. Afterwards
|
The above example will copy all existing fields to the field ``search_spellcheck``. Afterwards
|
||||||
all fields, including ``search_spellcheck`` will be copied to ``search_all``.
|
all fields, including ``search_spellcheck`` will be copied to ``search_all``.
|
||||||
E.g. used to index all information into a field for :ref:`spellchecking` or searching with
|
|
||||||
different :ref:`mapping`.
|
|
||||||
|
|
||||||
The following Processor are available:
|
.. include:: /configuration/dataProcessing/availableAndPlanned.rst
|
||||||
|
|
||||||
.. toctree::
|
Also data processors are available for search results too, see :ref:`searching_dataProcessing`.
|
||||||
:maxdepth: 1
|
|
||||||
:glob:
|
|
||||||
|
|
||||||
dataProcessing/CopyToProcessor
|
|
||||||
dataProcessing/RemoveProcessor
|
|
||||||
dataProcessing/GeoPointProcessor
|
|
||||||
|
|
||||||
The following Processor are planned:
|
|
||||||
|
|
||||||
``Codappix\SearchCore\DataProcessing\ReplaceProcessor``
|
|
||||||
Will execute a search and replace on configured fields.
|
|
||||||
|
|
||||||
``Codappix\SearchCore\DataProcessing\RootLevelProcessor``
|
|
||||||
Will attach the root level to the record.
|
|
||||||
|
|
||||||
``Codappix\SearchCore\DataProcessing\ChannelProcessor``
|
|
||||||
Will add a configurable channel to the record, e.g. if you have different areas in your
|
|
||||||
website like "products" and "infos".
|
|
||||||
|
|
||||||
``Codappix\SearchCore\DataProcessing\RelationResolverProcessor``
|
|
||||||
Resolves all relations using the TCA.
|
|
||||||
|
|
||||||
Of course you are able to provide further processors. Just implement
|
|
||||||
``Codappix\SearchCore\DataProcessing\ProcessorInterface`` and use the FQCN (=Fully qualified
|
|
||||||
class name) as done in the examples above.
|
|
||||||
|
|
||||||
By implementing also the same interface as necessary for TYPO3
|
|
||||||
:ref:`t3tsref:cobj-fluidtemplate-properties-dataprocessing`, you are able to reuse the same code
|
|
||||||
also for Fluid to prepare the same record fetched from DB for your fluid.
|
|
||||||
|
|
|
@ -119,20 +119,48 @@ E.g. you submit a filter in form of:
|
||||||
|
|
||||||
.. code-block:: html
|
.. code-block:: html
|
||||||
|
|
||||||
<f:form.textfield property="filter.distance.location.lat" value="51.168098" />
|
<f:comment>
|
||||||
<f:form.textfield property="filter.distance.location.lon" value="6.381384" />
|
Due to TYPO3 7.x fluid limitations, we build this input ourself.
|
||||||
<f:form.textfield property="filter.distance.distance" value="100km" />
|
No longer necessary in 8 and above
|
||||||
|
</f:comment>
|
||||||
|
<select name="tx_searchcore_search[searchRequest][filter][month][from]" class="_control" >
|
||||||
|
<option value="">Month</option>
|
||||||
|
<f:for each="{searchResult.facets.month.options}" as="month">
|
||||||
|
<f:if condition="{month.count}">
|
||||||
|
<option
|
||||||
|
value="{month.displayName -> f:format.date(format: 'Y-m')}"
|
||||||
|
{f:if(condition: '{searchRequest.filter.month.from} == {month.displayName -> f:format.date(format: \'Y-m\')}', then: 'selected="true"')}
|
||||||
|
>{month.displayName -> f:format.date(format: '%B %Y')}</option>
|
||||||
|
</f:if>
|
||||||
|
</f:for>
|
||||||
|
</select>
|
||||||
|
<select name="tx_searchcore_search[searchRequest][filter][month][to]" class="_control" >
|
||||||
|
<option value="">Month</option>
|
||||||
|
<f:for each="{searchResult.facets.month.options}" as="month">
|
||||||
|
<f:if condition="{month.count}">
|
||||||
|
<option
|
||||||
|
value="{month.displayName -> f:format.date(format: 'Y-m')}"
|
||||||
|
{f:if(condition: '{searchRequest.filter.month.from} == {month.displayName -> f:format.date(format: \'Y-m\')}', then: 'selected="true"')}
|
||||||
|
>{month.displayName -> f:format.date(format: '%B %Y')}</option>
|
||||||
|
</f:if>
|
||||||
|
</f:for>
|
||||||
|
</select>
|
||||||
|
|
||||||
This will create a ``distance`` filter with subproperties. To make this filter actually work, you
|
This will create a ``distance`` filter with subproperties. To make this filter actually work, you
|
||||||
can add the following TypoScript, which will be added to the filter::
|
can add the following TypoScript, which will be added to the filter::
|
||||||
|
|
||||||
mapping {
|
mapping {
|
||||||
filter {
|
filter {
|
||||||
distance {
|
month {
|
||||||
field = geo_distance
|
type = range
|
||||||
|
field = released
|
||||||
|
raw {
|
||||||
|
format = yyyy-MM
|
||||||
|
}
|
||||||
|
|
||||||
fields {
|
fields {
|
||||||
distance = distance
|
gte = from
|
||||||
location = location
|
lte = to
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,9 +171,12 @@ in elasticsearch. In above example they do match, but you can also use different
|
||||||
On the left hand side is the elasticsearch field name, on the right side the one submitted as a
|
On the left hand side is the elasticsearch field name, on the right side the one submitted as a
|
||||||
filter.
|
filter.
|
||||||
|
|
||||||
The ``field``, in above example ``geo_distance``, will be used as the elasticsearch field for
|
The ``field``, in above example ``released``, will be used as the elasticsearch field for
|
||||||
filtering. This way you can use arbitrary filter names and map them to existing elasticsearch fields.
|
filtering. This way you can use arbitrary filter names and map them to existing elasticsearch fields.
|
||||||
|
|
||||||
|
Everything that is configured inside ``raw`` is passed, as is, to search service, e.g.
|
||||||
|
elasticsearch.
|
||||||
|
|
||||||
.. _fields:
|
.. _fields:
|
||||||
|
|
||||||
fields
|
fields
|
||||||
|
@ -216,3 +247,37 @@ Example::
|
||||||
}
|
}
|
||||||
|
|
||||||
Only ``filter`` is allowed as value. Will submit an empty query to switch to filter mode.
|
Only ``filter`` is allowed as value. Will submit an empty query to switch to filter mode.
|
||||||
|
|
||||||
|
.. _searching_dataProcessing:
|
||||||
|
|
||||||
|
dataProcessing
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Used by: All connections while indexing, due to implementation inside ``SearchService``.
|
||||||
|
|
||||||
|
Configure modifications on each document before returning search result. Same as provided by TYPO3
|
||||||
|
for :ref:`t3tsref:cobj-fluidtemplate` through
|
||||||
|
:ref:`t3tsref:cobj-fluidtemplate-properties-dataprocessing`.
|
||||||
|
|
||||||
|
All processors are applied in configured order. Allowing to work with already processed data.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
plugin.tx_searchcore.settings.searching.dataProcessing {
|
||||||
|
1 = Codappix\SearchCore\DataProcessing\CopyToProcessor
|
||||||
|
1 {
|
||||||
|
to = search_spellcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
2 = Codappix\SearchCore\DataProcessing\CopyToProcessor
|
||||||
|
2 {
|
||||||
|
to = search_all
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
The above example will copy all existing fields to the field ``search_spellcheck``. Afterwards
|
||||||
|
all fields, including ``search_spellcheck`` will be copied to ``search_all``.
|
||||||
|
|
||||||
|
.. include:: /configuration/dataProcessing/availableAndPlanned.rst
|
||||||
|
|
||||||
|
Also data processors are available while indexing too, see :ref:`dataProcessing`.
|
||||||
|
|
|
@ -29,6 +29,16 @@ Also multiple filter are supported. Filtering results by fields for string conte
|
||||||
Facets / aggregates are also possible. Therefore a mapping has to be defined in TypoScript for
|
Facets / aggregates are also possible. Therefore a mapping has to be defined in TypoScript for
|
||||||
indexing, and the facets itself while searching.
|
indexing, and the facets itself while searching.
|
||||||
|
|
||||||
|
.. _features_dataProcessing:
|
||||||
|
|
||||||
|
DataProcessing
|
||||||
|
==============
|
||||||
|
|
||||||
|
DataProcessing, as known from ``FLUIDTEMPLATE`` is available while indexing and for search results.
|
||||||
|
Each item can be processed by multiple processor to prepare data for indexing and output.
|
||||||
|
|
||||||
|
See :ref:`concepts_indexing_dataprocessing` in :ref:`concepts` section.
|
||||||
|
|
||||||
.. _features_planned:
|
.. _features_planned:
|
||||||
|
|
||||||
Planned
|
Planned
|
||||||
|
|
|
@ -15,3 +15,4 @@ Table of Contents
|
||||||
connections
|
connections
|
||||||
indexer
|
indexer
|
||||||
development
|
development
|
||||||
|
changelog
|
||||||
|
|
|
@ -19,4 +19,8 @@ In that case you need to install all dependencies yourself. Dependencies are:
|
||||||
Afterwards you need to enable the extension through the extension manager and include the static
|
Afterwards you need to enable the extension through the extension manager and include the static
|
||||||
TypoScript setup.
|
TypoScript setup.
|
||||||
|
|
||||||
|
If you **don't** want to use the included elasticsearch integration, you have to disable it in the
|
||||||
|
extension manager configuration of the extension by checking the checkbox.
|
||||||
|
It's currently enabled by default but will be moved into its own extension in the future.
|
||||||
|
|
||||||
.. _downloading: https://github.com/DanielSiepmann/search_core/archive/master.zip
|
.. _downloading: https://github.com/DanielSiepmann/search_core/archive/master.zip
|
||||||
|
|
3
Makefile
3
Makefile
|
@ -15,6 +15,9 @@ install: clean
|
||||||
COMPOSER_PROCESS_TIMEOUT=1000 composer require -vv --dev --prefer-source typo3/cms="$(TYPO3_VERSION)"
|
COMPOSER_PROCESS_TIMEOUT=1000 composer require -vv --dev --prefer-source typo3/cms="$(TYPO3_VERSION)"
|
||||||
git checkout composer.json
|
git checkout composer.json
|
||||||
|
|
||||||
|
cgl:
|
||||||
|
./.Build/bin/phpcs
|
||||||
|
|
||||||
functionalTests:
|
functionalTests:
|
||||||
typo3DatabaseName=$(typo3DatabaseName) \
|
typo3DatabaseName=$(typo3DatabaseName) \
|
||||||
typo3DatabaseUsername=$(typo3DatabaseUsername) \
|
typo3DatabaseUsername=$(typo3DatabaseUsername) \
|
||||||
|
|
78
Tests/Functional/Connection/Elasticsearch/FacetTest.php
Normal file
78
Tests/Functional/Connection/Elasticsearch/FacetTest.php
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<?php
|
||||||
|
namespace Codappix\SearchCore\Tests\Functional\Connection\Elasticsearch;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Codappix\SearchCore\Domain\Index\IndexerFactory;
|
||||||
|
use Codappix\SearchCore\Domain\Model\SearchRequest;
|
||||||
|
use Codappix\SearchCore\Domain\Search\SearchService;
|
||||||
|
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||||
|
|
||||||
|
class FacetTest extends AbstractFunctionalTestCase
|
||||||
|
{
|
||||||
|
protected function getTypoScriptFilesForFrontendRootPage()
|
||||||
|
{
|
||||||
|
return array_merge(
|
||||||
|
parent::getTypoScriptFilesForFrontendRootPage(),
|
||||||
|
['EXT:search_core/Tests/Functional/Fixtures/Searching/Facet.ts']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getDataSets()
|
||||||
|
{
|
||||||
|
return array_merge(
|
||||||
|
parent::getDataSets(),
|
||||||
|
['Tests/Functional/Fixtures/Searching/Filter.xml']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function itsPossibleToFetchFacetsForField()
|
||||||
|
{
|
||||||
|
\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(ObjectManager::class)
|
||||||
|
->get(IndexerFactory::class)
|
||||||
|
->getIndexer('tt_content')
|
||||||
|
->indexAllDocuments()
|
||||||
|
;
|
||||||
|
|
||||||
|
$searchService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(ObjectManager::class)
|
||||||
|
->get(SearchService::class);
|
||||||
|
|
||||||
|
$searchRequest = new SearchRequest();
|
||||||
|
$result = $searchService->search($searchRequest);
|
||||||
|
|
||||||
|
$this->assertSame(1, count($result->getFacets()), 'Did not receive the single defined facet.');
|
||||||
|
|
||||||
|
$facet = current($result->getFacets());
|
||||||
|
$this->assertSame('contentTypes', $facet->getName(), 'Name of facet was not as expected.');
|
||||||
|
$this->assertSame('CType', $facet->getField(), 'Field of facet was not expected.');
|
||||||
|
|
||||||
|
$options = $facet->getOptions();
|
||||||
|
$this->assertSame(2, count($options), 'Did not receive the expected number of possible options for facet.');
|
||||||
|
$option = $options['HTML'];
|
||||||
|
$this->assertSame('HTML', $option->getName(), 'Option did not have expected Name.');
|
||||||
|
$this->assertSame(1, $option->getCount(), 'Option did not have expected count.');
|
||||||
|
$option = $options['Header'];
|
||||||
|
$this->assertSame('Header', $option->getName(), 'Option did not have expected Name.');
|
||||||
|
$this->assertSame(1, $option->getCount(), 'Option did not have expected count.');
|
||||||
|
}
|
||||||
|
}
|
|
@ -58,37 +58,4 @@ class FilterTest extends AbstractFunctionalTestCase
|
||||||
$this->assertSame(5, (int) $result->getResults()[0]['uid'], 'Did not get the expected result entry.');
|
$this->assertSame(5, (int) $result->getResults()[0]['uid'], 'Did not get the expected result entry.');
|
||||||
$this->assertSame(1, count($result), 'Did not receive the single filtered element.');
|
$this->assertSame(1, count($result), 'Did not receive the single filtered element.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @test
|
|
||||||
*/
|
|
||||||
public function itsPossibleToFetchFacetsForField()
|
|
||||||
{
|
|
||||||
\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(ObjectManager::class)
|
|
||||||
->get(IndexerFactory::class)
|
|
||||||
->getIndexer('tt_content')
|
|
||||||
->indexAllDocuments()
|
|
||||||
;
|
|
||||||
|
|
||||||
$searchService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(ObjectManager::class)
|
|
||||||
->get(SearchService::class);
|
|
||||||
|
|
||||||
$searchRequest = new SearchRequest();
|
|
||||||
$result = $searchService->search($searchRequest);
|
|
||||||
|
|
||||||
$this->assertSame(1, count($result->getFacets()), 'Did not receive the single defined facet.');
|
|
||||||
|
|
||||||
$facet = current($result->getFacets());
|
|
||||||
$this->assertSame('contentTypes', $facet->getName(), 'Name of facet was not as expected.');
|
|
||||||
$this->assertSame('CType', $facet->getField(), 'Field of facet was not expected.');
|
|
||||||
|
|
||||||
$options = $facet->getOptions();
|
|
||||||
$this->assertSame(2, count($options), 'Did not receive the expected number of possible options for facet.');
|
|
||||||
$option = $options['HTML'];
|
|
||||||
$this->assertSame('HTML', $option->getName(), 'Option did not have expected Name.');
|
|
||||||
$this->assertSame(1, $option->getCount(), 'Option did not have expected count.');
|
|
||||||
$option = $options['Header'];
|
|
||||||
$this->assertSame('Header', $option->getName(), 'Option did not have expected Name.');
|
|
||||||
$this->assertSame(1, $option->getCount(), 'Option did not have expected count.');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
namespace Codappix\SearchCore\Tests\Functional\DataProcessing;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Codappix\SearchCore\DataProcessing\ContentObjectDataProcessorAdapterProcessor;
|
||||||
|
use Codappix\SearchCore\Tests\Functional\AbstractFunctionalTestCase;
|
||||||
|
use TYPO3\CMS\Extbase\Service\TypoScriptService;
|
||||||
|
use TYPO3\CMS\Frontend\DataProcessing\SplitProcessor;
|
||||||
|
|
||||||
|
class ContentObjectDataProcessorAdapterProcessorTest extends AbstractFunctionalTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function contentObjectDataProcessorIsExecuted()
|
||||||
|
{
|
||||||
|
$record = ['content' => 'value1, value2'];
|
||||||
|
$configuration = [
|
||||||
|
'_dataProcessor' => SplitProcessor::class,
|
||||||
|
'delimiter' => ',',
|
||||||
|
'fieldName' => 'content',
|
||||||
|
'as' => 'new_content',
|
||||||
|
];
|
||||||
|
$expectedData = [
|
||||||
|
'content' => 'value1, value2',
|
||||||
|
'new_content' => ['value1', 'value2'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$subject = new ContentObjectDataProcessorAdapterProcessor(new TypoScriptService);
|
||||||
|
$processedData = $subject->processData($record, $configuration);
|
||||||
|
$this->assertSame(
|
||||||
|
$expectedData,
|
||||||
|
$processedData,
|
||||||
|
'The processor did not return the expected processed record.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,13 +37,6 @@ plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
searching {
|
searching {
|
||||||
fields = search_title
|
|
||||||
facets {
|
|
||||||
contentTypes {
|
|
||||||
field = CType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fields {
|
fields {
|
||||||
query = _all
|
query = _all
|
||||||
}
|
}
|
||||||
|
|
17
Tests/Functional/Fixtures/Searching/Facet.ts
Normal file
17
Tests/Functional/Fixtures/Searching/Facet.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
plugin {
|
||||||
|
tx_searchcore {
|
||||||
|
settings {
|
||||||
|
searching {
|
||||||
|
facets {
|
||||||
|
contentTypes {
|
||||||
|
terms {
|
||||||
|
field = CType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.tx_searchcore < plugin.tx_searchcore
|
|
@ -1,54 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace Codappix\SearchCore\Tests\Functional\Hooks\DataHandler;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
||||||
* 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
|
|
||||||
use Codappix\SearchCore\Domain\Service\DataHandler as DataHandlerService;
|
|
||||||
use Codappix\SearchCore\Hook\DataHandler as DataHandlerHook;
|
|
||||||
use TYPO3\CMS\Core\DataHandling\DataHandler as Typo3DataHandler;
|
|
||||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
|
||||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
|
||||||
|
|
||||||
class IgnoresUnkownOperationTest extends AbstractDataHandlerTest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var DataHandlerService|\PHPUnit_Framework_MockObject_MockObject|AccessibleObjectInterface
|
|
||||||
*/
|
|
||||||
protected $subject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @test
|
|
||||||
*/
|
|
||||||
public function dataHandlerCommandSomethingIsIgnored()
|
|
||||||
{
|
|
||||||
$subject = new DataHandlerHook($this->subject);
|
|
||||||
$this->assertFalse(
|
|
||||||
$subject->processDatamap_afterDatabaseOperations(
|
|
||||||
'something',
|
|
||||||
'tt_content',
|
|
||||||
1,
|
|
||||||
[],
|
|
||||||
new Typo3DataHandler
|
|
||||||
),
|
|
||||||
'Hook processed status "something".'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -64,7 +64,7 @@ class NonAllowedTablesTest extends AbstractDataHandlerTest
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
*/
|
*/
|
||||||
public function updateWillNotBeTriggeredForSysCategory()
|
public function updateWillNotBeTriggeredForExistingSysCategory()
|
||||||
{
|
{
|
||||||
$this->subject->expects($this->exactly(0))->method('update');
|
$this->subject->expects($this->exactly(0))->method('update');
|
||||||
|
|
||||||
|
@ -83,9 +83,9 @@ class NonAllowedTablesTest extends AbstractDataHandlerTest
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
*/
|
*/
|
||||||
public function addWillNotBeTriggeredForSysCategoy()
|
public function updateWillNotBeTriggeredForNewSysCategoy()
|
||||||
{
|
{
|
||||||
$this->subject->expects($this->exactly(0))->method('add');
|
$this->subject->expects($this->exactly(0))->method('update');
|
||||||
|
|
||||||
$tce = GeneralUtility::makeInstance(Typo3DataHandler::class);
|
$tce = GeneralUtility::makeInstance(Typo3DataHandler::class);
|
||||||
$tce->stripslashes_values = 0;
|
$tce->stripslashes_values = 0;
|
||||||
|
|
|
@ -66,7 +66,7 @@ class ProcessesAllowedTablesTest extends AbstractDataHandlerTest
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
*/
|
*/
|
||||||
public function updateWillBeTriggeredForTtContent()
|
public function updateWillBeTriggeredForExistingTtContent()
|
||||||
{
|
{
|
||||||
$this->subject->expects($this->exactly(1))->method('update')
|
$this->subject->expects($this->exactly(1))->method('update')
|
||||||
->with(
|
->with(
|
||||||
|
@ -94,9 +94,9 @@ class ProcessesAllowedTablesTest extends AbstractDataHandlerTest
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
*/
|
*/
|
||||||
public function addWillBeTriggeredForTtContent()
|
public function updateWillBeTriggeredForNewTtContent()
|
||||||
{
|
{
|
||||||
$this->subject->expects($this->exactly(1))->method('add')
|
$this->subject->expects($this->exactly(1))->method('update')
|
||||||
->with(
|
->with(
|
||||||
$this->equalTo('tt_content'),
|
$this->equalTo('tt_content'),
|
||||||
$this->callback(function ($record) {
|
$this->callback(function ($record) {
|
||||||
|
|
|
@ -31,8 +31,11 @@ class ConfigurationUtilityTest extends AbstractUnitTestCase
|
||||||
* @test
|
* @test
|
||||||
* @dataProvider possibleRequestAndConfigurationForFluidtemplate
|
* @dataProvider possibleRequestAndConfigurationForFluidtemplate
|
||||||
*/
|
*/
|
||||||
public function recursiveEntriesAreProcessedAsFluidtemplate(SearchRequestInterface $searchRequest, array $array, array $expected)
|
public function recursiveEntriesAreProcessedAsFluidtemplate(
|
||||||
{
|
SearchRequestInterface $searchRequest,
|
||||||
|
array $array,
|
||||||
|
array $expected
|
||||||
|
) {
|
||||||
$subject = new ConfigurationUtility();
|
$subject = new ConfigurationUtility();
|
||||||
|
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
|
|
64
Tests/Unit/Connection/Elasticsearch/FacetOptionTest.php
Normal file
64
Tests/Unit/Connection/Elasticsearch/FacetOptionTest.php
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
namespace Codappix\SearchCore\Tests\Unit\Connection\Elasticsearch;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Codappix\SearchCore\Connection\Elasticsearch\FacetOption;
|
||||||
|
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
|
||||||
|
|
||||||
|
class FacetOptionTest extends AbstractUnitTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function displayNameIsReturnedAsExpected()
|
||||||
|
{
|
||||||
|
$bucket = [
|
||||||
|
'key' => 'Name',
|
||||||
|
'key_as_string' => 'DisplayName',
|
||||||
|
'doc_count' => 10,
|
||||||
|
];
|
||||||
|
$subject = new FacetOption($bucket);
|
||||||
|
|
||||||
|
$this->assertSame(
|
||||||
|
$bucket['key_as_string'],
|
||||||
|
$subject->getDisplayName(),
|
||||||
|
'Display name was not returned as expected.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function displayNameIsReturnedAsExpectedIfNotProvided()
|
||||||
|
{
|
||||||
|
$bucket = [
|
||||||
|
'key' => 'Name',
|
||||||
|
'doc_count' => 10,
|
||||||
|
];
|
||||||
|
$subject = new FacetOption($bucket);
|
||||||
|
|
||||||
|
$this->assertSame(
|
||||||
|
$bucket['key'],
|
||||||
|
$subject->getDisplayName(),
|
||||||
|
'Display name was not returned as expected.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,15 +27,15 @@ class CopyToProcessorTest extends AbstractUnitTestCase
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @dataProvider getPossibleRecordConfigurationCombinations
|
* @dataProvider getPossibleDataConfigurationCombinations
|
||||||
*/
|
*/
|
||||||
public function fieldsAreCopiedAsConfigured(array $record, array $configuration, array $expectedRecord)
|
public function fieldsAreCopiedAsConfigured(array $record, array $configuration, array $expectedData)
|
||||||
{
|
{
|
||||||
$subject = new CopyToProcessor();
|
$subject = new CopyToProcessor();
|
||||||
$processedRecord = $subject->processRecord($record, $configuration);
|
$processedData = $subject->processData($record, $configuration);
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
$expectedRecord,
|
$expectedData,
|
||||||
$processedRecord,
|
$processedData,
|
||||||
'The processor did not return the expected processed record.'
|
'The processor did not return the expected processed record.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ class CopyToProcessorTest extends AbstractUnitTestCase
|
||||||
/**
|
/**
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getPossibleRecordConfigurationCombinations()
|
public function getPossibleDataConfigurationCombinations()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'Copy all fields to new field' => [
|
'Copy all fields to new field' => [
|
||||||
|
@ -54,7 +54,7 @@ class CopyToProcessorTest extends AbstractUnitTestCase
|
||||||
'configuration' => [
|
'configuration' => [
|
||||||
'to' => 'new_field',
|
'to' => 'new_field',
|
||||||
],
|
],
|
||||||
'expectedRecord' => [
|
'expectedData' => [
|
||||||
'field 1' => 'Some content like lorem',
|
'field 1' => 'Some content like lorem',
|
||||||
'field 2' => 'Some more content like ipsum',
|
'field 2' => 'Some more content like ipsum',
|
||||||
'new_field' => 'Some content like lorem' . PHP_EOL . 'Some more content like ipsum',
|
'new_field' => 'Some content like lorem' . PHP_EOL . 'Some more content like ipsum',
|
||||||
|
@ -71,7 +71,7 @@ class CopyToProcessorTest extends AbstractUnitTestCase
|
||||||
'configuration' => [
|
'configuration' => [
|
||||||
'to' => 'new_field',
|
'to' => 'new_field',
|
||||||
],
|
],
|
||||||
'expectedRecord' => [
|
'expectedData' => [
|
||||||
'field 1' => 'Some content like lorem',
|
'field 1' => 'Some content like lorem',
|
||||||
'field with sub2' => [
|
'field with sub2' => [
|
||||||
'Tag 1',
|
'Tag 1',
|
||||||
|
|
|
@ -27,15 +27,15 @@ class GeoPointProcessorTest extends AbstractUnitTestCase
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @dataProvider getPossibleRecordConfigurationCombinations
|
* @dataProvider getPossibleDataConfigurationCombinations
|
||||||
*/
|
*/
|
||||||
public function geoPointsAreAddedAsConfigured(array $record, array $configuration, array $expectedRecord)
|
public function geoPointsAreAddedAsConfigured(array $record, array $configuration, array $expectedData)
|
||||||
{
|
{
|
||||||
$subject = new GeoPointProcessor();
|
$subject = new GeoPointProcessor();
|
||||||
$processedRecord = $subject->processRecord($record, $configuration);
|
$processedData = $subject->processData($record, $configuration);
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
$expectedRecord,
|
$expectedData,
|
||||||
$processedRecord,
|
$processedData,
|
||||||
'The processor did not return the expected processed record.'
|
'The processor did not return the expected processed record.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ class GeoPointProcessorTest extends AbstractUnitTestCase
|
||||||
/**
|
/**
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getPossibleRecordConfigurationCombinations()
|
public function getPossibleDataConfigurationCombinations()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'Create new field with existing lat and lng' => [
|
'Create new field with existing lat and lng' => [
|
||||||
|
@ -56,7 +56,7 @@ class GeoPointProcessorTest extends AbstractUnitTestCase
|
||||||
'lat' => 'lat',
|
'lat' => 'lat',
|
||||||
'lon' => 'lng',
|
'lon' => 'lng',
|
||||||
],
|
],
|
||||||
'expectedRecord' => [
|
'expectedData' => [
|
||||||
'lat' => 23.232,
|
'lat' => 23.232,
|
||||||
'lng' => 45.43,
|
'lng' => 45.43,
|
||||||
'location' => [
|
'location' => [
|
||||||
|
@ -73,7 +73,7 @@ class GeoPointProcessorTest extends AbstractUnitTestCase
|
||||||
'configuration' => [
|
'configuration' => [
|
||||||
'to' => 'location',
|
'to' => 'location',
|
||||||
],
|
],
|
||||||
'expectedRecord' => [
|
'expectedData' => [
|
||||||
'lat' => 23.232,
|
'lat' => 23.232,
|
||||||
'lng' => 45.43,
|
'lng' => 45.43,
|
||||||
],
|
],
|
||||||
|
@ -88,7 +88,7 @@ class GeoPointProcessorTest extends AbstractUnitTestCase
|
||||||
'lat' => 'lat',
|
'lat' => 'lat',
|
||||||
'lon' => 'lng',
|
'lon' => 'lng',
|
||||||
],
|
],
|
||||||
'expectedRecord' => [
|
'expectedData' => [
|
||||||
'lat' => '',
|
'lat' => '',
|
||||||
'lng' => '',
|
'lng' => '',
|
||||||
],
|
],
|
||||||
|
@ -103,7 +103,7 @@ class GeoPointProcessorTest extends AbstractUnitTestCase
|
||||||
'lat' => 'lat',
|
'lat' => 'lat',
|
||||||
'lon' => 'lng',
|
'lon' => 'lng',
|
||||||
],
|
],
|
||||||
'expectedRecord' => [
|
'expectedData' => [
|
||||||
'lat' => 'av',
|
'lat' => 'av',
|
||||||
'lng' => 'dsf',
|
'lng' => 'dsf',
|
||||||
],
|
],
|
||||||
|
|
|
@ -27,15 +27,15 @@ class RemoveProcessorTest extends AbstractUnitTestCase
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @dataProvider getPossibleRecordConfigurationCombinations
|
* @dataProvider getPossibleDataConfigurationCombinations
|
||||||
*/
|
*/
|
||||||
public function fieldsAreCopiedAsConfigured(array $record, array $configuration, array $expectedRecord)
|
public function fieldsAreCopiedAsConfigured(array $record, array $configuration, array $expectedData)
|
||||||
{
|
{
|
||||||
$subject = new RemoveProcessor();
|
$subject = new RemoveProcessor();
|
||||||
$processedRecord = $subject->processRecord($record, $configuration);
|
$processedData = $subject->processData($record, $configuration);
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
$expectedRecord,
|
$expectedData,
|
||||||
$processedRecord,
|
$processedData,
|
||||||
'The processor did not return the expected processed record.'
|
'The processor did not return the expected processed record.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ class RemoveProcessorTest extends AbstractUnitTestCase
|
||||||
/**
|
/**
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getPossibleRecordConfigurationCombinations()
|
public function getPossibleDataConfigurationCombinations()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'Nothing configured' => [
|
'Nothing configured' => [
|
||||||
|
@ -56,7 +56,7 @@ class RemoveProcessorTest extends AbstractUnitTestCase
|
||||||
],
|
],
|
||||||
'configuration' => [
|
'configuration' => [
|
||||||
],
|
],
|
||||||
'expectedRecord' => [
|
'expectedData' => [
|
||||||
'field 1' => 'Some content like lorem',
|
'field 1' => 'Some content like lorem',
|
||||||
'field with sub2' => [
|
'field with sub2' => [
|
||||||
'Tag 1',
|
'Tag 1',
|
||||||
|
@ -76,7 +76,7 @@ class RemoveProcessorTest extends AbstractUnitTestCase
|
||||||
'fields' => 'field with sub2',
|
'fields' => 'field with sub2',
|
||||||
'_typoScriptNodeValue' => 'Codappix\SearchCore\DataProcessing\RemoveProcessor',
|
'_typoScriptNodeValue' => 'Codappix\SearchCore\DataProcessing\RemoveProcessor',
|
||||||
],
|
],
|
||||||
'expectedRecord' => [
|
'expectedData' => [
|
||||||
'field 1' => 'Some content like lorem',
|
'field 1' => 'Some content like lorem',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -92,7 +92,7 @@ class RemoveProcessorTest extends AbstractUnitTestCase
|
||||||
'fields' => 'non existing',
|
'fields' => 'non existing',
|
||||||
'_typoScriptNodeValue' => 'Codappix\SearchCore\DataProcessing\RemoveProcessor',
|
'_typoScriptNodeValue' => 'Codappix\SearchCore\DataProcessing\RemoveProcessor',
|
||||||
],
|
],
|
||||||
'expectedRecord' => [
|
'expectedData' => [
|
||||||
'field 1' => 'Some content like lorem',
|
'field 1' => 'Some content like lorem',
|
||||||
'field with sub2' => [
|
'field with sub2' => [
|
||||||
'Tag 1',
|
'Tag 1',
|
||||||
|
@ -113,7 +113,7 @@ class RemoveProcessorTest extends AbstractUnitTestCase
|
||||||
'fields' => 'field 3, field with sub2',
|
'fields' => 'field 3, field with sub2',
|
||||||
'_typoScriptNodeValue' => 'Codappix\SearchCore\DataProcessing\RemoveProcessor',
|
'_typoScriptNodeValue' => 'Codappix\SearchCore\DataProcessing\RemoveProcessor',
|
||||||
],
|
],
|
||||||
'expectedRecord' => [
|
'expectedData' => [
|
||||||
'field 1' => 'Some content like lorem',
|
'field 1' => 'Some content like lorem',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -125,7 +125,7 @@ class RemoveProcessorTest extends AbstractUnitTestCase
|
||||||
'fields' => 'field 1',
|
'fields' => 'field 1',
|
||||||
'_typoScriptNodeValue' => 'Codappix\SearchCore\DataProcessing\RemoveProcessor',
|
'_typoScriptNodeValue' => 'Codappix\SearchCore\DataProcessing\RemoveProcessor',
|
||||||
],
|
],
|
||||||
'expectedRecord' => [
|
'expectedData' => [
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
|
@ -24,6 +24,7 @@ use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||||
use Codappix\SearchCore\Configuration\InvalidArgumentException;
|
use Codappix\SearchCore\Configuration\InvalidArgumentException;
|
||||||
use Codappix\SearchCore\Connection\ConnectionInterface;
|
use Codappix\SearchCore\Connection\ConnectionInterface;
|
||||||
use Codappix\SearchCore\DataProcessing\CopyToProcessor;
|
use Codappix\SearchCore\DataProcessing\CopyToProcessor;
|
||||||
|
use Codappix\SearchCore\DataProcessing\Service as DataProcessorService;
|
||||||
use Codappix\SearchCore\Domain\Index\AbstractIndexer;
|
use Codappix\SearchCore\Domain\Index\AbstractIndexer;
|
||||||
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
|
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
|
||||||
|
|
||||||
|
@ -44,17 +45,26 @@ class AbstractIndexerTest extends AbstractUnitTestCase
|
||||||
*/
|
*/
|
||||||
protected $connection;
|
protected $connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DataProcessorService
|
||||||
|
*/
|
||||||
|
protected $dataProcessorService;
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->configuration = $this->getMockBuilder(ConfigurationContainerInterface::class)->getMock();
|
$this->configuration = $this->getMockBuilder(ConfigurationContainerInterface::class)->getMock();
|
||||||
$this->connection = $this->getMockBuilder(ConnectionInterface::class)->getMock();
|
$this->connection = $this->getMockBuilder(ConnectionInterface::class)->getMock();
|
||||||
|
$this->dataProcessorService = $this->getMockBuilder(DataProcessorService::class)
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
|
||||||
$this->subject = $this->getMockForAbstractClass(AbstractIndexer::class, [
|
$this->subject = $this->getMockForAbstractClass(AbstractIndexer::class, [
|
||||||
$this->connection,
|
$this->connection,
|
||||||
$this->configuration
|
$this->configuration
|
||||||
]);
|
]);
|
||||||
|
$this->inject($this->subject, 'dataProcessorService', $this->dataProcessorService);
|
||||||
$this->subject->injectLogger($this->getMockedLogger());
|
$this->subject->injectLogger($this->getMockedLogger());
|
||||||
$this->subject->setIdentifier('testTable');
|
$this->subject->setIdentifier('testTable');
|
||||||
$this->subject->expects($this->any())
|
$this->subject->expects($this->any())
|
||||||
|
@ -73,7 +83,30 @@ class AbstractIndexerTest extends AbstractUnitTestCase
|
||||||
$expectedRecord['new_test_field2'] = 'test' . PHP_EOL . 'test';
|
$expectedRecord['new_test_field2'] = 'test' . PHP_EOL . 'test';
|
||||||
$expectedRecord['search_abstract'] = '';
|
$expectedRecord['search_abstract'] = '';
|
||||||
|
|
||||||
$this->configuration->expects($this->exactly(2))
|
$this->dataProcessorService->expects($this->any())
|
||||||
|
->method('executeDataProcessor')
|
||||||
|
->withConsecutive(
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'_typoScriptNodeValue' => CopyToProcessor::class,
|
||||||
|
'to' => 'new_test_field',
|
||||||
|
],
|
||||||
|
$record,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'_typoScriptNodeValue' => CopyToProcessor::class,
|
||||||
|
'to' => 'new_test_field2',
|
||||||
|
],
|
||||||
|
array_merge($record, ['new_test_field' => 'test']),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->will($this->onConsecutiveCalls(
|
||||||
|
array_merge($record, ['new_test_field' => 'test']),
|
||||||
|
$expectedRecord
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->configuration->expects($this->any())
|
||||||
->method('get')
|
->method('get')
|
||||||
->withConsecutive(['indexing.testTable.dataProcessing'], ['indexing.testTable.abstractFields'])
|
->withConsecutive(['indexing.testTable.dataProcessing'], ['indexing.testTable.abstractFields'])
|
||||||
->will($this->onConsecutiveCalls([
|
->will($this->onConsecutiveCalls([
|
||||||
|
|
110
Tests/Unit/Domain/Model/ResultItemTest.php
Normal file
110
Tests/Unit/Domain/Model/ResultItemTest.php
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
<?php
|
||||||
|
namespace Codappix\SearchCore\Tests\Unit\Domain\Model;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Codappix\SearchCore\Domain\Model\ResultItem;
|
||||||
|
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
|
||||||
|
|
||||||
|
class ResultItemTest extends AbstractUnitTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function plainDataCanBeRetrieved()
|
||||||
|
{
|
||||||
|
$originalData = [
|
||||||
|
'uid' => 10,
|
||||||
|
'title' => 'Some title',
|
||||||
|
];
|
||||||
|
$expectedData = $originalData;
|
||||||
|
|
||||||
|
$subject = new ResultItem($originalData);
|
||||||
|
$this->assertSame(
|
||||||
|
$expectedData,
|
||||||
|
$subject->getPlainData(),
|
||||||
|
'Could not retrieve plain data from result item.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function dataCanBeRetrievedInArrayNotation()
|
||||||
|
{
|
||||||
|
$originalData = [
|
||||||
|
'uid' => 10,
|
||||||
|
'title' => 'Some title',
|
||||||
|
];
|
||||||
|
$expectedData = $originalData;
|
||||||
|
|
||||||
|
$subject = new ResultItem($originalData);
|
||||||
|
$this->assertSame(
|
||||||
|
$originalData['title'],
|
||||||
|
$subject['title'],
|
||||||
|
'Could not retrieve title in array notation.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function existenceOfDataCanBeChecked()
|
||||||
|
{
|
||||||
|
$originalData = [
|
||||||
|
'uid' => 10,
|
||||||
|
'title' => 'Some title',
|
||||||
|
];
|
||||||
|
|
||||||
|
$subject = new ResultItem($originalData);
|
||||||
|
$this->assertTrue(isset($subject['title']), 'Could not determine that title exists.');
|
||||||
|
$this->assertFalse(isset($subject['title2']), 'Could not determine that title2 does not exists.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function dataCanNotBeChanged()
|
||||||
|
{
|
||||||
|
$originalData = [
|
||||||
|
'uid' => 10,
|
||||||
|
'title' => 'Some title',
|
||||||
|
];
|
||||||
|
|
||||||
|
$subject = new ResultItem($originalData);
|
||||||
|
$this->expectException(\BadMethodCallException::class);
|
||||||
|
$subject['title'] = 'New Title';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function dataCanNotBeRemoved()
|
||||||
|
{
|
||||||
|
$originalData = [
|
||||||
|
'uid' => 10,
|
||||||
|
'title' => 'Some title',
|
||||||
|
];
|
||||||
|
|
||||||
|
$subject = new ResultItem($originalData);
|
||||||
|
$this->expectException(\BadMethodCallException::class);
|
||||||
|
unset($subject['title']);
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,10 @@ namespace Codappix\SearchCore\Tests\Unit\Domain\Model;
|
||||||
* 02110-1301, USA.
|
* 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Codappix\SearchCore\Connection\ConnectionInterface;
|
||||||
|
use Codappix\SearchCore\Connection\SearchResultInterface;
|
||||||
use Codappix\SearchCore\Domain\Model\SearchRequest;
|
use Codappix\SearchCore\Domain\Model\SearchRequest;
|
||||||
|
use Codappix\SearchCore\Domain\Search\SearchService;
|
||||||
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
|
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
|
||||||
|
|
||||||
class SearchRequestTest extends AbstractUnitTestCase
|
class SearchRequestTest extends AbstractUnitTestCase
|
||||||
|
@ -31,12 +34,12 @@ class SearchRequestTest extends AbstractUnitTestCase
|
||||||
*/
|
*/
|
||||||
public function emptyFilterWillNotBeSet(array $filter)
|
public function emptyFilterWillNotBeSet(array $filter)
|
||||||
{
|
{
|
||||||
$searchRequest = new SearchRequest();
|
$subject = new SearchRequest();
|
||||||
$searchRequest->setFilter($filter);
|
$subject->setFilter($filter);
|
||||||
|
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
[],
|
[],
|
||||||
$searchRequest->getFilter(),
|
$subject->getFilter(),
|
||||||
'Empty filter were set, even if they should not.'
|
'Empty filter were set, even if they should not.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -68,13 +71,67 @@ class SearchRequestTest extends AbstractUnitTestCase
|
||||||
public function filterIsSet()
|
public function filterIsSet()
|
||||||
{
|
{
|
||||||
$filter = ['someField' => 'someValue'];
|
$filter = ['someField' => 'someValue'];
|
||||||
$searchRequest = new SearchRequest();
|
$subject = new SearchRequest();
|
||||||
$searchRequest->setFilter($filter);
|
$subject->setFilter($filter);
|
||||||
|
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
$filter,
|
$filter,
|
||||||
$searchRequest->getFilter(),
|
$subject->getFilter(),
|
||||||
'Filter was not set.'
|
'Filter was not set.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function exceptionIsThrownIfSearchServiceWasNotSet()
|
||||||
|
{
|
||||||
|
$subject = new SearchRequest();
|
||||||
|
$subject->setConnection($this->getMockBuilder(ConnectionInterface::class)->getMock());
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$subject->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function exceptionIsThrownIfConnectionWasNotSet()
|
||||||
|
{
|
||||||
|
$subject = new SearchRequest();
|
||||||
|
$subject->setSearchService(
|
||||||
|
$this->getMockBuilder(SearchService::class)
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock()
|
||||||
|
);
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$subject->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function executionMakesUseOfProvidedConnectionAndSearchService()
|
||||||
|
{
|
||||||
|
$searchServiceMock = $this->getMockBuilder(SearchService::class)
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
$connectionMock = $this->getMockBuilder(ConnectionInterface::class)
|
||||||
|
->getMock();
|
||||||
|
$searchResultMock = $this->getMockBuilder(SearchResultInterface::class)
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$subject = new SearchRequest();
|
||||||
|
$subject->setSearchService($searchServiceMock);
|
||||||
|
$subject->setConnection($connectionMock);
|
||||||
|
|
||||||
|
$connectionMock->expects($this->once())
|
||||||
|
->method('search')
|
||||||
|
->with($subject)
|
||||||
|
->willReturn($searchResultMock);
|
||||||
|
$searchServiceMock->expects($this->once())
|
||||||
|
->method('processResult')
|
||||||
|
->with($searchResultMock);
|
||||||
|
|
||||||
|
$subject->execute();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
100
Tests/Unit/Domain/Model/SearchResultTest.php
Normal file
100
Tests/Unit/Domain/Model/SearchResultTest.php
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
<?php
|
||||||
|
namespace Codappix\SearchCore\Tests\Unit\Domain\Model;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Codappix\SearchCore\Connection\ResultItemInterface;
|
||||||
|
use Codappix\SearchCore\Connection\SearchResultInterface;
|
||||||
|
use Codappix\SearchCore\Domain\Model\SearchResult;
|
||||||
|
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
|
||||||
|
|
||||||
|
class SearchResultTest extends AbstractUnitTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function countIsRetrievedFromOriginalResult()
|
||||||
|
{
|
||||||
|
$originalSearchResultMock = $this->getMockBuilder(SearchResultInterface::class)->getMock();
|
||||||
|
$originalSearchResultMock->expects($this->once())->method('count');
|
||||||
|
|
||||||
|
$subject = new SearchResult($originalSearchResultMock, []);
|
||||||
|
$subject->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function currentCountIsRetrievedFromOriginalResult()
|
||||||
|
{
|
||||||
|
$originalSearchResultMock = $this->getMockBuilder(SearchResultInterface::class)->getMock();
|
||||||
|
$originalSearchResultMock->expects($this->once())->method('getCurrentCount');
|
||||||
|
|
||||||
|
$subject = new SearchResult($originalSearchResultMock, []);
|
||||||
|
$subject->getCurrentCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function facetsAreRetrievedFromOriginalResult()
|
||||||
|
{
|
||||||
|
$originalSearchResultMock = $this->getMockBuilder(SearchResultInterface::class)->getMock();
|
||||||
|
$originalSearchResultMock->expects($this->once())->method('getFacets');
|
||||||
|
|
||||||
|
$subject = new SearchResult($originalSearchResultMock, []);
|
||||||
|
$subject->getFacets();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function resultItemsCanBeRetrieved()
|
||||||
|
{
|
||||||
|
$originalSearchResultMock = $this->getMockBuilder(SearchResultInterface::class)->getMock();
|
||||||
|
$data = [
|
||||||
|
[
|
||||||
|
'uid' => 10,
|
||||||
|
'title' => 'Some Title',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'uid' => 11,
|
||||||
|
'title' => 'Some Title 2',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'uid' => 12,
|
||||||
|
'title' => 'Some Title 3',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$subject = new SearchResult($originalSearchResultMock, $data);
|
||||||
|
$resultItems = $subject->getResults();
|
||||||
|
|
||||||
|
$this->assertCount(3, $resultItems);
|
||||||
|
|
||||||
|
$this->assertSame($data[0]['uid'], $resultItems[0]['uid']);
|
||||||
|
$this->assertSame($data[1]['uid'], $resultItems[1]['uid']);
|
||||||
|
$this->assertSame($data[2]['uid'], $resultItems[2]['uid']);
|
||||||
|
|
||||||
|
$this->assertInstanceOf(ResultItemInterface::class, $resultItems[0]);
|
||||||
|
$this->assertInstanceOf(ResultItemInterface::class, $resultItems[1]);
|
||||||
|
$this->assertInstanceOf(ResultItemInterface::class, $resultItems[2]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -86,6 +86,58 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function rangeFilterIsAddedToQuery()
|
||||||
|
{
|
||||||
|
$this->configureConfigurationMockWithDefault();
|
||||||
|
$this->configuration->expects($this->any())
|
||||||
|
->method('getIfExists')
|
||||||
|
->will($this->returnCallback(function ($configName) {
|
||||||
|
if ($configName === 'searching.mapping.filter.month') {
|
||||||
|
return [
|
||||||
|
'type' => 'range',
|
||||||
|
'field' => 'released',
|
||||||
|
'raw' => [
|
||||||
|
'format' => 'yyyy-MM',
|
||||||
|
],
|
||||||
|
'fields' => [
|
||||||
|
'gte' => 'from',
|
||||||
|
'lte' => 'to',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}));
|
||||||
|
|
||||||
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
|
$searchRequest->setFilter([
|
||||||
|
'month' => [
|
||||||
|
'from' => '2016-03',
|
||||||
|
'to' => '2017-11',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$query = $this->subject->create($searchRequest);
|
||||||
|
$this->assertSame(
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'range' => [
|
||||||
|
'released' => [
|
||||||
|
'format' => 'yyyy-MM',
|
||||||
|
'gte' => '2016-03',
|
||||||
|
'lte' => '2017-11',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
$query->toArray()['query']['bool']['filter'],
|
||||||
|
'Filter was not added to query.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
*/
|
*/
|
||||||
|
@ -118,8 +170,8 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
{
|
{
|
||||||
$this->configureConfigurationMockWithDefault();
|
$this->configureConfigurationMockWithDefault();
|
||||||
$searchRequest = new SearchRequest('SearchWord');
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
$searchRequest->addFacet(new FacetRequest('Identifier', 'FieldName'));
|
$searchRequest->addFacet(new FacetRequest('Identifier', ['terms' => ['field' => 'FieldName']]));
|
||||||
$searchRequest->addFacet(new FacetRequest('Identifier 2', 'FieldName 2'));
|
$searchRequest->addFacet(new FacetRequest('Identifier 2', ['terms' => ['field' => 'FieldName 2']]));
|
||||||
|
|
||||||
$query = $this->subject->create($searchRequest);
|
$query = $this->subject->create($searchRequest);
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Copyright\SearchCore\Tests\Unit\Domain\Search;
|
namespace Codappix\SearchCore\Tests\Unit\Domain\Search;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 Daniel Siepmann <coding@daniel-siepmann.de>
|
* Copyright (C) 2017 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||||
|
@ -23,7 +23,10 @@ namespace Copyright\SearchCore\Tests\Unit\Domain\Search;
|
||||||
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
|
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||||
use Codappix\SearchCore\Configuration\InvalidArgumentException;
|
use Codappix\SearchCore\Configuration\InvalidArgumentException;
|
||||||
use Codappix\SearchCore\Connection\ConnectionInterface;
|
use Codappix\SearchCore\Connection\ConnectionInterface;
|
||||||
|
use Codappix\SearchCore\Connection\SearchResultInterface;
|
||||||
|
use Codappix\SearchCore\DataProcessing\Service as DataProcessorService;
|
||||||
use Codappix\SearchCore\Domain\Model\SearchRequest;
|
use Codappix\SearchCore\Domain\Model\SearchRequest;
|
||||||
|
use Codappix\SearchCore\Domain\Model\SearchResult;
|
||||||
use Codappix\SearchCore\Domain\Search\SearchService;
|
use Codappix\SearchCore\Domain\Search\SearchService;
|
||||||
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
|
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
|
||||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||||
|
@ -35,10 +38,38 @@ class SearchServiceTest extends AbstractUnitTestCase
|
||||||
*/
|
*/
|
||||||
protected $subject;
|
protected $subject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SearchResultInterface
|
||||||
|
*/
|
||||||
|
protected $result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ConnectionInterface
|
||||||
|
*/
|
||||||
|
protected $connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ConfigurationContainerInterface
|
||||||
|
*/
|
||||||
|
protected $configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ObjectManagerInterface
|
||||||
|
*/
|
||||||
|
protected $objectManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DataProcessorService
|
||||||
|
*/
|
||||||
|
protected $dataProcessorService;
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->result = $this->getMockBuilder(SearchResultInterface::class)
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
$this->connection = $this->getMockBuilder(ConnectionInterface::class)
|
$this->connection = $this->getMockBuilder(ConnectionInterface::class)
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
->getMock();
|
->getMock();
|
||||||
|
@ -48,11 +79,15 @@ class SearchServiceTest extends AbstractUnitTestCase
|
||||||
$this->objectManager = $this->getMockBuilder(ObjectManagerInterface::class)
|
$this->objectManager = $this->getMockBuilder(ObjectManagerInterface::class)
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
->getMock();
|
->getMock();
|
||||||
|
$this->dataProcessorService = $this->getMockBuilder(DataProcessorService::class)
|
||||||
|
->setConstructorArgs([$this->objectManager])
|
||||||
|
->getMock();
|
||||||
|
|
||||||
$this->subject = new SearchService(
|
$this->subject = new SearchService(
|
||||||
$this->connection,
|
$this->connection,
|
||||||
$this->configuration,
|
$this->configuration,
|
||||||
$this->objectManager
|
$this->objectManager,
|
||||||
|
$this->dataProcessorService
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,19 +96,19 @@ class SearchServiceTest extends AbstractUnitTestCase
|
||||||
*/
|
*/
|
||||||
public function sizeIsAddedFromConfiguration()
|
public function sizeIsAddedFromConfiguration()
|
||||||
{
|
{
|
||||||
$this->configuration->expects($this->exactly(2))
|
$this->configuration->expects($this->any())
|
||||||
->method('getIfExists')
|
->method('getIfExists')
|
||||||
->withConsecutive(['searching.size'], ['searching.facets'])
|
->withConsecutive(['searching.size'], ['searching.facets'])
|
||||||
->will($this->onConsecutiveCalls(45, null));
|
->will($this->onConsecutiveCalls(45, null));
|
||||||
$this->configuration->expects($this->exactly(1))
|
$this->configuration->expects($this->any())
|
||||||
->method('get')
|
->method('get')
|
||||||
->with('searching.filter')
|
|
||||||
->will($this->throwException(new InvalidArgumentException));
|
->will($this->throwException(new InvalidArgumentException));
|
||||||
$this->connection->expects($this->once())
|
$this->connection->expects($this->once())
|
||||||
->method('search')
|
->method('search')
|
||||||
->with($this->callback(function ($searchRequest) {
|
->with($this->callback(function ($searchRequest) {
|
||||||
return $searchRequest->getLimit() === 45;
|
return $searchRequest->getLimit() === 45;
|
||||||
}));
|
}))
|
||||||
|
->willReturn($this->getMockBuilder(SearchResultInterface::class)->getMock());
|
||||||
|
|
||||||
$searchRequest = new SearchRequest('SearchWord');
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
$this->subject->search($searchRequest);
|
$this->subject->search($searchRequest);
|
||||||
|
@ -84,19 +119,19 @@ class SearchServiceTest extends AbstractUnitTestCase
|
||||||
*/
|
*/
|
||||||
public function defaultSizeIsAddedIfNothingIsConfigured()
|
public function defaultSizeIsAddedIfNothingIsConfigured()
|
||||||
{
|
{
|
||||||
$this->configuration->expects($this->exactly(2))
|
$this->configuration->expects($this->any())
|
||||||
->method('getIfExists')
|
->method('getIfExists')
|
||||||
->withConsecutive(['searching.size'], ['searching.facets'])
|
->withConsecutive(['searching.size'], ['searching.facets'])
|
||||||
->will($this->onConsecutiveCalls(null, null));
|
->will($this->onConsecutiveCalls(null, null));
|
||||||
$this->configuration->expects($this->exactly(1))
|
$this->configuration->expects($this->any())
|
||||||
->method('get')
|
->method('get')
|
||||||
->with('searching.filter')
|
|
||||||
->will($this->throwException(new InvalidArgumentException));
|
->will($this->throwException(new InvalidArgumentException));
|
||||||
$this->connection->expects($this->once())
|
$this->connection->expects($this->once())
|
||||||
->method('search')
|
->method('search')
|
||||||
->with($this->callback(function ($searchRequest) {
|
->with($this->callback(function ($searchRequest) {
|
||||||
return $searchRequest->getLimit() === 10;
|
return $searchRequest->getLimit() === 10;
|
||||||
}));
|
}))
|
||||||
|
->willReturn($this->getMockBuilder(SearchResultInterface::class)->getMock());
|
||||||
|
|
||||||
$searchRequest = new SearchRequest('SearchWord');
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
$this->subject->search($searchRequest);
|
$this->subject->search($searchRequest);
|
||||||
|
@ -107,20 +142,23 @@ class SearchServiceTest extends AbstractUnitTestCase
|
||||||
*/
|
*/
|
||||||
public function configuredFilterAreAddedToRequestWithoutAnyFilter()
|
public function configuredFilterAreAddedToRequestWithoutAnyFilter()
|
||||||
{
|
{
|
||||||
$this->configuration->expects($this->exactly(2))
|
$this->configuration->expects($this->any())
|
||||||
->method('getIfExists')
|
->method('getIfExists')
|
||||||
->withConsecutive(['searching.size'], ['searching.facets'])
|
->withConsecutive(['searching.size'], ['searching.facets'])
|
||||||
->will($this->onConsecutiveCalls(null, null));
|
->will($this->onConsecutiveCalls(null, null));
|
||||||
$this->configuration->expects($this->exactly(1))
|
$this->configuration->expects($this->any())
|
||||||
->method('get')
|
->method('get')
|
||||||
->with('searching.filter')
|
->will($this->onConsecutiveCalls(
|
||||||
->willReturn(['property' => 'something']);
|
['property' => 'something'],
|
||||||
|
$this->throwException(new InvalidArgumentException)
|
||||||
|
));
|
||||||
|
|
||||||
$this->connection->expects($this->once())
|
$this->connection->expects($this->once())
|
||||||
->method('search')
|
->method('search')
|
||||||
->with($this->callback(function ($searchRequest) {
|
->with($this->callback(function ($searchRequest) {
|
||||||
return $searchRequest->getFilter() === ['property' => 'something'];
|
return $searchRequest->getFilter() === ['property' => 'something'];
|
||||||
}));
|
}))
|
||||||
|
->willReturn($this->getMockBuilder(SearchResultInterface::class)->getMock());
|
||||||
|
|
||||||
$searchRequest = new SearchRequest('SearchWord');
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
$this->subject->search($searchRequest);
|
$this->subject->search($searchRequest);
|
||||||
|
@ -131,14 +169,16 @@ class SearchServiceTest extends AbstractUnitTestCase
|
||||||
*/
|
*/
|
||||||
public function configuredFilterAreAddedToRequestWithExistingFilter()
|
public function configuredFilterAreAddedToRequestWithExistingFilter()
|
||||||
{
|
{
|
||||||
$this->configuration->expects($this->exactly(2))
|
$this->configuration->expects($this->any())
|
||||||
->method('getIfExists')
|
->method('getIfExists')
|
||||||
->withConsecutive(['searching.size'], ['searching.facets'])
|
->withConsecutive(['searching.size'], ['searching.facets'])
|
||||||
->will($this->onConsecutiveCalls(null, null));
|
->will($this->onConsecutiveCalls(null, null));
|
||||||
$this->configuration->expects($this->exactly(1))
|
$this->configuration->expects($this->any())
|
||||||
->method('get')
|
->method('get')
|
||||||
->with('searching.filter')
|
->will($this->onConsecutiveCalls(
|
||||||
->willReturn(['property' => 'something']);
|
['property' => 'something'],
|
||||||
|
$this->throwException(new InvalidArgumentException)
|
||||||
|
));
|
||||||
|
|
||||||
$this->connection->expects($this->once())
|
$this->connection->expects($this->once())
|
||||||
->method('search')
|
->method('search')
|
||||||
|
@ -147,7 +187,8 @@ class SearchServiceTest extends AbstractUnitTestCase
|
||||||
'anotherProperty' => 'anything',
|
'anotherProperty' => 'anything',
|
||||||
'property' => 'something',
|
'property' => 'something',
|
||||||
];
|
];
|
||||||
}));
|
}))
|
||||||
|
->willReturn($this->getMockBuilder(SearchResultInterface::class)->getMock());
|
||||||
|
|
||||||
$searchRequest = new SearchRequest('SearchWord');
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
$searchRequest->setFilter(['anotherProperty' => 'anything']);
|
$searchRequest->setFilter(['anotherProperty' => 'anything']);
|
||||||
|
@ -159,20 +200,20 @@ class SearchServiceTest extends AbstractUnitTestCase
|
||||||
*/
|
*/
|
||||||
public function nonConfiguredFilterIsNotChangingRequestWithExistingFilter()
|
public function nonConfiguredFilterIsNotChangingRequestWithExistingFilter()
|
||||||
{
|
{
|
||||||
$this->configuration->expects($this->exactly(2))
|
$this->configuration->expects($this->any())
|
||||||
->method('getIfExists')
|
->method('getIfExists')
|
||||||
->withConsecutive(['searching.size'], ['searching.facets'])
|
->withConsecutive(['searching.size'], ['searching.facets'])
|
||||||
->will($this->onConsecutiveCalls(null, null));
|
->will($this->onConsecutiveCalls(null, null));
|
||||||
$this->configuration->expects($this->exactly(1))
|
$this->configuration->expects($this->any())
|
||||||
->method('get')
|
->method('get')
|
||||||
->with('searching.filter')
|
|
||||||
->will($this->throwException(new InvalidArgumentException));
|
->will($this->throwException(new InvalidArgumentException));
|
||||||
|
|
||||||
$this->connection->expects($this->once())
|
$this->connection->expects($this->once())
|
||||||
->method('search')
|
->method('search')
|
||||||
->with($this->callback(function ($searchRequest) {
|
->with($this->callback(function ($searchRequest) {
|
||||||
return $searchRequest->getFilter() === ['anotherProperty' => 'anything'];
|
return $searchRequest->getFilter() === ['anotherProperty' => 'anything'];
|
||||||
}));
|
}))
|
||||||
|
->willReturn($this->getMockBuilder(SearchResultInterface::class)->getMock());
|
||||||
|
|
||||||
$searchRequest = new SearchRequest('SearchWord');
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
$searchRequest->setFilter(['anotherProperty' => 'anything']);
|
$searchRequest->setFilter(['anotherProperty' => 'anything']);
|
||||||
|
@ -184,23 +225,101 @@ class SearchServiceTest extends AbstractUnitTestCase
|
||||||
*/
|
*/
|
||||||
public function emptyConfiguredFilterIsNotChangingRequestWithExistingFilter()
|
public function emptyConfiguredFilterIsNotChangingRequestWithExistingFilter()
|
||||||
{
|
{
|
||||||
$this->configuration->expects($this->exactly(2))
|
$this->configuration->expects($this->any())
|
||||||
->method('getIfExists')
|
->method('getIfExists')
|
||||||
->withConsecutive(['searching.size'], ['searching.facets'])
|
->withConsecutive(['searching.size'], ['searching.facets'])
|
||||||
->will($this->onConsecutiveCalls(null, null));
|
->will($this->onConsecutiveCalls(null, null));
|
||||||
$this->configuration->expects($this->exactly(1))
|
$this->configuration->expects($this->any())
|
||||||
->method('get')
|
->method('get')
|
||||||
->with('searching.filter')
|
->will($this->onConsecutiveCalls(
|
||||||
->willReturn(['anotherProperty' => '']);
|
['anotherProperty' => ''],
|
||||||
|
$this->throwException(new InvalidArgumentException)
|
||||||
|
));
|
||||||
|
|
||||||
$this->connection->expects($this->once())
|
$this->connection->expects($this->once())
|
||||||
->method('search')
|
->method('search')
|
||||||
->with($this->callback(function ($searchRequest) {
|
->with($this->callback(function ($searchRequest) {
|
||||||
return $searchRequest->getFilter() === ['anotherProperty' => 'anything'];
|
return $searchRequest->getFilter() === ['anotherProperty' => 'anything'];
|
||||||
}));
|
}))
|
||||||
|
->willReturn($this->getMockBuilder(SearchResultInterface::class)->getMock());
|
||||||
|
|
||||||
$searchRequest = new SearchRequest('SearchWord');
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
$searchRequest->setFilter(['anotherProperty' => 'anything']);
|
$searchRequest->setFilter(['anotherProperty' => 'anything']);
|
||||||
$this->subject->search($searchRequest);
|
$this->subject->search($searchRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function originalSearchResultIsReturnedIfNoDataProcessorIsConfigured()
|
||||||
|
{
|
||||||
|
$this->configuration->expects($this->any())
|
||||||
|
->method('getIfExists')
|
||||||
|
->withConsecutive(['searching.size'], ['searching.facets'])
|
||||||
|
->will($this->onConsecutiveCalls(null, null));
|
||||||
|
$this->configuration->expects($this->any())
|
||||||
|
->method('get')
|
||||||
|
->will($this->throwException(new InvalidArgumentException));
|
||||||
|
|
||||||
|
$searchResultMock = $this->getMockBuilder(SearchResultInterface::class)->getMock();
|
||||||
|
|
||||||
|
$this->connection->expects($this->once())
|
||||||
|
->method('search')
|
||||||
|
->willReturn($searchResultMock);
|
||||||
|
|
||||||
|
$this->dataProcessorService->expects($this->never())->method('executeDataProcessor');
|
||||||
|
|
||||||
|
$searchRequest = new SearchRequest('');
|
||||||
|
$this->assertSame(
|
||||||
|
$searchResultMock,
|
||||||
|
$this->subject->search($searchRequest),
|
||||||
|
'Did not get created result without applied data processing'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function configuredDataProcessorsAreExecutedOnSearchResult()
|
||||||
|
{
|
||||||
|
$this->configuration->expects($this->any())
|
||||||
|
->method('getIfExists')
|
||||||
|
->withConsecutive(['searching.size'], ['searching.facets'])
|
||||||
|
->will($this->onConsecutiveCalls(null, null));
|
||||||
|
$this->configuration->expects($this->any())
|
||||||
|
->method('get')
|
||||||
|
->will($this->onConsecutiveCalls(
|
||||||
|
$this->throwException(new InvalidArgumentException),
|
||||||
|
['SomeProcessorClass']
|
||||||
|
));
|
||||||
|
|
||||||
|
$searchResultMock = $this->getMockBuilder(SearchResultInterface::class)->getMock();
|
||||||
|
$searchResult = new SearchResult($searchResultMock, [['field 1' => 'value 1']]);
|
||||||
|
|
||||||
|
$this->connection->expects($this->once())
|
||||||
|
->method('search')
|
||||||
|
->willReturn($searchResult);
|
||||||
|
|
||||||
|
$this->dataProcessorService->expects($this->once())
|
||||||
|
->method('executeDataProcessor')
|
||||||
|
->with('SomeProcessorClass', ['field 1' => 'value 1'])
|
||||||
|
->willReturn([
|
||||||
|
'field 1' => 'value 1',
|
||||||
|
'field 2' => 'value 2',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->objectManager->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with(SearchResult::class, $searchResult, [
|
||||||
|
['field 1' => 'value 1', 'field 2' => 'value 2']
|
||||||
|
])
|
||||||
|
->willReturn($searchResultMock);
|
||||||
|
|
||||||
|
$searchRequest = new SearchRequest('');
|
||||||
|
$this->assertSame(
|
||||||
|
$searchResultMock,
|
||||||
|
$this->subject->search($searchRequest),
|
||||||
|
'Did not get created result with applied data processing'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
4
ext_conf_template.txt
Normal file
4
ext_conf_template.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
disable {
|
||||||
|
# cat=basic/enable; type=boolean; label=Disable Elasticsearch, which is enabled by default
|
||||||
|
elasticsearch = true
|
||||||
|
}
|
|
@ -37,11 +37,18 @@ call_user_func(
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\CMS\Extbase\Object\Container\Container')
|
// API does make use of object manager, therefore use GLOBALS
|
||||||
|
$extensionConfiguration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$extensionKey]);
|
||||||
|
if ($extensionConfiguration === false
|
||||||
|
|| !isset($extensionConfiguration['disable.']['elasticsearch'])
|
||||||
|
|| $extensionConfiguration['disable.']['elasticsearch'] !== '1'
|
||||||
|
) {
|
||||||
|
\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\Container::class)
|
||||||
->registerImplementation(
|
->registerImplementation(
|
||||||
'Codappix\SearchCore\Connection\ConnectionInterface',
|
\Codappix\SearchCore\Connection\ConnectionInterface::class,
|
||||||
'Codappix\SearchCore\Connection\Elasticsearch'
|
\Codappix\SearchCore\Connection\Elasticsearch::class
|
||||||
);
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
$_EXTKEY
|
$_EXTKEY
|
||||||
);
|
);
|
||||||
|
|
23
phpcs.xml.dist
Normal file
23
phpcs.xml.dist
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<ruleset name="search_core">
|
||||||
|
<description>The coding standard for search_core.</description>
|
||||||
|
|
||||||
|
<file>Classes/</file>
|
||||||
|
<file>Tests/</file>
|
||||||
|
|
||||||
|
<!-- Set default settings -->
|
||||||
|
<arg value="sp"/>
|
||||||
|
<arg name="colors"/>
|
||||||
|
<arg name="encoding" value="utf-8" />
|
||||||
|
<arg name="extensions" value="php,php.dist,inc" />
|
||||||
|
|
||||||
|
<!-- Base rules -->
|
||||||
|
<rule ref="PSR2">
|
||||||
|
<!-- As it does not work with new array syntax. -->
|
||||||
|
<exclude name="Squiz.Arrays.ArrayBracketSpacing.SpaceBeforeBracket" />
|
||||||
|
</rule>
|
||||||
|
<rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps">
|
||||||
|
<!-- We have to follow the TYPO3 hook method names. -->
|
||||||
|
<exclude-pattern>Classes/Hook/DataHandler.php</exclude-pattern>
|
||||||
|
</rule>
|
||||||
|
</ruleset>
|
Loading…
Reference in a new issue