mirror of
https://github.com/Codappix/search_core.git
synced 2024-11-22 10:16:10 +01:00
Merge remote-tracking branch 'origin/develop' into feature/phpcs-travis
Conflicts: Classes/Hook/DataHandler.php
This commit is contained in:
commit
ae51de9041
34 changed files with 437 additions and 269 deletions
|
@ -11,6 +11,7 @@ before_install:
|
||||||
language: php
|
language: php
|
||||||
|
|
||||||
php:
|
php:
|
||||||
|
- 7.0
|
||||||
- 7.1
|
- 7.1
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,12 @@ abstract class AbstractIndexer implements IndexerInterface
|
||||||
*/
|
*/
|
||||||
protected $logger;
|
protected $logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
|
||||||
|
* @inject
|
||||||
|
*/
|
||||||
|
protected $objectManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject log manager to get concrete logger from it.
|
* Inject log manager to get concrete logger from it.
|
||||||
*
|
*
|
||||||
|
@ -141,7 +147,8 @@ abstract class AbstractIndexer implements IndexerInterface
|
||||||
} else {
|
} else {
|
||||||
$className = $configuration['_typoScriptNodeValue'];
|
$className = $configuration['_typoScriptNodeValue'];
|
||||||
}
|
}
|
||||||
$dataProcessor = GeneralUtility::makeInstance($className);
|
|
||||||
|
$dataProcessor = $this->objectManager->get($className);
|
||||||
if ($dataProcessor instanceof ProcessorInterface) {
|
if ($dataProcessor instanceof ProcessorInterface) {
|
||||||
$record = $dataProcessor->processRecord($record, $configuration);
|
$record = $dataProcessor->processRecord($record, $configuration);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ namespace Codappix\SearchCore\Domain\Index\TcaIndexer;
|
||||||
* 02110-1301, USA.
|
* 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Codappix\SearchCore\Utility\FrontendUtility;
|
||||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||||
use TYPO3\CMS\Core\SingletonInterface as Singleton;
|
use TYPO3\CMS\Core\SingletonInterface as Singleton;
|
||||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
@ -32,14 +33,16 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
*/
|
*/
|
||||||
class RelationResolver implements Singleton
|
class RelationResolver implements Singleton
|
||||||
{
|
{
|
||||||
public function resolveRelationsForRecord(TcaTableService $service, array &$record) : void
|
public function resolveRelationsForRecord(TcaTableService $service, array &$record)
|
||||||
{
|
{
|
||||||
foreach (array_keys($record) as $column) {
|
foreach (array_keys($record) as $column) {
|
||||||
// TODO: Define / configure fields to exclude?!
|
// TODO: Define / configure fields to exclude?!
|
||||||
if ($column === 'pid') {
|
if ($column === 'pid') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$record[$column] = BackendUtility::getProcessedValueExtra(
|
|
||||||
|
$record[$column] = GeneralUtility::makeInstance($this->getUtilityForMode())
|
||||||
|
::getProcessedValueExtra(
|
||||||
$service->getTableName(),
|
$service->getTableName(),
|
||||||
$column,
|
$column,
|
||||||
$record[$column],
|
$record[$column],
|
||||||
|
@ -93,4 +96,13 @@ class RelationResolver implements Singleton
|
||||||
{
|
{
|
||||||
return array_map('trim', explode(',', $value));
|
return array_map('trim', explode(',', $value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getUtilityForMode(): string
|
||||||
|
{
|
||||||
|
if (TYPO3_MODE === 'BE') {
|
||||||
|
return BackendUtility::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FrontendUtility::class;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ class TcaTableService
|
||||||
* @param array &$records
|
* @param array &$records
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function filterRecordsByRootLineBlacklist(array &$records) : void
|
public function filterRecordsByRootLineBlacklist(array &$records)
|
||||||
{
|
{
|
||||||
$records = array_filter(
|
$records = array_filter(
|
||||||
$records,
|
$records,
|
||||||
|
@ -142,7 +142,7 @@ class TcaTableService
|
||||||
/**
|
/**
|
||||||
* @param array &$record
|
* @param array &$record
|
||||||
*/
|
*/
|
||||||
public function prepareRecord(array &$record) : void
|
public function prepareRecord(array &$record)
|
||||||
{
|
{
|
||||||
$this->relationResolver->resolveRelationsForRecord($this, $record);
|
$this->relationResolver->resolveRelationsForRecord($this, $record);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,20 +99,18 @@ class QueryFactory
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = ArrayUtility::setValueByPath(
|
$matchExpression = [
|
||||||
$query,
|
'type' => 'most_fields',
|
||||||
'query.bool.must.0.match._all.query',
|
'query' => $searchRequest->getSearchTerm(),
|
||||||
$searchRequest->getSearchTerm()
|
'fields' => GeneralUtility::trimExplode(',', $this->configuration->get('searching.fields.query')),
|
||||||
);
|
];
|
||||||
|
|
||||||
$minimumShouldMatch = $this->configuration->getIfExists('searching.minimumShouldMatch');
|
$minimumShouldMatch = $this->configuration->getIfExists('searching.minimumShouldMatch');
|
||||||
if ($minimumShouldMatch) {
|
if ($minimumShouldMatch) {
|
||||||
$query = ArrayUtility::setValueByPath(
|
$matchExpression['minimum_should_match'] = $minimumShouldMatch;
|
||||||
$query,
|
|
||||||
'query.bool.must.0.match._all.minimum_should_match',
|
|
||||||
$minimumShouldMatch
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$query = ArrayUtility::setValueByPath($query, 'query.bool.must.0.multi_match', $matchExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function addBoosts(SearchRequestInterface $searchRequest, array &$query)
|
protected function addBoosts(SearchRequestInterface $searchRequest, array &$query)
|
||||||
|
@ -248,11 +246,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(),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,15 +103,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
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,16 +83,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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -92,47 +92,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,
|
$this->processRecord($table, $uid);
|
||||||
$table,
|
}
|
||||||
$uid,
|
}
|
||||||
array $fieldArray,
|
|
||||||
CoreDataHandler $dataHandler
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,10 +58,8 @@ class DataHandlerFinisher extends AbstractFinisher
|
||||||
|
|
||||||
switch ($action) {
|
switch ($action) {
|
||||||
case 'update':
|
case 'update':
|
||||||
$this->dataHandler->update($tableName, $record);
|
|
||||||
break;
|
|
||||||
case 'add':
|
case 'add':
|
||||||
$this->dataHandler->add($tableName, $record);
|
$this->dataHandler->update($tableName, $record);
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
$this->dataHandler->delete($tableName, $record['uid']);
|
$this->dataHandler->delete($tableName, $record['uid']);
|
||||||
|
|
39
Classes/Utility/FrontendUtility.php
Normal file
39
Classes/Utility/FrontendUtility.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
namespace Codappix\SearchCore\Utility;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Justus Moroni <developer@leonmrni.com>
|
||||||
|
*
|
||||||
|
* 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\Backend\Utility\BackendUtility;
|
||||||
|
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrite BackendUtility to use in frontend.
|
||||||
|
* LanguageService was only usable in backend.
|
||||||
|
*/
|
||||||
|
class FrontendUtility extends BackendUtility
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return TypoScriptFrontendController
|
||||||
|
*/
|
||||||
|
protected static function getLanguageService()
|
||||||
|
{
|
||||||
|
return $GLOBALS['TSFE'];
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,12 @@ plugin {
|
||||||
abstractFields = {$plugin.tx_searchcore.settings.indexing.pages.abstractFields}
|
abstractFields = {$plugin.tx_searchcore.settings.indexing.pages.abstractFields}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
searching {
|
||||||
|
fields {
|
||||||
|
query = _all
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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`.
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -205,3 +205,6 @@ class name) as done in the examples above.
|
||||||
By implementing also the same interface as necessary for TYPO3
|
By implementing also the same interface as necessary for TYPO3
|
||||||
:ref:`t3tsref:cobj-fluidtemplate-properties-dataprocessing`, you are able to reuse the same code
|
: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.
|
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``.
|
||||||
|
|
|
@ -151,7 +151,18 @@ filtering. This way you can use arbitrary filter names and map them to existing
|
||||||
fields
|
fields
|
||||||
------
|
------
|
||||||
|
|
||||||
Defines the fields to fetch from elasticsearch. Two sub entries exist:
|
Defines the fields to fetch and search from elasticsearch. With the following sub keys:
|
||||||
|
|
||||||
|
``query`` defines the fields to search in. Default is ``_all`` from 5.x times of elasticsearch.
|
||||||
|
Configure a comma separated list of fields to search in. This is necessary if you have configured
|
||||||
|
special mapping for some fields, or just want to search some fields.
|
||||||
|
The most hits get ranked highest. The following is an example configuration::
|
||||||
|
|
||||||
|
fields {
|
||||||
|
query = _all, city
|
||||||
|
}
|
||||||
|
|
||||||
|
The following sub properties configure the fields to fetch from elasticsearch:
|
||||||
|
|
||||||
First ``stored_fields`` which is a list of comma separated fields which actually exist and will be
|
First ``stored_fields`` which is a list of comma separated fields which actually exist and will be
|
||||||
added. Typically you will use ``_source`` to fetch the whole indexed fields.
|
added. Typically you will use ``_source`` to fetch the whole indexed fields.
|
||||||
|
|
|
@ -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
|
||||||
|
|
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, $result->getResults()[0]['uid'], 'Did not get the expected result entry.');
|
$this->assertSame(5, $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('Search Word');
|
|
||||||
$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.');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,8 @@ plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
searching {
|
searching {
|
||||||
facets {
|
fields {
|
||||||
contentTypes {
|
query = _all
|
||||||
field = CType
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ use Codappix\SearchCore\Connection\ConnectionInterface;
|
||||||
use Codappix\SearchCore\DataProcessing\CopyToProcessor;
|
use Codappix\SearchCore\DataProcessing\CopyToProcessor;
|
||||||
use Codappix\SearchCore\Domain\Index\AbstractIndexer;
|
use Codappix\SearchCore\Domain\Index\AbstractIndexer;
|
||||||
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
|
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
|
||||||
|
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||||
|
|
||||||
class AbstractIndexerTest extends AbstractUnitTestCase
|
class AbstractIndexerTest extends AbstractUnitTestCase
|
||||||
{
|
{
|
||||||
|
@ -44,17 +45,25 @@ class AbstractIndexerTest extends AbstractUnitTestCase
|
||||||
*/
|
*/
|
||||||
protected $connection;
|
protected $connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ObjectManagerInterface
|
||||||
|
*/
|
||||||
|
protected $objectManager;
|
||||||
|
|
||||||
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->objectManager = $this->getMockBuilder(ObjectManagerInterface::class)->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, 'objectManager', $this->objectManager);
|
||||||
$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,6 +82,11 @@ 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->objectManager->expects($this->any())
|
||||||
|
->method('get')
|
||||||
|
->with(CopyToProcessor::class)
|
||||||
|
->willReturn(new CopyToProcessor());
|
||||||
|
|
||||||
$this->configuration->expects($this->exactly(2))
|
$this->configuration->expects($this->exactly(2))
|
||||||
->method('get')
|
->method('get')
|
||||||
->withConsecutive(['indexing.testTable.dataProcessing'], ['indexing.testTable.abstractFields'])
|
->withConsecutive(['indexing.testTable.dataProcessing'], ['indexing.testTable.abstractFields'])
|
||||||
|
|
|
@ -56,9 +56,7 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
{
|
{
|
||||||
$searchRequest = new SearchRequest('SearchWord');
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
|
|
||||||
$this->configuration->expects($this->any())
|
$this->configureConfigurationMockWithDefault();
|
||||||
->method('get')
|
|
||||||
->will($this->throwException(new InvalidArgumentException));
|
|
||||||
|
|
||||||
$query = $this->subject->create($searchRequest);
|
$query = $this->subject->create($searchRequest);
|
||||||
$this->assertInstanceOf(
|
$this->assertInstanceOf(
|
||||||
|
@ -73,9 +71,7 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
*/
|
*/
|
||||||
public function filterIsAddedToQuery()
|
public function filterIsAddedToQuery()
|
||||||
{
|
{
|
||||||
$this->configuration->expects($this->any())
|
$this->configureConfigurationMockWithDefault();
|
||||||
->method('get')
|
|
||||||
->will($this->throwException(new InvalidArgumentException));
|
|
||||||
|
|
||||||
$searchRequest = new SearchRequest('SearchWord');
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
$searchRequest->setFilter(['field' => 'content']);
|
$searchRequest->setFilter(['field' => 'content']);
|
||||||
|
@ -95,9 +91,7 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
*/
|
*/
|
||||||
public function emptyFilterIsNotAddedToQuery()
|
public function emptyFilterIsNotAddedToQuery()
|
||||||
{
|
{
|
||||||
$this->configuration->expects($this->any())
|
$this->configureConfigurationMockWithDefault();
|
||||||
->method('get')
|
|
||||||
->will($this->throwException(new InvalidArgumentException));
|
|
||||||
|
|
||||||
$searchRequest = new SearchRequest('SearchWord');
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
$searchRequest->setFilter([
|
$searchRequest->setFilter([
|
||||||
|
@ -122,12 +116,10 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
*/
|
*/
|
||||||
public function facetsAreAddedToQuery()
|
public function facetsAreAddedToQuery()
|
||||||
{
|
{
|
||||||
$this->configuration->expects($this->any())
|
$this->configureConfigurationMockWithDefault();
|
||||||
->method('get')
|
|
||||||
->will($this->throwException(new InvalidArgumentException));
|
|
||||||
$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(
|
||||||
|
@ -153,9 +145,7 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
*/
|
*/
|
||||||
public function sizeIsAddedToQuery()
|
public function sizeIsAddedToQuery()
|
||||||
{
|
{
|
||||||
$this->configuration->expects($this->any())
|
$this->configureConfigurationMockWithDefault();
|
||||||
->method('get')
|
|
||||||
->will($this->throwException(new InvalidArgumentException));
|
|
||||||
$searchRequest = new SearchRequest('SearchWord');
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
$searchRequest->setLimit(45);
|
$searchRequest->setLimit(45);
|
||||||
$searchRequest->setOffset(35);
|
$searchRequest->setOffset(35);
|
||||||
|
@ -179,9 +169,7 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
public function searchTermIsAddedToQuery()
|
public function searchTermIsAddedToQuery()
|
||||||
{
|
{
|
||||||
$searchRequest = new SearchRequest('SearchWord');
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
$this->configuration->expects($this->any())
|
$this->configureConfigurationMockWithDefault();
|
||||||
->method('get')
|
|
||||||
->will($this->throwException(new InvalidArgumentException));
|
|
||||||
$query = $this->subject->create($searchRequest);
|
$query = $this->subject->create($searchRequest);
|
||||||
|
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
|
@ -189,9 +177,11 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
'bool' => [
|
'bool' => [
|
||||||
'must' => [
|
'must' => [
|
||||||
[
|
[
|
||||||
'match' => [
|
'multi_match' => [
|
||||||
'_all' => [
|
'type' => 'most_fields',
|
||||||
'query' => 'SearchWord',
|
'query' => 'SearchWord',
|
||||||
|
'fields' => [
|
||||||
|
'_all',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -219,9 +209,7 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
'50%',
|
'50%',
|
||||||
null
|
null
|
||||||
));
|
));
|
||||||
$this->configuration->expects($this->any())
|
$this->configureConfigurationMockWithDefault();
|
||||||
->method('get')
|
|
||||||
->will($this->throwException(new InvalidArgumentException));
|
|
||||||
$query = $this->subject->create($searchRequest);
|
$query = $this->subject->create($searchRequest);
|
||||||
|
|
||||||
$this->assertArraySubset(
|
$this->assertArraySubset(
|
||||||
|
@ -229,10 +217,13 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
'bool' => [
|
'bool' => [
|
||||||
'must' => [
|
'must' => [
|
||||||
[
|
[
|
||||||
'match' => [
|
'multi_match' => [
|
||||||
'_all' => [
|
'type' => 'most_fields',
|
||||||
'minimum_should_match' => '50%',
|
'query' => 'SearchWord',
|
||||||
|
'fields' => [
|
||||||
|
'_all',
|
||||||
],
|
],
|
||||||
|
'minimum_should_match' => '50%',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -253,12 +244,14 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
$this->configuration->expects($this->any())
|
$this->configuration->expects($this->any())
|
||||||
->method('get')
|
->method('get')
|
||||||
->withConsecutive(
|
->withConsecutive(
|
||||||
|
['searching.fields.query'],
|
||||||
['searching.boost'],
|
['searching.boost'],
|
||||||
['searching.fields.stored_fields'],
|
['searching.fields.stored_fields'],
|
||||||
['searching.fields.script_fields'],
|
['searching.fields.script_fields'],
|
||||||
['searching.fieldValueFactor']
|
['searching.fieldValueFactor']
|
||||||
)
|
)
|
||||||
->will($this->onConsecutiveCalls(
|
->will($this->onConsecutiveCalls(
|
||||||
|
'_all',
|
||||||
[
|
[
|
||||||
'search_title' => 3,
|
'search_title' => 3,
|
||||||
'search_abstract' => 1.5,
|
'search_abstract' => 1.5,
|
||||||
|
@ -308,12 +301,14 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
$this->configuration->expects($this->any())
|
$this->configuration->expects($this->any())
|
||||||
->method('get')
|
->method('get')
|
||||||
->withConsecutive(
|
->withConsecutive(
|
||||||
|
['searching.fields.query'],
|
||||||
['searching.boost'],
|
['searching.boost'],
|
||||||
['searching.fields.stored_fields'],
|
['searching.fields.stored_fields'],
|
||||||
['searching.fields.script_fields'],
|
['searching.fields.script_fields'],
|
||||||
['searching.fieldValueFactor']
|
['searching.fieldValueFactor']
|
||||||
)
|
)
|
||||||
->will($this->onConsecutiveCalls(
|
->will($this->onConsecutiveCalls(
|
||||||
|
'_all',
|
||||||
$this->throwException(new InvalidArgumentException),
|
$this->throwException(new InvalidArgumentException),
|
||||||
$this->throwException(new InvalidArgumentException),
|
$this->throwException(new InvalidArgumentException),
|
||||||
$this->throwException(new InvalidArgumentException),
|
$this->throwException(new InvalidArgumentException),
|
||||||
|
@ -328,9 +323,11 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
'bool' => [
|
'bool' => [
|
||||||
'must' => [
|
'must' => [
|
||||||
[
|
[
|
||||||
'match' => [
|
'multi_match' => [
|
||||||
'_all' => [
|
'type' => 'most_fields',
|
||||||
'query' => 'SearchWord',
|
'query' => 'SearchWord',
|
||||||
|
'fields' => [
|
||||||
|
'_all',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -352,9 +349,7 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
{
|
{
|
||||||
$searchRequest = new SearchRequest();
|
$searchRequest = new SearchRequest();
|
||||||
|
|
||||||
$this->configuration->expects($this->any())
|
$this->configureConfigurationMockWithDefault();
|
||||||
->method('get')
|
|
||||||
->will($this->throwException(new InvalidArgumentException));
|
|
||||||
|
|
||||||
$query = $this->subject->create($searchRequest);
|
$query = $this->subject->create($searchRequest);
|
||||||
$this->assertInstanceOf(
|
$this->assertInstanceOf(
|
||||||
|
@ -364,6 +359,54 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function configuredQueryFieldsAreAddedToQuery()
|
||||||
|
{
|
||||||
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
|
|
||||||
|
$this->configuration->expects($this->any())
|
||||||
|
->method('get')
|
||||||
|
->withConsecutive(
|
||||||
|
['searching.fields.query'],
|
||||||
|
['searching.boost'],
|
||||||
|
['searching.fields.stored_fields'],
|
||||||
|
['searching.fields.script_fields'],
|
||||||
|
['searching.fieldValueFactor']
|
||||||
|
)
|
||||||
|
->will($this->onConsecutiveCalls(
|
||||||
|
'_all, field1, field2',
|
||||||
|
$this->throwException(new InvalidArgumentException),
|
||||||
|
$this->throwException(new InvalidArgumentException),
|
||||||
|
$this->throwException(new InvalidArgumentException),
|
||||||
|
$this->throwException(new InvalidArgumentException)
|
||||||
|
));
|
||||||
|
|
||||||
|
$query = $this->subject->create($searchRequest);
|
||||||
|
$this->assertArraySubset(
|
||||||
|
[
|
||||||
|
'bool' => [
|
||||||
|
'must' => [
|
||||||
|
[
|
||||||
|
'multi_match' => [
|
||||||
|
'type' => 'most_fields',
|
||||||
|
'query' => 'SearchWord',
|
||||||
|
'fields' => [
|
||||||
|
'_all',
|
||||||
|
'field1',
|
||||||
|
'field2',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
$query->toArray()['query'],
|
||||||
|
'Configured fields were not added to query as configured.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
*/
|
*/
|
||||||
|
@ -433,12 +476,14 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
$this->configuration->expects($this->any())
|
$this->configuration->expects($this->any())
|
||||||
->method('get')
|
->method('get')
|
||||||
->withConsecutive(
|
->withConsecutive(
|
||||||
|
['searching.fields.query'],
|
||||||
['searching.boost'],
|
['searching.boost'],
|
||||||
['searching.fields.stored_fields'],
|
['searching.fields.stored_fields'],
|
||||||
['searching.fields.script_fields'],
|
['searching.fields.script_fields'],
|
||||||
['searching.fieldValueFactor']
|
['searching.fieldValueFactor']
|
||||||
)
|
)
|
||||||
->will($this->onConsecutiveCalls(
|
->will($this->onConsecutiveCalls(
|
||||||
|
'_all',
|
||||||
$this->throwException(new InvalidArgumentException),
|
$this->throwException(new InvalidArgumentException),
|
||||||
$this->throwException(new InvalidArgumentException),
|
$this->throwException(new InvalidArgumentException),
|
||||||
[
|
[
|
||||||
|
@ -521,9 +566,7 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
]
|
]
|
||||||
));
|
));
|
||||||
|
|
||||||
$this->configuration->expects($this->any())
|
$this->configureConfigurationMockWithDefault();
|
||||||
->method('get')
|
|
||||||
->will($this->throwException(new InvalidArgumentException));
|
|
||||||
|
|
||||||
$query = $this->subject->create($searchRequest);
|
$query = $this->subject->create($searchRequest);
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
|
@ -558,9 +601,7 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
null
|
null
|
||||||
));
|
));
|
||||||
|
|
||||||
$this->configuration->expects($this->any())
|
$this->configureConfigurationMockWithDefault();
|
||||||
->method('get')
|
|
||||||
->will($this->throwException(new InvalidArgumentException));
|
|
||||||
|
|
||||||
$query = $this->subject->create($searchRequest);
|
$query = $this->subject->create($searchRequest);
|
||||||
$this->assertTrue(
|
$this->assertTrue(
|
||||||
|
@ -568,4 +609,17 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
'Sort was added to query even if not configured.'
|
'Sort was added to query even if not configured.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function configureConfigurationMockWithDefault()
|
||||||
|
{
|
||||||
|
$this->configuration->expects($this->any())
|
||||||
|
->method('get')
|
||||||
|
->will($this->returnCallback(function ($configName) {
|
||||||
|
if ($configName === 'searching.fields.query') {
|
||||||
|
return '_all';
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidArgumentException();
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,19 +83,14 @@ class DataHandlerFinisherTest extends AbstractUnitTestCase
|
||||||
public function possibleFinisherSetup() : array
|
public function possibleFinisherSetup() : array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'valid add configuration' => [
|
|
||||||
'action' => 'add',
|
|
||||||
'nonCalledActions' => ['delete', 'update'],
|
|
||||||
'expectedSecondArgument' => ['uid' => 23],
|
|
||||||
],
|
|
||||||
'valid update configuration' => [
|
'valid update configuration' => [
|
||||||
'action' => 'update',
|
'action' => 'update',
|
||||||
'nonCalledActions' => ['delete', 'add'],
|
'nonCalledActions' => ['delete'],
|
||||||
'expectedSecondArgument' => ['uid' => 23],
|
'expectedSecondArgument' => ['uid' => 23],
|
||||||
],
|
],
|
||||||
'valid delete configuration' => [
|
'valid delete configuration' => [
|
||||||
'action' => 'delete',
|
'action' => 'delete',
|
||||||
'nonCalledActions' => ['update', 'add'],
|
'nonCalledActions' => ['update'],
|
||||||
'expectedSecondArgument' => 23,
|
'expectedSecondArgument' => 23,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -109,7 +104,7 @@ class DataHandlerFinisherTest extends AbstractUnitTestCase
|
||||||
{
|
{
|
||||||
$this->subject->setOptions($options);
|
$this->subject->setOptions($options);
|
||||||
|
|
||||||
foreach (['add', 'update', 'delete'] as $nonCalledAction) {
|
foreach (['update', 'delete'] as $nonCalledAction) {
|
||||||
$this->dataHandlerMock->expects($this->never())->method($nonCalledAction);
|
$this->dataHandlerMock->expects($this->never())->method($nonCalledAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
"TYPO3\\CMS\\Core\\Tests\\": ".Build/vendor/typo3/cms/typo3/sysext/core/Tests/"
|
"TYPO3\\CMS\\Core\\Tests\\": ".Build/vendor/typo3/cms/typo3/sysext/core/Tests/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"require" : {
|
"require": {
|
||||||
"php": ">=7.1.0",
|
"php": ">=7.0.0",
|
||||||
"typo3/cms": "~8.7",
|
"typo3/cms": "~8.7",
|
||||||
"ruflin/elastica": "~3.2"
|
"ruflin/elastica": "~3.2"
|
||||||
},
|
},
|
||||||
|
|
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
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue