mirror of
https://github.com/Codappix/search_core.git
synced 2024-12-22 17:56:10 +01:00
Merge remote-tracking branch 'origin/develop' into feature/cms-8-support
This commit is contained in:
commit
3d90bad58d
78 changed files with 2066 additions and 319 deletions
21
.travis.yml
21
.travis.yml
|
@ -1,7 +1,15 @@
|
|||
sudo: true
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- oracle-java8-set-default
|
||||
before_install:
|
||||
- curl -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.2.0.deb && sudo dpkg -i --force-confnew elasticsearch-5.2.0.deb && sudo service elasticsearch start
|
||||
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
|
||||
|
@ -16,18 +24,12 @@ env:
|
|||
- typo3DatabaseUsername="travis"
|
||||
- typo3DatabasePassword=""
|
||||
matrix:
|
||||
- TYPO3_VERSION="~7.6"
|
||||
- TYPO3_VERSION="~8"
|
||||
- TYPO3_VERSION="dev-master"
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
exclude:
|
||||
# TYPO3 no longer supports 5.6
|
||||
- env: TYPO3_VERSION="~8"
|
||||
php: 5.6
|
||||
- env: TYPO3_VERSION="dev-master"
|
||||
php: 5.6
|
||||
allow_failures:
|
||||
- env: TYPO3_VERSION="~8"
|
||||
php: 7.0
|
||||
|
@ -40,11 +42,12 @@ matrix:
|
|||
|
||||
services:
|
||||
- mysql
|
||||
- elasticsearch
|
||||
|
||||
install: make install
|
||||
|
||||
script: make functionalTests
|
||||
script:
|
||||
- make unitTests
|
||||
- make functionalTests
|
||||
|
||||
after_script:
|
||||
- make uploadCodeCoverage
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Command;
|
||||
namespace Codappix\SearchCore\Command;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,7 +20,8 @@ namespace Leonmrni\SearchCore\Command;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Domain\Index\IndexerFactory;
|
||||
use Codappix\SearchCore\Domain\Index\IndexerFactory;
|
||||
use Codappix\SearchCore\Domain\Index\NoMatchingIndexerException;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\CommandController;
|
||||
|
||||
|
@ -34,12 +35,6 @@ class IndexCommandController extends CommandController
|
|||
*/
|
||||
protected $indexerFactory;
|
||||
|
||||
/**
|
||||
* @var \Leonmrni\SearchCore\Configuration\ConfigurationContainerInterface
|
||||
* @inject
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* @param IndexerFactory $factory
|
||||
*/
|
||||
|
@ -49,19 +44,18 @@ class IndexCommandController extends CommandController
|
|||
}
|
||||
|
||||
/**
|
||||
* Will index the given table or everything.
|
||||
* Will index the given identifier.
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $identifier
|
||||
*/
|
||||
public function indexCommand($table)
|
||||
public function indexCommand($identifier)
|
||||
{
|
||||
// TODO: Allow to index multiple tables at once?
|
||||
// TODO: Also allow to index everything?
|
||||
if (! in_array($table, GeneralUtility::trimExplode(',', $this->configuration->get('indexer.tca.allowedTables')))) {
|
||||
$this->outputLine('Table is not allowed for indexing.');
|
||||
$this->quit(1);
|
||||
try {
|
||||
$this->indexerFactory->getIndexer($identifier)->indexAllDocuments();
|
||||
$this->outputLine($identifier . ' was indexed.');
|
||||
} catch (NoMatchingIndexerException $e) {
|
||||
$this->outputLine('No indexer found for: ' . $identifier);
|
||||
}
|
||||
$this->indexerFactory->getIndexer($table)->indexAllDocuments();
|
||||
$this->outputLine('Table was indexed.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Configuration;
|
||||
namespace Codappix\SearchCore\Configuration;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Configuration;
|
||||
namespace Codappix\SearchCore\Configuration;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -34,6 +34,8 @@ interface ConfigurationContainerInterface extends Singleton
|
|||
*
|
||||
* @param string $path In dot notation. E.g. indexer.tca.allowedTables
|
||||
* @return mixed
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function get($path);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Configuration;
|
||||
namespace Codappix\SearchCore\Configuration;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Configuration;
|
||||
namespace Codappix\SearchCore\Configuration;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Connection;
|
||||
namespace Codappix\SearchCore\Connection;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Connection;
|
||||
namespace Codappix\SearchCore\Connection;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,7 +20,10 @@ namespace Leonmrni\SearchCore\Connection;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Codappix\SearchCore\Connection\Elasticsearch\SearchResult;
|
||||
use Codappix\SearchCore\Domain\Search\QueryFactory;
|
||||
use TYPO3\CMS\Core\SingletonInterface as Singleton;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||
|
||||
/**
|
||||
* Outer wrapper to elasticsearch.
|
||||
|
@ -42,16 +45,31 @@ class Elasticsearch implements Singleton, ConnectionInterface
|
|||
*/
|
||||
protected $typeFactory;
|
||||
|
||||
/**
|
||||
* @var Elasticsearch\MappingFactory
|
||||
*/
|
||||
protected $mappingFactory;
|
||||
|
||||
/**
|
||||
* @var Elasticsearch\DocumentFactory
|
||||
*/
|
||||
protected $documentFactory;
|
||||
|
||||
/**
|
||||
* @var QueryFactory
|
||||
*/
|
||||
protected $queryFactory;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Log\Logger
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var ObjectManagerInterface
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* Inject log manager to get concrete logger from it.
|
||||
*
|
||||
|
@ -62,22 +80,36 @@ class Elasticsearch implements Singleton, ConnectionInterface
|
|||
$this->logger = $logManager->getLogger(__CLASS__);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectManagerInterface $objectManager
|
||||
*/
|
||||
public function injectObjectManager(ObjectManagerInterface $objectManager)
|
||||
{
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Elasticsearch\Connection $connection
|
||||
* @param Elasticsearch\IndexFactory $indexFactory
|
||||
* @param Elasticsearch\TypeFactory $typeFactory
|
||||
* @param Elasticsearch\MappingFactory $mappingFactory
|
||||
* @param Elasticsearch\DocumentFactory $documentFactory
|
||||
* @param QueryFactory $queryFactory
|
||||
*/
|
||||
public function __construct(
|
||||
Elasticsearch\Connection $connection,
|
||||
Elasticsearch\IndexFactory $indexFactory,
|
||||
Elasticsearch\TypeFactory $typeFactory,
|
||||
Elasticsearch\DocumentFactory $documentFactory
|
||||
Elasticsearch\MappingFactory $mappingFactory,
|
||||
Elasticsearch\DocumentFactory $documentFactory,
|
||||
QueryFactory $queryFactory
|
||||
) {
|
||||
$this->connection = $connection;
|
||||
$this->indexFactory = $indexFactory;
|
||||
$this->typeFactory = $typeFactory;
|
||||
$this->mappingFactory = $mappingFactory;
|
||||
$this->documentFactory = $documentFactory;
|
||||
$this->queryFactory = $queryFactory;
|
||||
}
|
||||
|
||||
public function addDocument($documentType, array $document)
|
||||
|
@ -133,6 +165,13 @@ class Elasticsearch implements Singleton, ConnectionInterface
|
|||
protected function withType($documentType, callable $callback)
|
||||
{
|
||||
$type = $this->getType($documentType);
|
||||
// TODO: Check whether it's to heavy to send it so often e.g. for every single document.
|
||||
// Perhaps add command controller to submit mapping?!
|
||||
// Also it's not possible to change mapping without deleting index first.
|
||||
// Mattes told about a solution.
|
||||
// So command looks like the best way so far, except we manage mattes solution.
|
||||
// Still then this should be done once. So perhaps singleton which tracks state and does only once?
|
||||
$this->mappingFactory->getMapping($type)->send();
|
||||
$callback($type);
|
||||
$type->getIndex()->refresh();
|
||||
}
|
||||
|
@ -140,7 +179,7 @@ class Elasticsearch implements Singleton, ConnectionInterface
|
|||
/**
|
||||
* @param SearchRequestInterface $searchRequest
|
||||
*
|
||||
* @return \Elastica\ResultSet
|
||||
* @return SearchResultInterface
|
||||
*/
|
||||
public function search(SearchRequestInterface $searchRequest)
|
||||
{
|
||||
|
@ -148,10 +187,9 @@ class Elasticsearch implements Singleton, ConnectionInterface
|
|||
|
||||
$search = new \Elastica\Search($this->connection->getClient());
|
||||
$search->addIndex('typo3content');
|
||||
$search->setQuery($this->queryFactory->create($searchRequest));
|
||||
|
||||
// TODO: Return wrapped result to implement our interface.
|
||||
// Also update php doc to reflect the change.
|
||||
return $search->search('"' . $searchRequest->getSearchTerm() . '"');
|
||||
return $this->objectManager->get(SearchResult::class, $search->search());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Connection\Elasticsearch;
|
||||
namespace Codappix\SearchCore\Connection\Elasticsearch;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,7 +20,7 @@ namespace Leonmrni\SearchCore\Connection\Elasticsearch;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use TYPO3\CMS\Core\SingletonInterface as Singleton;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Connection\Elasticsearch;
|
||||
namespace Codappix\SearchCore\Connection\Elasticsearch;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
|
93
Classes/Connection/Elasticsearch/Facet.php
Normal file
93
Classes/Connection/Elasticsearch/Facet.php
Normal file
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\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\Configuration\ConfigurationContainerInterface;
|
||||
use Codappix\SearchCore\Connection\FacetInterface;
|
||||
|
||||
class Facet implements FacetInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $field = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $buckets = [];
|
||||
|
||||
/**
|
||||
* @var array<FacetOption>
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
public function __construct($name, array $aggregation, ConfigurationContainerInterface $configuration)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->buckets = $aggregation['buckets'];
|
||||
$this->field = $configuration->getIfExists('searching.facets.' . $this->name . '.field') ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getField()
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all possible options for this facet.
|
||||
*
|
||||
* @return array<FacetOptionInterface>
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
$this->initOptions();
|
||||
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
protected function initOptions()
|
||||
{
|
||||
if ($this->options !== []) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->buckets as $bucket) {
|
||||
$this->options[] = new FacetOption($bucket);
|
||||
}
|
||||
}
|
||||
}
|
61
Classes/Connection/Elasticsearch/FacetOption.php
Normal file
61
Classes/Connection/Elasticsearch/FacetOption.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\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\Connection\FacetOptionInterface;
|
||||
|
||||
class FacetOption implements FacetOptionInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $count = 0;
|
||||
|
||||
/**
|
||||
* @param array $bucket
|
||||
*/
|
||||
public function __construct(array $bucket)
|
||||
{
|
||||
$this->name = $bucket['key'];
|
||||
$this->count = $bucket['doc_count'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCount()
|
||||
{
|
||||
return $this->count;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Connection\Elasticsearch;
|
||||
namespace Codappix\SearchCore\Connection\Elasticsearch;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
|
73
Classes/Connection/Elasticsearch/MappingFactory.php
Normal file
73
Classes/Connection/Elasticsearch/MappingFactory.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\Connection\Elasticsearch;
|
||||
|
||||
/*
|
||||
* 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\Configuration\InvalidArgumentException;
|
||||
use TYPO3\CMS\Core\SingletonInterface as Singleton;
|
||||
|
||||
/**
|
||||
* Factory to get mappings.
|
||||
*/
|
||||
class MappingFactory implements Singleton
|
||||
{
|
||||
/**
|
||||
* @var ConfigurationContainerInterface
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* @param ConfigurationContainerInterface $configuration
|
||||
*/
|
||||
public function __construct(ConfigurationContainerInterface $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an mapping based on type.
|
||||
*
|
||||
* @param \Elastica\Type $type
|
||||
*
|
||||
* @return \Elastica\Mapping
|
||||
*/
|
||||
public function getMapping(\Elastica\Type $type)
|
||||
{
|
||||
$mapping = new \Elastica\Type\Mapping();
|
||||
$mapping->setType($type);
|
||||
$mapping->setProperties($this->getConfiguration($type->getName()));
|
||||
|
||||
return $mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $identifier
|
||||
* @return array
|
||||
*/
|
||||
protected function getConfiguration($identifier)
|
||||
{
|
||||
try {
|
||||
return $this->configuration->get('indexing.' . $identifier . '.mapping');
|
||||
} catch (InvalidArgumentException $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
56
Classes/Connection/Elasticsearch/ResultItem.php
Normal file
56
Classes/Connection/Elasticsearch/ResultItem.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\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\Connection\ResultItemInterface;
|
||||
|
||||
class ResultItem implements ResultItemInterface
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
public function __construct(\Elastica\Result $result)
|
||||
{
|
||||
$this->data = $result->getData();
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->data[$offset]);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->data[$offset];
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
throw new \BadMethodCallException('It\'s not possible to change the search result.', 1499179077);
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
throw new \BadMethodCallException('It\'s not possible to change the search result.', 1499179077);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Connection\Elasticsearch;
|
||||
namespace Codappix\SearchCore\Connection\Elasticsearch;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,12 +20,127 @@ namespace Leonmrni\SearchCore\Connection\Elasticsearch;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Connection\SearchResultInterface;
|
||||
use Codappix\SearchCore\Connection\FacetInterface;
|
||||
use Codappix\SearchCore\Connection\ResultItemInterface;
|
||||
use Codappix\SearchCore\Connection\SearchResultInterface;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class SearchResult extends \Elastica\SearchResult implements SearchResultInterface
|
||||
class SearchResult implements SearchResultInterface
|
||||
{
|
||||
/**
|
||||
* @var \Elastica\ResultSet
|
||||
*/
|
||||
protected $result;
|
||||
|
||||
/**
|
||||
* @var array<FacetInterface>
|
||||
*/
|
||||
protected $facets = [];
|
||||
|
||||
/**
|
||||
* @var array<ResultItemInterface>
|
||||
*/
|
||||
protected $results = [];
|
||||
|
||||
/**
|
||||
* @var ObjectManagerInterface
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
public function __construct(\Elastica\ResultSet $result, ObjectManagerInterface $objectManager)
|
||||
{
|
||||
$this->result = $result;
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<ResultItemInterface>
|
||||
*/
|
||||
public function getResults()
|
||||
{
|
||||
$this->initResults();
|
||||
|
||||
return $this->results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all facets, if any.
|
||||
*
|
||||
* @return array<FacetIterface>
|
||||
*/
|
||||
public function getFacets()
|
||||
{
|
||||
$this->initFacets();
|
||||
|
||||
return $this->facets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total sum of matching results.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalCount()
|
||||
{
|
||||
return $this->result->getTotalHits();
|
||||
}
|
||||
|
||||
// Countable - Interface
|
||||
/**
|
||||
* Returns the total sum of results contained in this result.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return $this->result->count();
|
||||
}
|
||||
|
||||
// Iterator - Interface
|
||||
public function current()
|
||||
{
|
||||
return $this->result->current();
|
||||
}
|
||||
|
||||
public function next()
|
||||
{
|
||||
return $this->result->next();
|
||||
}
|
||||
|
||||
public function key()
|
||||
{
|
||||
return $this->result->key();
|
||||
}
|
||||
|
||||
public function valid()
|
||||
{
|
||||
return $this->result->valid();
|
||||
}
|
||||
|
||||
public function rewind()
|
||||
{
|
||||
$this->result->rewind();
|
||||
}
|
||||
|
||||
protected function initResults()
|
||||
{
|
||||
if ($this->results !== []) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->result->getResults() as $result) {
|
||||
$this->results[] = new ResultItem($result);
|
||||
}
|
||||
}
|
||||
|
||||
protected function initFacets()
|
||||
{
|
||||
if ($this->facets !== [] || !$this->result->hasAggregations()) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->result->getAggregations() as $aggregationName => $aggregation) {
|
||||
$this->facets[] = $this->objectManager->get(Facet::class, $aggregationName, $aggregation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Connection\Elasticsearch;
|
||||
namespace Codappix\SearchCore\Connection\Elasticsearch;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
|
39
Classes/Connection/FacetInterface.php
Normal file
39
Classes/Connection/FacetInterface.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\Connection;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A single facet, e.g. keyword based.
|
||||
*/
|
||||
interface FacetInterface
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Returns all possible options for this facet.
|
||||
*
|
||||
* @return array<FacetOptionInterface>
|
||||
*/
|
||||
public function getOptions();
|
||||
}
|
42
Classes/Connection/FacetOptionInterface.php
Normal file
42
Classes/Connection/FacetOptionInterface.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\Connection;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A single possible option of a facet.
|
||||
*/
|
||||
interface FacetOptionInterface
|
||||
{
|
||||
/**
|
||||
* Returns the name of this option. Equivalent
|
||||
* to value used for filtering.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Returns the number of found results for this option.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCount();
|
||||
}
|
42
Classes/Connection/FacetRequestInterface.php
Normal file
42
Classes/Connection/FacetRequestInterface.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\Connection;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Used to request facets / aggregates from connection.
|
||||
*/
|
||||
interface FacetRequestInterface
|
||||
{
|
||||
/**
|
||||
* The identifier of the facet, used as key in arrays and to get the facet
|
||||
* from search request, etc.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier();
|
||||
|
||||
/**
|
||||
* The field to use for facet building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getField();
|
||||
}
|
29
Classes/Connection/ResultItemInterface.php
Normal file
29
Classes/Connection/ResultItemInterface.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\Connection;
|
||||
|
||||
/*
|
||||
* 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 ArrayAccess to enable retrieval of information in fluid.
|
||||
*/
|
||||
interface ResultItemInterface extends \ArrayAccess
|
||||
{
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Connection;
|
||||
namespace Codappix\SearchCore\Connection;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -31,4 +31,14 @@ interface SearchRequestInterface
|
|||
* @return string
|
||||
*/
|
||||
public function getSearchTerm();
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasFilter();
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFilter();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Connection;
|
||||
namespace Codappix\SearchCore\Connection;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -21,9 +21,35 @@ namespace Leonmrni\SearchCore\Connection;
|
|||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* A search result.
|
||||
*/
|
||||
interface SearchResultInterface extends \Iterator, \Countable, \ArrayAccess
|
||||
interface SearchResultInterface extends \Iterator, \Countable
|
||||
{
|
||||
/**
|
||||
* @return array<ResultItemInterface>
|
||||
*/
|
||||
public function getResults();
|
||||
|
||||
/**
|
||||
* Return all facets, if any.
|
||||
*
|
||||
* @return array<FacetIterface>
|
||||
*/
|
||||
public function getFacets();
|
||||
|
||||
/**
|
||||
* Returns the total sum of matching results.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalCount();
|
||||
|
||||
// Countable - Interface
|
||||
|
||||
/**
|
||||
* Returns the total sum of results contained in this result.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Controller;
|
||||
namespace Codappix\SearchCore\Controller;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,8 +20,8 @@ namespace Leonmrni\SearchCore\Controller;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Domain\Model\SearchRequest;
|
||||
use Leonmrni\SearchCore\Domain\Search\SearchService;
|
||||
use Codappix\SearchCore\Domain\Model\SearchRequest;
|
||||
use Codappix\SearchCore\Domain\Search\SearchService;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
|
||||
/**
|
||||
|
|
113
Classes/Domain/Index/AbstractIndexer.php
Normal file
113
Classes/Domain/Index/AbstractIndexer.php
Normal file
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\Domain\Index;
|
||||
|
||||
/*
|
||||
* 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\Connection\ConnectionInterface;
|
||||
|
||||
abstract class AbstractIndexer implements IndexerInterface
|
||||
{
|
||||
/**
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Log\Logger
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Inject log manager to get concrete logger from it.
|
||||
*
|
||||
* @param \TYPO3\CMS\Core\Log\LogManager $logManager
|
||||
*/
|
||||
public function injectLogger(\TYPO3\CMS\Core\Log\LogManager $logManager)
|
||||
{
|
||||
$this->logger = $logManager->getLogger(__CLASS__);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ConnectionInterface $connection
|
||||
*/
|
||||
public function __construct(ConnectionInterface $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
public function indexAllDocuments()
|
||||
{
|
||||
$this->logger->info('Start indexing');
|
||||
foreach ($this->getRecordGenerator() as $records) {
|
||||
$this->logger->debug('Index records.', [$records]);
|
||||
if ($records === null) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->connection->addDocuments($this->getDocumentName(), $records);
|
||||
}
|
||||
$this->logger->info('Finish indexing');
|
||||
}
|
||||
|
||||
public function indexDocument($identifier)
|
||||
{
|
||||
$this->logger->info('Start indexing single record.', [$identifier]);
|
||||
try {
|
||||
$this->connection->addDocument($this->getDocumentName(), $this->getRecord($identifier));
|
||||
} catch (NoRecordFoundException $e) {
|
||||
$this->logger->info('Could not index document.', [$e->getMessage()]);
|
||||
}
|
||||
$this->logger->info('Finish indexing');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Generator
|
||||
*/
|
||||
protected function getRecordGenerator()
|
||||
{
|
||||
$offset = 0;
|
||||
// TODO: Make configurable.
|
||||
$limit = 50;
|
||||
|
||||
while (($records = $this->getRecords($offset, $limit)) !== []) {
|
||||
yield $records;
|
||||
$offset += $limit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $offset
|
||||
* @param int $limit
|
||||
* @return array|null
|
||||
*/
|
||||
abstract protected function getRecords($offset, $limit);
|
||||
|
||||
/**
|
||||
* @param int $identifier
|
||||
* @return array
|
||||
* @throws NoRecordFoundException If record could not be found.
|
||||
*/
|
||||
abstract protected function getRecord($identifier);
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getDocumentName();
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Domain\Index;
|
||||
namespace Codappix\SearchCore\Domain\Index;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,6 +20,11 @@ namespace Leonmrni\SearchCore\Domain\Index;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Codappix\SearchCore\Configuration\InvalidArgumentException;
|
||||
use Codappix\SearchCore\Domain\Index\IndexerInterface;
|
||||
use Codappix\SearchCore\Domain\Index\TcaIndexer;
|
||||
use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService;
|
||||
use TYPO3\CMS\Core\SingletonInterface as Singleton;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||
|
||||
|
@ -34,27 +39,61 @@ class IndexerFactory implements Singleton
|
|||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* @param ObjectManagerInterface $objectManager
|
||||
* @var ConfigurationContainerInterface
|
||||
*/
|
||||
public function __construct(ObjectManagerInterface $objectManager)
|
||||
{
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* @param ObjectManagerInterface $objectManager
|
||||
* @param ConfigurationContainerInterface $configuration
|
||||
*/
|
||||
public function __construct(
|
||||
ObjectManagerInterface $objectManager,
|
||||
ConfigurationContainerInterface $configuration
|
||||
) {
|
||||
$this->objectManager = $objectManager;
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tableName
|
||||
* @param string $identifier
|
||||
*
|
||||
* @return IndexerInterface
|
||||
* @throws NoMatchingIndexer
|
||||
*/
|
||||
public function getIndexer($tableName)
|
||||
public function getIndexer($identifier)
|
||||
{
|
||||
// This is the place to use configuration to return different indexer.
|
||||
return $this->objectManager->get(
|
||||
TcaIndexer::Class,
|
||||
$this->objectManager->get(
|
||||
TcaIndexer\TcaTableService::class,
|
||||
$tableName
|
||||
)
|
||||
);
|
||||
try {
|
||||
return $this->buildIndexer($this->configuration->get('indexing.' . $identifier . '.indexer'), $identifier);
|
||||
} catch (NoMatchingIndexerException $e) {
|
||||
// Nothing to do, we throw exception below
|
||||
} catch (InvalidArgumentException $e) {
|
||||
// Nothing to do, we throw exception below
|
||||
}
|
||||
|
||||
throw new NoMatchingIndexerException('Could not find an indexer for ' . $identifier, 1497341442);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $indexerClass
|
||||
* @param string $identifier
|
||||
*
|
||||
* @return IndexerInterface
|
||||
* @throws NoMatchingIndexer
|
||||
*/
|
||||
protected function buildIndexer($indexerClass, $identifier)
|
||||
{
|
||||
if ($indexerClass === TcaIndexer::class) {
|
||||
return $this->objectManager->get(
|
||||
TcaIndexer::class,
|
||||
$this->objectManager->get(TcaTableService::class, $identifier)
|
||||
);
|
||||
}
|
||||
|
||||
if (class_exists($indexerClass) && in_array(IndexerInterface::class, class_implements($indexerClass))) {
|
||||
return $this->objectManager->get($indexerClass);
|
||||
}
|
||||
|
||||
throw new NoMatchingIndexerException('Could not find indexer: ' . $indexerClass, 1497341442);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Domain\Index;
|
||||
namespace Codappix\SearchCore\Domain\Index;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Domain\Index;
|
||||
namespace Codappix\SearchCore\Domain\Index;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
|
25
Classes/Domain/Index/NoMatchingIndexerException.php
Normal file
25
Classes/Domain/Index/NoMatchingIndexerException.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\Domain\Index;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
class NoMatchingIndexerException extends IndexingException
|
||||
{
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Domain\Index;
|
||||
namespace Codappix\SearchCore\Domain\Index;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Domain\Index;
|
||||
namespace Codappix\SearchCore\Domain\Index;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,39 +20,18 @@ namespace Leonmrni\SearchCore\Domain\Index;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||
use Leonmrni\SearchCore\Connection\ConnectionInterface;
|
||||
use Codappix\SearchCore\Connection\ConnectionInterface;
|
||||
|
||||
/**
|
||||
* Will index the given table using configuration from TCA.
|
||||
*/
|
||||
class TcaIndexer implements IndexerInterface
|
||||
class TcaIndexer extends AbstractIndexer
|
||||
{
|
||||
/**
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* @var TcaIndexer\TcaTableService
|
||||
*/
|
||||
protected $tcaTableService;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Log\Logger
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Inject log manager to get concrete logger from it.
|
||||
*
|
||||
* @param \TYPO3\CMS\Core\Log\LogManager $logManager
|
||||
*/
|
||||
public function injectLogger(\TYPO3\CMS\Core\Log\LogManager $logManager)
|
||||
{
|
||||
$this->logger = $logManager->getLogger(__CLASS__);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TcaIndexer\TcaTableService $tcaTableService
|
||||
* @param ConnectionInterface $connection
|
||||
|
@ -65,46 +44,6 @@ class TcaIndexer implements IndexerInterface
|
|||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
public function indexAllDocuments()
|
||||
{
|
||||
$this->logger->info('Start indexing');
|
||||
foreach ($this->getRecordGenerator() as $records) {
|
||||
$this->logger->debug('Index records.', [$records]);
|
||||
if ($records === null) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->connection->addDocuments($this->tcaTableService->getTableName(), $records);
|
||||
}
|
||||
$this->logger->info('Finish indexing');
|
||||
}
|
||||
|
||||
public function indexDocument($identifier)
|
||||
{
|
||||
$this->logger->info('Start indexing single record.', [$identifier]);
|
||||
try {
|
||||
$this->connection->addDocument($this->tcaTableService->getTableName(), $this->getRecord($identifier));
|
||||
} catch (NoRecordFoundException $e) {
|
||||
$this->logger->info('Could not index document.', [$e->getMessage()]);
|
||||
}
|
||||
$this->logger->info('Finish indexing');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Generator
|
||||
*/
|
||||
protected function getRecordGenerator()
|
||||
{
|
||||
$offset = 0;
|
||||
// TODO: Make configurable.
|
||||
$limit = 50;
|
||||
|
||||
while (($records = $this->getRecords($offset, $limit)) !== []) {
|
||||
yield $records;
|
||||
$offset += $limit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $offset
|
||||
* @param int $limit
|
||||
|
@ -157,6 +96,14 @@ class TcaIndexer implements IndexerInterface
|
|||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getDocumentName()
|
||||
{
|
||||
return $this->tcaTableService->getTableName();
|
||||
}
|
||||
|
||||
protected function getDatabaseConnection()
|
||||
{
|
||||
return GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Domain\Index\TcaIndexer;
|
||||
namespace Codappix\SearchCore\Domain\Index\TcaIndexer;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Domain\Index\TcaIndexer;
|
||||
namespace Codappix\SearchCore\Domain\Index\TcaIndexer;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Domain\Index\TcaIndexer;
|
||||
namespace Codappix\SearchCore\Domain\Index\TcaIndexer;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,8 +20,8 @@ namespace Leonmrni\SearchCore\Domain\Index\TcaIndexer;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Leonmrni\SearchCore\Domain\Index\IndexingException;
|
||||
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Codappix\SearchCore\Domain\Index\IndexingException;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
|
@ -151,7 +151,7 @@ class TcaTableService
|
|||
. ' AND pages.no_search = 0'
|
||||
;
|
||||
|
||||
$userDefinedWhere = $this->configuration->getIfExists('indexer.tca.' . $this->tableName . '.additionalWhereClause');
|
||||
$userDefinedWhere = $this->configuration->getIfExists('indexing.' . $this->getTableName() . '.additionalWhereClause');
|
||||
if (is_string($userDefinedWhere)) {
|
||||
$whereClause .= ' AND ' . $userDefinedWhere;
|
||||
}
|
||||
|
@ -269,7 +269,7 @@ class TcaTableService
|
|||
*/
|
||||
protected function isBlackListedRootLineConfigured()
|
||||
{
|
||||
return (bool) $this->configuration->getIfExists('indexer.tca.rootLineBlacklist');
|
||||
return (bool) $this->configuration->getIfExists('indexing.' . $this->getTableName() . '.rootLineBlacklist');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -279,6 +279,6 @@ class TcaTableService
|
|||
*/
|
||||
protected function getBlackListedRootLine()
|
||||
{
|
||||
return GeneralUtility::intExplode(',', $this->configuration->getIfExists('indexer.tca.rootLineBlacklist'));
|
||||
return GeneralUtility::intExplode(',', $this->configuration->getIfExists('indexing.' . $this->getTableName() . '.rootLineBlacklist'));
|
||||
}
|
||||
}
|
||||
|
|
66
Classes/Domain/Model/FacetRequest.php
Normal file
66
Classes/Domain/Model/FacetRequest.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\Domain\Model;
|
||||
|
||||
/*
|
||||
* 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\Connection\FacetRequestInterface;
|
||||
|
||||
class FacetRequest implements FacetRequestInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $identifier = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $field = '';
|
||||
|
||||
/**
|
||||
* TODO: Add validation / exception?
|
||||
* As the facets come from configuration this might be a good idea to help
|
||||
* integrators find issues.
|
||||
*
|
||||
* @param string $identifier
|
||||
* @param string $field
|
||||
*/
|
||||
public function __construct($identifier, $field)
|
||||
{
|
||||
$this->identifier = $identifier;
|
||||
$this->field = $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getField()
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Domain\Model;
|
||||
namespace Codappix\SearchCore\Domain\Model;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,7 +20,8 @@ namespace Leonmrni\SearchCore\Domain\Model;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Connection\SearchRequestInterface;
|
||||
use Codappix\SearchCore\Connection\FacetRequestInterface;
|
||||
use Codappix\SearchCore\Connection\SearchRequestInterface;
|
||||
|
||||
/**
|
||||
* Represents a search request used to process an actual search.
|
||||
|
@ -32,14 +33,24 @@ class SearchRequest implements SearchRequestInterface
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $query;
|
||||
protected $query = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $filter = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $facets = [];
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
*/
|
||||
public function __construct($query)
|
||||
{
|
||||
$this->query = $query;
|
||||
$this->query = (string) $query;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,4 +68,48 @@ class SearchRequest implements SearchRequestInterface
|
|||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $filter
|
||||
*/
|
||||
public function setFilter(array $filter)
|
||||
{
|
||||
$this->filter = array_filter(array_map('strval', $filter));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasFilter()
|
||||
{
|
||||
return count($this->filter) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFilter()
|
||||
{
|
||||
return $this->filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a facet to gather in this search request.
|
||||
*
|
||||
* @param FacetRequestInterface $facet
|
||||
*/
|
||||
public function addFacet(FacetRequestInterface $facet)
|
||||
{
|
||||
$this->facets[$facet->getIdentifier()] = $facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all configured facets to fetch in this search request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFacets()
|
||||
{
|
||||
return $this->facets;
|
||||
}
|
||||
}
|
||||
|
|
138
Classes/Domain/Search/QueryFactory.php
Normal file
138
Classes/Domain/Search/QueryFactory.php
Normal file
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\Domain\Search;
|
||||
|
||||
/*
|
||||
* 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\Connection\ConnectionInterface;
|
||||
use Codappix\SearchCore\Connection\Elasticsearch\Query;
|
||||
use Codappix\SearchCore\Connection\SearchRequestInterface;
|
||||
use TYPO3\CMS\Extbase\Utility\ArrayUtility;
|
||||
|
||||
class QueryFactory
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Log\Logger
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $query = [];
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Core\Log\LogManager $logManager
|
||||
*/
|
||||
public function __construct(\TYPO3\CMS\Core\Log\LogManager $logManager)
|
||||
{
|
||||
$this->logger = $logManager->getLogger(__CLASS__);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SearchRequestInterface $searchRequest
|
||||
*
|
||||
* @return \Elastica\Query
|
||||
*/
|
||||
public function create(SearchRequestInterface $searchRequest)
|
||||
{
|
||||
return $this->createElasticaQuery($searchRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SearchRequestInterface $searchRequest
|
||||
*
|
||||
* TODO: This is not in scope Elasticsearch, therefore should not return elastica.
|
||||
* @return \Elastica\Query
|
||||
*/
|
||||
protected function createElasticaQuery(SearchRequestInterface $searchRequest)
|
||||
{
|
||||
$this->addSearch($searchRequest);
|
||||
$this->addFilter($searchRequest);
|
||||
$this->addFacets($searchRequest);
|
||||
|
||||
$this->logger->debug('Generated elasticsearch query.', [$this->query]);
|
||||
return new \Elastica\Query($this->query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SearchRequestInterface $searchRequest
|
||||
*/
|
||||
protected function addSearch(SearchRequestInterface $searchRequest)
|
||||
{
|
||||
$this->query = ArrayUtility::arrayMergeRecursiveOverrule($this->query, [
|
||||
'query' => [
|
||||
'bool' => [
|
||||
'must' => [
|
||||
[
|
||||
'match' => [
|
||||
'_all' => $searchRequest->getSearchTerm()
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SearchRequestInterface $searchRequest
|
||||
*/
|
||||
protected function addFilter(SearchRequestInterface $searchRequest)
|
||||
{
|
||||
if (! $searchRequest->hasFilter()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$terms = [];
|
||||
foreach ($searchRequest->getFilter() as $name => $value) {
|
||||
$terms[] = [
|
||||
'term' => [
|
||||
$name => $value,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$this->query = ArrayUtility::arrayMergeRecursiveOverrule($this->query, [
|
||||
'query' => [
|
||||
'bool' => [
|
||||
'filter' => $terms,
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SearchRequestInterface $searchRequest
|
||||
*/
|
||||
protected function addFacets(SearchRequestInterface $searchRequest)
|
||||
{
|
||||
foreach ($searchRequest->getFacets() as $facet) {
|
||||
$this->query = ArrayUtility::arrayMergeRecursiveOverrule($this->query, [
|
||||
'aggs' => [
|
||||
$facet->getIdentifier() => [
|
||||
'terms' => [
|
||||
'field' => $facet->getField(),
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Domain\Search;
|
||||
namespace Codappix\SearchCore\Domain\Search;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,10 +20,13 @@ namespace Leonmrni\SearchCore\Domain\Search;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Connection\ConnectionInterface;
|
||||
use Leonmrni\SearchCore\Connection\SearchRequestInterface;
|
||||
use Leonmrni\SearchCore\Connection\SearchResultInterface;
|
||||
use Leonmrni\SearchCore\Domain\Model\SearchRequest;
|
||||
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Codappix\SearchCore\Configuration\InvalidArgumentException;
|
||||
use Codappix\SearchCore\Connection\ConnectionInterface;
|
||||
use Codappix\SearchCore\Connection\SearchRequestInterface;
|
||||
use Codappix\SearchCore\Connection\SearchResultInterface;
|
||||
use Codappix\SearchCore\Domain\Model\FacetRequest;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||
|
||||
/**
|
||||
* Service to process a search request.
|
||||
|
@ -36,11 +39,28 @@ class SearchService
|
|||
protected $connection;
|
||||
|
||||
/**
|
||||
* @param ConnectionInterface $connection
|
||||
* @var ConfigurationContainerInterface
|
||||
*/
|
||||
public function __construct(ConnectionInterface $connection)
|
||||
{
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* @var ObjectManagerInterface
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* @param ConnectionInterface $connection
|
||||
* @param ConfigurationContainerInterface $configuration
|
||||
* @param ObjectManagerInterface $objectManager
|
||||
*/
|
||||
public function __construct(
|
||||
ConnectionInterface $connection,
|
||||
ConfigurationContainerInterface $configuration,
|
||||
ObjectManagerInterface $objectManager
|
||||
) {
|
||||
$this->connection = $connection;
|
||||
$this->configuration = $configuration;
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,6 +69,34 @@ class SearchService
|
|||
*/
|
||||
public function search(SearchRequestInterface $searchRequest)
|
||||
{
|
||||
$this->addConfiguredFacets($searchRequest);
|
||||
|
||||
return $this->connection->search($searchRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add facets from configuration to request.
|
||||
*
|
||||
* @param SearchRequestInterface $searchRequest
|
||||
*/
|
||||
protected function addConfiguredFacets(SearchRequestInterface $searchRequest)
|
||||
{
|
||||
$facetsConfig = $this->configuration->getIfExists('searching.facets');
|
||||
if ($facetsConfig === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
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(
|
||||
FacetRequest::class,
|
||||
$identifier,
|
||||
$facetConfig['field']
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Domain\Service;
|
||||
namespace Codappix\SearchCore\Domain\Service;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,7 +20,10 @@ namespace Leonmrni\SearchCore\Domain\Service;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Codappix\SearchCore\Domain\Index\IndexerFactory;
|
||||
use Codappix\SearchCore\Domain\Index\NoMatchingIndexerException;
|
||||
use Codappix\SearchCore\Domain\Index\TcaIndexer;
|
||||
use TYPO3\CMS\Core\SingletonInterface as Singleton;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
|
@ -40,14 +43,13 @@ class DataHandler implements Singleton
|
|||
/**
|
||||
* TODO: Only inject on first use?!
|
||||
*
|
||||
* @var \Leonmrni\SearchCore\Connection\ConnectionInterface
|
||||
* @var \Codappix\SearchCore\Connection\ConnectionInterface
|
||||
* @inject
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* @var \Leonmrni\SearchCore\Domain\Index\IndexerFactory
|
||||
* @inject
|
||||
* @var IndexerFactory
|
||||
*/
|
||||
protected $indexerFactory;
|
||||
|
||||
|
@ -73,20 +75,12 @@ class DataHandler implements Singleton
|
|||
|
||||
/**
|
||||
* @param ConfigurationContainerInterface $configuration
|
||||
* @param IndexerFactory $indexerFactory
|
||||
*/
|
||||
public function __construct(ConfigurationContainerInterface $configuration)
|
||||
public function __construct(ConfigurationContainerInterface $configuration, IndexerFactory $indexerFactory)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all tables that are allowed for indexing.
|
||||
*
|
||||
* @return array<String>
|
||||
*/
|
||||
public function getAllowedTablesForIndexing()
|
||||
{
|
||||
return GeneralUtility::trimExplode(',', $this->configuration->get('indexer.tca.allowedTables'));
|
||||
$this->indexerFactory = $indexerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,7 +90,7 @@ class DataHandler implements Singleton
|
|||
public function add($table, array $record)
|
||||
{
|
||||
$this->logger->debug('Record received for add.', [$table, $record]);
|
||||
$this->indexerFactory->getIndexer($table)->indexDocument($record['uid']);
|
||||
$this->getIndexer($table)->indexDocument($record['uid']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -105,7 +99,7 @@ class DataHandler implements Singleton
|
|||
public function update($table, array $record)
|
||||
{
|
||||
$this->logger->debug('Record received for update.', [$table, $record]);
|
||||
$this->indexerFactory->getIndexer($table)->indexDocument($record['uid']);
|
||||
$this->getIndexer($table)->indexDocument($record['uid']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,4 +111,31 @@ class DataHandler implements Singleton
|
|||
$this->logger->debug('Record received for delete.', [$table, $identifier]);
|
||||
$this->connection->deleteDocument($table, $identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @return IndexerInterface
|
||||
*
|
||||
* @throws NoMatchingIndexerException
|
||||
*/
|
||||
protected function getIndexer($table)
|
||||
{
|
||||
return $this->indexerFactory->getIndexer($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @return bool
|
||||
*/
|
||||
public function canHandle($table)
|
||||
{
|
||||
try {
|
||||
$this->getIndexer($table);
|
||||
return true;
|
||||
} catch (NoMatchingIndexerException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Hook;
|
||||
namespace Codappix\SearchCore\Hook;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,8 +20,9 @@ namespace Leonmrni\SearchCore\Hook;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Configuration\NoConfigurationException;
|
||||
use Leonmrni\SearchCore\Domain\Service\DataHandler as OwnDataHandler;
|
||||
use Codappix\SearchCore\Configuration\NoConfigurationException;
|
||||
use Codappix\SearchCore\Domain\Index\NoMatchingIndexerException;
|
||||
use Codappix\SearchCore\Domain\Service\DataHandler as OwnDataHandler;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\DataHandling\DataHandler as CoreDataHandler;
|
||||
use TYPO3\CMS\Core\Log\LogManager;
|
||||
|
@ -140,7 +141,7 @@ class DataHandler implements Singleton
|
|||
$this->logger->debug('Datahandler could not be setup.');
|
||||
return false;
|
||||
}
|
||||
if (! $this->shouldProcessTable($table)) {
|
||||
if (! $this->dataHandler->canHandle($table)) {
|
||||
$this->logger->debug('Table is not allowed.', [$table]);
|
||||
return false;
|
||||
}
|
||||
|
@ -148,15 +149,6 @@ class DataHandler implements Singleton
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @return bool
|
||||
*/
|
||||
protected function shouldProcessTable($table)
|
||||
{
|
||||
return in_array($table, $this->dataHandler->getAllowedTablesForIndexing());
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper to allow unit testing.
|
||||
*
|
||||
|
|
24
Configuration/TypoScript/constants.txt
Executable file → Normal file
24
Configuration/TypoScript/constants.txt
Executable file → Normal file
|
@ -8,20 +8,16 @@ plugin {
|
|||
}
|
||||
}
|
||||
|
||||
indexer {
|
||||
tca {
|
||||
# Pages are not supported yet, see
|
||||
# https://github.com/DanielSiepmann/search_core/issues/24 but
|
||||
# should also be added, together with additionalWhereClause
|
||||
# based on doktypes
|
||||
allowedTables = tt_content
|
||||
|
||||
tt_content {
|
||||
additionalWhereClause (
|
||||
pages.doktype NOT IN (3, 199)
|
||||
AND tt_content.CType NOT IN ('gridelements_pi1', 'list', 'div', 'menu', 'shortcut', 'search', 'login')
|
||||
)
|
||||
}
|
||||
indexing {
|
||||
# Pages are not supported yet, see
|
||||
# https://github.com/DanielSiepmann/search_core/issues/24 but
|
||||
# should also be added, together with additionalWhereClause
|
||||
# based on doktypes
|
||||
tt_content {
|
||||
additionalWhereClause (
|
||||
pages.doktype NOT IN (3, 199)
|
||||
AND tt_content.CType NOT IN ('gridelements_pi1', 'list', 'div', 'menu', 'shortcut', 'search', 'login')
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
11
Configuration/TypoScript/setup.txt
Executable file → Normal file
11
Configuration/TypoScript/setup.txt
Executable file → Normal file
|
@ -8,13 +8,10 @@ plugin {
|
|||
}
|
||||
}
|
||||
|
||||
indexer {
|
||||
tca {
|
||||
allowedTables = {$plugin.tx_searchcore.settings.indexer.tca.allowedTables}
|
||||
|
||||
tt_content {
|
||||
additionalWhereClause = {$plugin.tx_searchcore.settings.indexer.tca.tt_content.additionalWhereClause}
|
||||
}
|
||||
indexing {
|
||||
tt_content {
|
||||
indexer = Codappix\SearchCore\Domain\Index\TcaIndexer
|
||||
additionalWhereClause = {$plugin.tx_searchcore.settings.indexing.tt_content.additionalWhereClause}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ Currently only :ref:`Elasticsearch` is provided.
|
|||
Indexing
|
||||
--------
|
||||
|
||||
The indexing is done by one of the available indexer. It should be possible to define the indexer to
|
||||
use for certein document types. Also it should be possible to write custom indexer to use.
|
||||
The indexing is done by one of the available indexer. For each identifier it's possible to define
|
||||
the indexer to use. Also it's possible to write custom indexer to use.
|
||||
|
||||
Currently only the :ref:`TcaIndexer` is provided.
|
||||
|
|
|
@ -120,7 +120,7 @@ html_theme = 'alabaster'
|
|||
# documentation.
|
||||
html_theme_options = {
|
||||
'description': 'TYPO3 Extension to integrate search services.',
|
||||
'github_user': 'DanielSiepmann',
|
||||
'github_user': 'Codappix',
|
||||
'github_repo': 'search_core',
|
||||
'github_button': True,
|
||||
'github_banner': True,
|
||||
|
@ -306,7 +306,7 @@ intersphinx_mapping = {
|
|||
't3tcaref': ('https://docs.typo3.org/typo3cms/TCAReference/', None),
|
||||
}
|
||||
extlinks = {
|
||||
'project': ('https://github.com/DanielSiepmann/search_core/projects/%s', 'Github project: '),
|
||||
'pr': ('https://github.com/DanielSiepmann/search_core/pull/%s', 'Github pull request: '),
|
||||
'issue': ('https://github.com/DanielSiepmann/search_core/issues/%s', 'Github issue: '),
|
||||
'project': ('https://github.com/Codappix/search_core/projects/%s', 'Github project: '),
|
||||
'pr': ('https://github.com/Codappix/search_core/pull/%s', 'Github pull request: '),
|
||||
'issue': ('https://github.com/Codappix/search_core/issues/%s', 'Github issue: '),
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ Options
|
|||
|
||||
The following section contains the different options, e.g. for :ref:`connections` and
|
||||
:ref:`indexer`: ``plugin.tx_searchcore.settings.connection`` or
|
||||
``plugin.tx_searchcore.settings.index``.
|
||||
``plugin.tx_searchcore.settings.indexing``.
|
||||
|
||||
.. _configuration_options_connection:
|
||||
|
||||
|
@ -96,8 +96,8 @@ The following settings are available. For each setting its documented which conn
|
|||
|
||||
.. _configuration_options_index:
|
||||
|
||||
index
|
||||
^^^^^
|
||||
Indexing
|
||||
^^^^^^^^
|
||||
|
||||
Holds settings regarding the indexing, e.g. of TYPO3 records, to search services.
|
||||
|
||||
|
@ -106,8 +106,9 @@ Configured as::
|
|||
plugin {
|
||||
tx_searchcore {
|
||||
settings {
|
||||
indexer {
|
||||
indexerName {
|
||||
indexing {
|
||||
identifier {
|
||||
indexer = FullyQualifiedClassname
|
||||
// the settings
|
||||
}
|
||||
}
|
||||
|
@ -115,26 +116,10 @@ Configured as::
|
|||
}
|
||||
}
|
||||
|
||||
Where ``indexerName`` is one of the available :ref:`indexer`.
|
||||
Where ``identifier`` is up to you, but should match table names to make :ref:`TcaIndexer` work.
|
||||
|
||||
The following settings are available. For each setting its documented which indexer consumes it.
|
||||
|
||||
.. _allowedTables:
|
||||
|
||||
``allowedTables``
|
||||
"""""""""""""""""
|
||||
|
||||
Used by: :ref:`TcaIndexer`.
|
||||
|
||||
Defines which TYPO3 tables are allowed to be indexed. Only white listed tables will be processed
|
||||
through Command Line Interface and Hooks.
|
||||
|
||||
Contains a comma separated list of table names. Spaces are trimmed.
|
||||
|
||||
Example::
|
||||
|
||||
plugin.tx_searchcore.settings.indexer.tca.allowedTables = tt_content, fe_users
|
||||
|
||||
.. _rootLineBlacklist:
|
||||
|
||||
``rootLineBlacklist``
|
||||
|
@ -151,7 +136,7 @@ The following settings are available. For each setting its documented which inde
|
|||
|
||||
Example::
|
||||
|
||||
plugin.tx_searchcore.settings.index.tca.rootLineBlacklist = 3, 10, 100
|
||||
plugin.tx_searchcore.settings.indexing.<identifier>.rootLineBlacklist = 3, 10, 100
|
||||
|
||||
Also it's possible to define some behaviour for the different document types. In context of TYPO3
|
||||
tables are used as document types 1:1. It's possible to configure different tables. The following
|
||||
|
@ -170,9 +155,57 @@ options are available:
|
|||
|
||||
Example::
|
||||
|
||||
plugin.tx_searchcore.settings.index.tca.tt_content.additionalWhereClause = tt_content.CType NOT IN ('gridelements_pi1', 'list', 'div', 'menu')
|
||||
plugin.tx_searchcore.settings.indexing.<identifier>.additionalWhereClause = tt_content.CType NOT IN ('gridelements_pi1', 'list', 'div', 'menu')
|
||||
|
||||
.. attention::
|
||||
|
||||
Make sure to prefix all fields with the corresponding table name. The selection from
|
||||
database will contain joins and can lead to SQL errors if a field exists in multiple tables.
|
||||
|
||||
.. _mapping:
|
||||
|
||||
``mapping``
|
||||
"""""""""""
|
||||
|
||||
Used by: Elasticsearch connection while indexing.
|
||||
|
||||
Define mapping for Elasticsearch, have a look at the official docs: https://www.elastic.co/guide/en/elasticsearch/reference/5.2/mapping.html
|
||||
You are able to define the mapping for each property / columns.
|
||||
|
||||
Example::
|
||||
|
||||
plugin.tx_searchcore.settings.indexing.tt_content.mapping {
|
||||
CType {
|
||||
type = keyword
|
||||
}
|
||||
}
|
||||
|
||||
The above example will define the ``CType`` field of ``tt_content`` as ``type: keyword``. This
|
||||
makes building a facet possible.
|
||||
|
||||
|
||||
.. _configuration_options_search:
|
||||
|
||||
Searching
|
||||
^^^^^^^^^
|
||||
|
||||
.. _facets:
|
||||
|
||||
``facets``
|
||||
"""""""""""
|
||||
|
||||
Used by: Elasticsearch connection while building search query.
|
||||
|
||||
Define aggregations for Elasticsearch, have a look at the official docs: https://www.elastic.co/guide/en/elasticsearch/reference/5.2/search-aggregations-bucket-terms-aggregation.html
|
||||
Currently only the term facet is provided.
|
||||
|
||||
Example::
|
||||
|
||||
plugin.tx_searchcore.settings.searching.facets {
|
||||
contentTypes {
|
||||
field = CType
|
||||
}
|
||||
}
|
||||
|
||||
The above example will provide a facet with options for all found ``CType`` results together
|
||||
with a count.
|
||||
|
|
|
@ -22,5 +22,9 @@ The connection is configurable through the following options:
|
|||
|
||||
* :ref:`port`
|
||||
|
||||
* :ref:`mapping`
|
||||
|
||||
* :ref:`facets`
|
||||
|
||||
.. _elastic Elasticsearch: https://www.elastic.co/products/elasticsearch
|
||||
.. _elastica: http://elastica.io/
|
||||
|
|
|
@ -32,9 +32,10 @@ Then setup your system::
|
|||
|
||||
git clone git@github.com:DanielSiepmann/search_core.git \
|
||||
&& cd search_core \
|
||||
&& export typo3DatabaseName="searchcoretest62" \
|
||||
&& export TYPO3_VERSION="~6.2" \
|
||||
&& export typo3DatabaseName="searchcoretest76" \
|
||||
&& export TYPO3_VERSION="~7.6" \
|
||||
&& make install \
|
||||
&& make unitTests \
|
||||
&& make functionalTests
|
||||
|
||||
If all tests are okay, start your work.
|
||||
|
@ -42,8 +43,8 @@ If all tests are okay, start your work.
|
|||
If you are working with multiple TYPO3 versions make sure to export `typo3DatabaseName` and
|
||||
`TYPO3_VERSION` in your environment like::
|
||||
|
||||
export typo3DatabaseName="searchcoretest76"
|
||||
export TYPO3_VERSION="~7.6"
|
||||
export typo3DatabaseName="searchcoretest62"
|
||||
export TYPO3_VERSION="~6.2"
|
||||
|
||||
Also run the install command for each version before running any tests. Only this will make sure you
|
||||
are testing against the actual TYPO3 Version and database scheme.
|
||||
|
|
38
Documentation/source/features.rst
Normal file
38
Documentation/source/features.rst
Normal file
|
@ -0,0 +1,38 @@
|
|||
.. _features:
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
The following features are currently provided:
|
||||
|
||||
.. _features_indexing:
|
||||
|
||||
Indexing
|
||||
--------
|
||||
|
||||
Indexing data to Elasticsearch is provided. The extension delivers an indexer for TCA with zero
|
||||
configuration needs. Still it's possible to configure the indexer.
|
||||
|
||||
Also custom classes can be used as indexers.
|
||||
|
||||
.. _features_search:
|
||||
|
||||
Searching
|
||||
---------
|
||||
|
||||
Currently all fields are searched for a single search input.
|
||||
|
||||
Also multiple filter are supported. Filtering results by fields for string contents.
|
||||
|
||||
Even facets / aggregates are now possible. Therefore a mapping has to be defined in TypoScript for
|
||||
indexing, and the facets itself while searching.
|
||||
|
||||
.. _features_planned:
|
||||
|
||||
Planned
|
||||
---------
|
||||
|
||||
The following features are currently planned and will be integrated:
|
||||
|
||||
#. Pagination
|
||||
Add a pagination to search results, to allow users to walk through all results.
|
|
@ -7,6 +7,7 @@ Table of Contents
|
|||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
features
|
||||
installation
|
||||
configuration
|
||||
usage
|
||||
|
|
|
@ -6,7 +6,7 @@ Installation
|
|||
|
||||
The extension can be installed through composer::
|
||||
|
||||
composer require "leonmrni/search_core dev-feature/integrate-elasticsearch"
|
||||
composer require "leonmrni/search_core dev-master as 1.0.x-dev"
|
||||
|
||||
or by `downloading`_ and placing it inside the :file:`typo3conf/ext`-Folder of your installation.
|
||||
In that case you need to install all dependencies yourself. Dependencies are:
|
||||
|
@ -16,8 +16,7 @@ In that case you need to install all dependencies yourself. Dependencies are:
|
|||
:lines: 19-21
|
||||
:dedent: 8
|
||||
|
||||
|
||||
Afterwards you need to enable the extension through the extension manager and include the static
|
||||
typoscript setup.
|
||||
TypoScript setup.
|
||||
|
||||
.. _downloading: https://github.com/DanielSiepmann/search_core/archive/feature/integrate-elasticsearch.zip
|
||||
.. _downloading: https://github.com/DanielSiepmann/search_core/archive/master.zip
|
||||
|
|
|
@ -18,14 +18,14 @@ reindexing.
|
|||
Current state
|
||||
-------------
|
||||
|
||||
This is still a very early alpha version. More information can be taken from Github at
|
||||
This is still a very early beta version. More information can be taken from Github at
|
||||
`current issues`_ and `current projects`_.
|
||||
|
||||
We are also focusing on Code Quality and Testing through `travis ci`_, `scrutinizer`_ and `codacy`_.
|
||||
|
||||
.. _current issues: https://github.com/DanielSiepmann/search_core/issues
|
||||
.. _current projects: https://github.com/DanielSiepmann/search_core/projects
|
||||
.. _travis ci: https://travis-ci.org/DanielSiepmann/search_core
|
||||
.. _scrutinizer: https://scrutinizer-ci.com/g/DanielSiepmann/search_core/inspections
|
||||
.. _codacy: https://www.codacy.com/app/daniel-siepmann/search_core/dashboard
|
||||
.. _current issues: https://github.com/Codappix/search_core/issues
|
||||
.. _current projects: https://github.com/Codappix/search_core/projects
|
||||
.. _travis ci: https://travis-ci.org/Codappix/search_core
|
||||
.. _scrutinizer: https://scrutinizer-ci.com/g/Codappix/search_core/inspections
|
||||
.. _codacy: https://www.codacy.com/app/Codappix/search_core/dashboard
|
||||
|
||||
|
|
|
@ -11,12 +11,12 @@ Manual indexing
|
|||
|
||||
You can trigger indexing from CLI::
|
||||
|
||||
./typo3/cli_dispatch.phpsh extbase index:index --table 'tt_content'
|
||||
./typo3/cli_dispatch.phpsh extbase index:index --identifier 'tt_content'
|
||||
|
||||
This will index the table ``tt_content`` using the :ref:`TcaIndexer`.
|
||||
|
||||
Only one table per call is available, to index multiple tables just make multiple calls.
|
||||
The tables have to be white listed through :ref:`allowedTables` option.
|
||||
Only one index per call is available, to run multiple indexers, just make multiple calls.
|
||||
The indexers have to be defined in TypoScript via :ref:`configuration_options_index`.
|
||||
|
||||
.. _usage_auto_indexing:
|
||||
|
||||
|
@ -24,7 +24,7 @@ Auto indexing
|
|||
-------------
|
||||
|
||||
Indexing is done through hooks every time an record is changed.
|
||||
The tables have to be white listed through :ref:`allowedTables` option.
|
||||
The tables have to be configured via :ref:`configuration_options_index`.
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -37,3 +37,48 @@ Searching / Frontend Plugin
|
|||
|
||||
To provide a search interface you can insert the frontend Plugin as normal content element of type
|
||||
plugin. The plugin is named *Search Core*.
|
||||
|
||||
Please provide your own template, the extension will not deliver a useful template for now.
|
||||
|
||||
The extbase mapping is used, this way you can create a form:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<f:form name="searchRequest" object="{searchRequest}">
|
||||
<f:form.textfield property="query" />
|
||||
<f:form.submit value="search" />
|
||||
</f:form>
|
||||
|
||||
.. _usage_searching_filter:
|
||||
|
||||
Filter
|
||||
""""""
|
||||
|
||||
Thanks to extbase mapping, filter are added to the form:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<!-- Case sensitive for fields of type keyword. -->
|
||||
<f:form.textfield property="filter.exampleName" value="the value to match" />
|
||||
|
||||
.. _usage_searching_facets:
|
||||
|
||||
Facets
|
||||
""""""
|
||||
|
||||
To add a facet as criteria for searching, use :ref:`usage_searching_filter`.
|
||||
|
||||
To display facet results use:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<f:for each="{searchResult.facets}" as="facet">
|
||||
<f:for each="{facet.options}" as="option">
|
||||
<label for="{option.name}-desktop">
|
||||
<f:form.checkbox value="{option.name}" property="filter.{facet.field}" />
|
||||
{f:translate(id: 'search.filter.channel.{option.name}', default: option.name, extensionName: 'SitePackage')}
|
||||
({option.count})
|
||||
</label>
|
||||
</f:for>
|
||||
</f:for>
|
||||
|
||||
|
|
5
Makefile
5
Makefile
|
@ -25,6 +25,11 @@ functionalTests:
|
|||
--stop-on-error \
|
||||
-c Tests/Functional/FunctionalTests.xml
|
||||
|
||||
unitTests:
|
||||
TYPO3_PATH_WEB=$(TYPO3_WEB_DIR) \
|
||||
.Build/bin/phpunit --colors --debug -v \
|
||||
-c Tests/Unit/UnitTests.xml
|
||||
|
||||
uploadCodeCoverage: uploadCodeCoverageToScrutinizer uploadCodeCoverageToCodacy
|
||||
|
||||
uploadCodeCoverageToScrutinizer:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Tests\Functional;
|
||||
namespace Codappix\SearchCore\Tests\Functional;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Tests\Functional\Connection\Elasticsearch;
|
||||
namespace Codappix\SearchCore\Tests\Functional\Connection\Elasticsearch;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,7 +20,7 @@ namespace Leonmrni\SearchCore\Tests\Functional\Connection\Elasticsearch;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Tests\Functional\AbstractFunctionalTestCase as BaseFunctionalTestCase;
|
||||
use Codappix\SearchCore\Tests\Functional\AbstractFunctionalTestCase as BaseFunctionalTestCase;
|
||||
|
||||
/**
|
||||
* All functional tests should extend this base class.
|
||||
|
@ -43,11 +43,19 @@ abstract class AbstractFunctionalTestCase extends BaseFunctionalTestCase
|
|||
'host' => getenv('ES_HOST') ?: \Elastica\Connection::DEFAULT_HOST,
|
||||
'port' => getenv('ES_PORT') ?: \Elastica\Connection::DEFAULT_PORT,
|
||||
]);
|
||||
|
||||
// Start with clean system for test.
|
||||
$this->cleanUp();
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
// Delete everything so next test starts clean.
|
||||
// Make system clean again.
|
||||
$this->cleanUp();
|
||||
}
|
||||
|
||||
protected function cleanUp()
|
||||
{
|
||||
$this->client->getIndex('_all')->delete();
|
||||
$this->client->getIndex('_all')->clearCache();
|
||||
}
|
||||
|
|
94
Tests/Functional/Connection/Elasticsearch/FilterTest.php
Normal file
94
Tests/Functional/Connection/Elasticsearch/FilterTest.php
Normal file
|
@ -0,0 +1,94 @@
|
|||
<?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 FilterTest extends AbstractFunctionalTestCase
|
||||
{
|
||||
protected function getDataSets()
|
||||
{
|
||||
return array_merge(
|
||||
parent::getDataSets(),
|
||||
['Tests/Functional/Fixtures/Searching/Filter.xml']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function itsPossibleToFilterResultsByASingleField()
|
||||
{
|
||||
\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(2, count($result), 'Did not receive both indexed elements without filter.');
|
||||
|
||||
$searchRequest->setFilter(['CType' => 'HTML']);
|
||||
$result = $searchService->search($searchRequest);
|
||||
$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.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 = $result->getFacets()[0];
|
||||
$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[0];
|
||||
$this->assertSame('HTML', $option->getName(), 'Option did not have expected Name.');
|
||||
$this->assertSame(1, $option->getCount(), 'Option did not have expected count.');
|
||||
$option = $options[1];
|
||||
$this->assertSame('Header', $option->getName(), 'Option did not have expected Name.');
|
||||
$this->assertSame(1, $option->getCount(), 'Option did not have expected count.');
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Tests\Functional\Connection\Elasticsearch;
|
||||
namespace Codappix\SearchCore\Tests\Functional\Connection\Elasticsearch;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,7 +20,7 @@ namespace Leonmrni\SearchCore\Tests\Functional\Connection\Elasticsearch;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Domain\Index\IndexerFactory;
|
||||
use Codappix\SearchCore\Domain\Index\IndexerFactory;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||
|
||||
/**
|
||||
|
@ -61,7 +61,7 @@ class IndexTcaTableTest extends AbstractFunctionalTestCase
|
|||
|
||||
/**
|
||||
* @test
|
||||
* @expectedException \Leonmrni\SearchCore\Domain\Index\IndexingException
|
||||
* @expectedException \Codappix\SearchCore\Domain\Index\IndexingException
|
||||
*/
|
||||
public function indexingNonConfiguredTableWillThrowException()
|
||||
{
|
||||
|
|
|
@ -8,9 +8,23 @@ plugin {
|
|||
}
|
||||
}
|
||||
|
||||
indexer {
|
||||
tca {
|
||||
allowedTables = tt_content
|
||||
indexing {
|
||||
tt_content {
|
||||
indexer = Codappix\SearchCore\Domain\Index\TcaIndexer
|
||||
|
||||
mapping {
|
||||
CType {
|
||||
type = keyword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
searching {
|
||||
facets {
|
||||
contentTypes {
|
||||
field = CType
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
plugin {
|
||||
tx_searchcore {
|
||||
settings {
|
||||
indexer {
|
||||
tca {
|
||||
indexing {
|
||||
tt_content {
|
||||
rootLineBlacklist = 3
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
plugin {
|
||||
tx_searchcore {
|
||||
settings {
|
||||
indexer {
|
||||
tca {
|
||||
tt_content {
|
||||
additionalWhereClause = tt_content.CType NOT IN ('div')
|
||||
}
|
||||
indexing {
|
||||
tt_content {
|
||||
additionalWhereClause = tt_content.CType NOT IN ('div')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
42
Tests/Functional/Fixtures/Searching/Filter.xml
Normal file
42
Tests/Functional/Fixtures/Searching/Filter.xml
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<dataset>
|
||||
<tt_content>
|
||||
<uid>5</uid>
|
||||
<pid>1</pid>
|
||||
<tstamp>1480686370</tstamp>
|
||||
<crdate>1480686370</crdate>
|
||||
<hidden>0</hidden>
|
||||
<sorting>72</sorting>
|
||||
<CType>html</CType>
|
||||
<header>indexed content element with html ctype</header>
|
||||
<bodytext>Search Word</bodytext>
|
||||
<media>0</media>
|
||||
<layout>0</layout>
|
||||
<deleted>0</deleted>
|
||||
<cols>0</cols>
|
||||
<starttime>0</starttime>
|
||||
<endtime>0</endtime>
|
||||
<colPos>0</colPos>
|
||||
<filelink_sorting>0</filelink_sorting>
|
||||
</tt_content>
|
||||
|
||||
<tt_content>
|
||||
<uid>6</uid>
|
||||
<pid>1</pid>
|
||||
<tstamp>1480686370</tstamp>
|
||||
<crdate>1480686370</crdate>
|
||||
<hidden>0</hidden>
|
||||
<sorting>72</sorting>
|
||||
<CType>header</CType>
|
||||
<header>indexed content element with header ctype</header>
|
||||
<bodytext>Search Word</bodytext>
|
||||
<media>0</media>
|
||||
<layout>0</layout>
|
||||
<deleted>0</deleted>
|
||||
<cols>0</cols>
|
||||
<starttime>0</starttime>
|
||||
<endtime>0</endtime>
|
||||
<colPos>0</colPos>
|
||||
<filelink_sorting>0</filelink_sorting>
|
||||
</tt_content>
|
||||
</dataset>
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Tests\Functional\Hooks\DataHandler;
|
||||
namespace Codappix\SearchCore\Tests\Functional\Hooks\DataHandler;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,10 +20,11 @@ namespace Leonmrni\SearchCore\Tests\Functional\Hooks\DataHandler;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Leonmrni\SearchCore\Domain\Service\DataHandler as DataHandlerService;
|
||||
use Leonmrni\SearchCore\Hook\DataHandler as DataHandlerHook;
|
||||
use Leonmrni\SearchCore\Tests\Functional\AbstractFunctionalTestCase;
|
||||
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Codappix\SearchCore\Domain\Index\IndexerFactory;
|
||||
use Codappix\SearchCore\Domain\Service\DataHandler as DataHandlerService;
|
||||
use Codappix\SearchCore\Hook\DataHandler as DataHandlerHook;
|
||||
use Codappix\SearchCore\Tests\Functional\AbstractFunctionalTestCase;
|
||||
use TYPO3\CMS\Core\DataHandling\DataHandler as Typo3DataHandler;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||
|
@ -42,7 +43,10 @@ abstract class AbstractDataHandlerTest extends AbstractFunctionalTestCase
|
|||
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
|
||||
|
||||
$this->subject = $this->getMockBuilder(DataHandlerService::class)
|
||||
->setConstructorArgs([$objectManager->get(ConfigurationContainerInterface::class)])
|
||||
->setConstructorArgs([
|
||||
$objectManager->get(ConfigurationContainerInterface::class),
|
||||
$objectManager->get(IndexerFactory::class)
|
||||
])
|
||||
->setMethods(['add', 'update', 'delete'])
|
||||
->getMock();
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Tests\Functional\Hooks\DataHandler;
|
||||
namespace Codappix\SearchCore\Tests\Functional\Hooks\DataHandler;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,9 +20,9 @@ namespace Leonmrni\SearchCore\Tests\Functional\Hooks\DataHandler;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Leonmrni\SearchCore\Domain\Service\DataHandler as DataHandlerService;
|
||||
use Leonmrni\SearchCore\Hook\DataHandler as DataHandlerHook;
|
||||
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;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Tests\Functional\Hooks\DataHandler;
|
||||
namespace Codappix\SearchCore\Tests\Functional\Hooks\DataHandler;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,9 +20,9 @@ namespace Leonmrni\SearchCore\Tests\Functional\Hooks\DataHandler;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Leonmrni\SearchCore\Domain\Service\DataHandler as DataHandlerService;
|
||||
use Leonmrni\SearchCore\Hook\DataHandler as DataHandlerHook;
|
||||
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;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Tests\Functional\Hooks\DataHandler;
|
||||
namespace Codappix\SearchCore\Tests\Functional\Hooks\DataHandler;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Tests\Functional\Hooks\DataHandler;
|
||||
namespace Codappix\SearchCore\Tests\Functional\Hooks\DataHandler;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,9 +20,9 @@ namespace Leonmrni\SearchCore\Tests\Functional\Hooks\DataHandler;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Leonmrni\SearchCore\Domain\Service\DataHandler as DataHandlerService;
|
||||
use Leonmrni\SearchCore\Hook\DataHandler as DataHandlerHook;
|
||||
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;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Tests\Functional\Hooks\DataHandler;
|
||||
namespace Codappix\SearchCore\Tests\Functional\Hooks\DataHandler;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Tests\Indexing\TcaIndexer;
|
||||
namespace Codappix\SearchCore\Tests\Indexing\TcaIndexer;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,8 +20,8 @@ namespace Leonmrni\SearchCore\Tests\Indexing\TcaIndexer;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Domain\Index\TcaIndexer\TcaTableService;
|
||||
use Leonmrni\SearchCore\Tests\Functional\AbstractFunctionalTestCase;
|
||||
use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService;
|
||||
use Codappix\SearchCore\Tests\Functional\AbstractFunctionalTestCase;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Tests\Indexing;
|
||||
namespace Codappix\SearchCore\Tests\Indexing;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
|
@ -20,12 +20,12 @@ namespace Leonmrni\SearchCore\Tests\Indexing;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Leonmrni\SearchCore\Connection\Elasticsearch;
|
||||
use Leonmrni\SearchCore\Domain\Index\TcaIndexer;
|
||||
use Leonmrni\SearchCore\Domain\Index\TcaIndexer\RelationResolver;
|
||||
use Leonmrni\SearchCore\Domain\Index\TcaIndexer\TcaTableService;
|
||||
use Leonmrni\SearchCore\Tests\Functional\AbstractFunctionalTestCase;
|
||||
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Codappix\SearchCore\Connection\Elasticsearch;
|
||||
use Codappix\SearchCore\Domain\Index\TcaIndexer;
|
||||
use Codappix\SearchCore\Domain\Index\TcaIndexer\RelationResolver;
|
||||
use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService;
|
||||
use Codappix\SearchCore\Tests\Functional\AbstractFunctionalTestCase;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||
|
||||
class TcaIndexerTest extends AbstractFunctionalTestCase
|
||||
|
|
46
Tests/Unit/AbstractUnitTestCase.php
Normal file
46
Tests/Unit/AbstractUnitTestCase.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\Tests\Unit;
|
||||
|
||||
/*
|
||||
* 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 TYPO3\CMS\Core\Tests\UnitTestCase as CoreTestCase;
|
||||
|
||||
abstract class AbstractUnitTestCase extends CoreTestCase
|
||||
{
|
||||
/**
|
||||
* @return \TYPO3\CMS\Core\Log\LogManager
|
||||
*/
|
||||
protected function getMockedLogger()
|
||||
{
|
||||
$logger = $this->getMockBuilder(\TYPO3\CMS\Core\Log\LogManager::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['getLogger'])
|
||||
->getMock();
|
||||
$logger->expects($this->once())
|
||||
->method('getLogger')
|
||||
->will($this->returnValue(
|
||||
$this->getMockBuilder(\TYPO3\CMS\Core\Log\Logger::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock()
|
||||
));
|
||||
|
||||
return $logger;
|
||||
}
|
||||
}
|
94
Tests/Unit/Command/IndexCommandControllerTest.php
Normal file
94
Tests/Unit/Command/IndexCommandControllerTest.php
Normal file
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\Tests\Unit\Command;
|
||||
|
||||
/*
|
||||
* 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\Command\IndexCommandController;
|
||||
use Codappix\SearchCore\Domain\Index\IndexerFactory;
|
||||
use Codappix\SearchCore\Domain\Index\NoMatchingIndexerException;
|
||||
use Codappix\SearchCore\Domain\Index\TcaIndexer;
|
||||
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\CommandController;
|
||||
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
|
||||
|
||||
class IndexCommandControllerTest extends AbstractUnitTestCase
|
||||
{
|
||||
/**
|
||||
* @var IndexCommandController
|
||||
*/
|
||||
protected $subject;
|
||||
|
||||
/**
|
||||
* @var IndexerFactory
|
||||
*/
|
||||
protected $indexerFactory;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->indexerFactory = $this->getMockBuilder(IndexerFactory::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->subject = $this->getMockBuilder(IndexCommandController::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['quit', 'outputLine'])
|
||||
->getMock();
|
||||
$this->subject->injectIndexerFactory($this->indexerFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function indexerStopsForNonAllowedTable()
|
||||
{
|
||||
$this->subject->expects($this->once())
|
||||
->method('outputLine')
|
||||
->with('No indexer found for: nonAllowedTable');
|
||||
$this->indexerFactory->expects($this->once())
|
||||
->method('getIndexer')
|
||||
->with('nonAllowedTable')
|
||||
->will($this->throwException(new NoMatchingIndexerException));
|
||||
|
||||
$this->subject->indexCommand('nonAllowedTable');
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function indexerExecutesForAllowedTable()
|
||||
{
|
||||
$indexerMock = $this->getMockBuilder(TcaIndexer::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->subject->expects($this->never())
|
||||
->method('quit');
|
||||
$this->subject->expects($this->once())
|
||||
->method('outputLine')
|
||||
->with('allowedTable was indexed.');
|
||||
$this->indexerFactory->expects($this->once())
|
||||
->method('getIndexer')
|
||||
->with('allowedTable')
|
||||
->will($this->returnValue($indexerMock));
|
||||
|
||||
$this->subject->indexCommand('allowedTable');
|
||||
}
|
||||
}
|
85
Tests/Unit/Domain/Index/TcaIndexer/TcaTableServiceTest.php
Normal file
85
Tests/Unit/Domain/Index/TcaIndexer/TcaTableServiceTest.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\Tests\Unit\Domain\Index\TcaIndexer;
|
||||
|
||||
/*
|
||||
* 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\Configuration\ConfigurationContainerInterface;
|
||||
use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService;
|
||||
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
|
||||
|
||||
class TcaTableServiceTest extends AbstractUnitTestCase
|
||||
{
|
||||
/**
|
||||
* @var TcaTableService
|
||||
*/
|
||||
protected $subject;
|
||||
|
||||
/**
|
||||
* @var ConfigurationContainerInterface
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->configuration = $this->getMockBuilder(ConfigurationContainerInterface::class)->getMock();
|
||||
|
||||
$this->subject = $this->getMockBuilder(TcaTableService::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethodsExcept(['getWhereClause', 'injectLogger', 'getTableName'])
|
||||
->getMock();
|
||||
$this->inject($this->subject, 'configuration', $this->configuration);
|
||||
$this->inject($this->subject, 'logger', $this->getMockedLogger());
|
||||
$this->inject($this->subject, 'tableName', 'table');
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function doUsePlainQueryIfNoAdditionalWhereClauseIsDefined()
|
||||
{
|
||||
$this->configuration->expects($this->exactly(2))
|
||||
->method('getIfExists')
|
||||
->withConsecutive(['indexing.table.additionalWhereClause'], ['indexing.table.rootLineBlacklist'])
|
||||
->will($this->onConsecutiveCalls(null, false));
|
||||
|
||||
$this->assertSame(
|
||||
'1=1 AND pages.no_search = 0',
|
||||
$this->subject->getWhereClause()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function configuredAdditionalWhereClauseIsAdded()
|
||||
{
|
||||
$this->configuration->expects($this->exactly(2))
|
||||
->method('getIfExists')
|
||||
->withConsecutive(['indexing.table.additionalWhereClause'], ['indexing.table.rootLineBlacklist'])
|
||||
->will($this->onConsecutiveCalls('table.field = "someValue"', false));
|
||||
|
||||
$this->assertSame(
|
||||
'1=1 AND pages.no_search = 0 AND table.field = "someValue"',
|
||||
$this->subject->getWhereClause()
|
||||
);
|
||||
}
|
||||
}
|
148
Tests/Unit/Domain/Search/QueryFactoryTest.php
Normal file
148
Tests/Unit/Domain/Search/QueryFactoryTest.php
Normal file
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
namespace Codappix\SearchCore\Tests\Unit\Domain\Search;
|
||||
|
||||
/*
|
||||
* 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\Model\FacetRequest;
|
||||
use Codappix\SearchCore\Domain\Model\SearchRequest;
|
||||
use Codappix\SearchCore\Domain\Search\QueryFactory;
|
||||
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
|
||||
|
||||
class QueryFactoryTest extends AbstractUnitTestCase
|
||||
{
|
||||
/**
|
||||
* @var QueryFactory
|
||||
*/
|
||||
protected $subject;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->subject = new QueryFactory($this->getMockedLogger());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function creatonOfQueryWorksInGeneral()
|
||||
{
|
||||
$searchRequest = new SearchRequest('SearchWord');
|
||||
|
||||
$query = $this->subject->create($searchRequest);
|
||||
$this->assertInstanceOf(
|
||||
\Elastica\Query::class,
|
||||
$query,
|
||||
'Factory did not create the expected instance.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function filterIsAddedToQuery()
|
||||
{
|
||||
$searchRequest = new SearchRequest('SearchWord');
|
||||
$searchRequest->setFilter(['field' => 'content']);
|
||||
|
||||
$query = $this->subject->create($searchRequest);
|
||||
$this->assertSame(
|
||||
[
|
||||
['term' => ['field' => 'content']]
|
||||
],
|
||||
$query->toArray()['query']['bool']['filter'],
|
||||
'Filter was not added to query.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function emptyFilterIsNotAddedToQuery()
|
||||
{
|
||||
$searchRequest = new SearchRequest('SearchWord');
|
||||
$searchRequest->setFilter([
|
||||
'field' => '',
|
||||
'field1' => 0,
|
||||
'field2' => false,
|
||||
]);
|
||||
|
||||
$this->assertFalse(
|
||||
$searchRequest->hasFilter(),
|
||||
'Search request contains filter even if it should not.'
|
||||
);
|
||||
|
||||
$query = $this->subject->create($searchRequest);
|
||||
$this->assertSame(
|
||||
null,
|
||||
$query->toArray()['query']['bool']['filter'],
|
||||
'Filter was added to query, even if no filter exists.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function userInputIsAlwaysString()
|
||||
{
|
||||
$searchRequest = new SearchRequest(10);
|
||||
$searchRequest->setFilter(['field' => 20]);
|
||||
|
||||
$query = $this->subject->create($searchRequest);
|
||||
$this->assertSame(
|
||||
'10',
|
||||
$query->toArray()['query']['bool']['must'][0]['match']['_all'],
|
||||
'Search word was not escaped as expected.'
|
||||
);
|
||||
$this->assertSame(
|
||||
'20',
|
||||
$query->toArray()['query']['bool']['filter'][0]['term']['field'],
|
||||
'Search word was not escaped as expected.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function facetsAreAddedToQuery()
|
||||
{
|
||||
$searchRequest = new SearchRequest('SearchWord');
|
||||
$searchRequest->addFacet(new FacetRequest('Identifier', 'FieldName'));
|
||||
$searchRequest->addFacet(new FacetRequest('Identifier 2', 'FieldName 2'));
|
||||
|
||||
$query = $this->subject->create($searchRequest);
|
||||
$this->assertSame(
|
||||
[
|
||||
'Identifier' => [
|
||||
'terms' => [
|
||||
'field' => 'FieldName',
|
||||
],
|
||||
],
|
||||
'Identifier 2' => [
|
||||
'terms' => [
|
||||
'field' => 'FieldName 2',
|
||||
],
|
||||
],
|
||||
],
|
||||
$query->toArray()['aggs'],
|
||||
'Facets were not added to query.'
|
||||
);
|
||||
}
|
||||
}
|
28
Tests/Unit/UnitTests.xml
Normal file
28
Tests/Unit/UnitTests.xml
Normal file
|
@ -0,0 +1,28 @@
|
|||
<phpunit
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="../../.Build/vendor/typo3/cms/typo3/sysext/core/Build/UnitTestsBootstrap.php"
|
||||
|
||||
colors="true"
|
||||
convertErrorsToExceptions="false"
|
||||
convertWarningsToExceptions="false"
|
||||
forceCoversAnnotation="false"
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
stopOnIncomplete="false"
|
||||
stopOnSkipped="false"
|
||||
verbose="false">
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="unit-tests">
|
||||
<directory>.</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">../../Classes</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
|
@ -1,17 +1,17 @@
|
|||
{
|
||||
"name": "leonmrni/search_core",
|
||||
"name": "codappix/search_core",
|
||||
"type": "typo3-cms-extension",
|
||||
"description": "Leonmrni Search Core.",
|
||||
"homepage": "http://www.leonmrni.com",
|
||||
"description": "Codappix Search Core.",
|
||||
"homepage": "https://github.com/Codappix/search_core",
|
||||
"license": ["GPL-2.0+"],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Leonmrni\\SearchCore\\": "Classes"
|
||||
"Codappix\\SearchCore\\": "Classes"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Leonmrni\\SearchCore\\Tests\\": "Tests/",
|
||||
"Codappix\\SearchCore\\Tests\\": "Tests/",
|
||||
"TYPO3\\CMS\\Core\\Tests\\": ".Build/vendor/typo3/cms/typo3/sysext/core/Tests/"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -4,6 +4,7 @@ $EM_CONF[$_EXTKEY] = [
|
|||
'title' => 'Search Core',
|
||||
'description' => 'Search core for implementing various search types.',
|
||||
'category' => 'be',
|
||||
'clearCacheOnLoad' => 1,
|
||||
'constraints' => [
|
||||
'depends' => [
|
||||
'typo3' => '7.6.0-7.6.99',
|
||||
|
@ -13,12 +14,11 @@ $EM_CONF[$_EXTKEY] = [
|
|||
],
|
||||
'autoload' => [
|
||||
'psr-4' => [
|
||||
'Leonmrni\\SearchCore\\' => 'Classes',
|
||||
'Codappix\\SearchCore\\' => 'Classes',
|
||||
],
|
||||
],
|
||||
'state' => 'alpha',
|
||||
'clearCacheOnLoad' => 1,
|
||||
'state' => 'beta',
|
||||
'version' => '1.0.0',
|
||||
'author' => 'Daniel Siepmann',
|
||||
'author_email' => 'coding@daniel-siepmann.de',
|
||||
'version' => '1.0.0',
|
||||
];
|
||||
|
|
|
@ -11,15 +11,15 @@ call_user_func(
|
|||
'SC_OPTIONS' => [
|
||||
'extbase' => [
|
||||
'commandControllers' => [
|
||||
Leonmrni\SearchCore\Command\IndexCommandController::class,
|
||||
Codappix\SearchCore\Command\IndexCommandController::class,
|
||||
],
|
||||
],
|
||||
't3lib/class.t3lib_tcemain.php' => [
|
||||
'processCmdmapClass' => [
|
||||
$extensionKey => '&' . \Leonmrni\SearchCore\Hook\DataHandler::class,
|
||||
$extensionKey => '&' . \Codappix\SearchCore\Hook\DataHandler::class,
|
||||
],
|
||||
'processDatamapClass' => [
|
||||
$extensionKey => '&' . \Leonmrni\SearchCore\Hook\DataHandler::class,
|
||||
$extensionKey => '&' . \Codappix\SearchCore\Hook\DataHandler::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -27,7 +27,7 @@ call_user_func(
|
|||
);
|
||||
|
||||
TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
|
||||
'Leonmrni.' . $extensionKey,
|
||||
'Codappix.' . $extensionKey,
|
||||
'search',
|
||||
[
|
||||
'Search' => 'search'
|
||||
|
@ -39,8 +39,8 @@ call_user_func(
|
|||
|
||||
\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\CMS\Extbase\Object\Container\Container')
|
||||
->registerImplementation(
|
||||
'Leonmrni\SearchCore\Connection\ConnectionInterface',
|
||||
'Leonmrni\SearchCore\Connection\Elasticsearch'
|
||||
'Codappix\SearchCore\Connection\ConnectionInterface',
|
||||
'Codappix\SearchCore\Connection\Elasticsearch'
|
||||
);
|
||||
},
|
||||
$_EXTKEY
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
);
|
||||
|
||||
TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
|
||||
'Leonmrni.' . $_EXTKEY,
|
||||
'Codappix.' . $_EXTKEY,
|
||||
'search',
|
||||
'Search Core'
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue