mirror of
https://github.com/Codappix/search_core.git
synced 2024-12-23 03:36:09 +01:00
TASK: Integrate working code
Copied code from customer installation with working implementation.
This commit is contained in:
parent
432335c80d
commit
975381cc4a
18 changed files with 832 additions and 33 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -20,8 +20,10 @@ namespace Leonmrni\SearchCore\Connection;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\SingletonInterface as Singleton;
|
||||
use Leonmrni\SearchCore\Connection\Elasticsearch\SearchResult;
|
||||
use Leonmrni\SearchCore\Domain\Search\QueryFactory;
|
||||
use TYPO3\CMS\Core\SingletonInterface as Singleton;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||
|
||||
/**
|
||||
* Outer wrapper to elasticsearch.
|
||||
|
@ -43,6 +45,11 @@ class Elasticsearch implements Singleton, ConnectionInterface
|
|||
*/
|
||||
protected $typeFactory;
|
||||
|
||||
/**
|
||||
* @var Elasticsearch\MappingFactory
|
||||
*/
|
||||
protected $mappingFactory;
|
||||
|
||||
/**
|
||||
* @var Elasticsearch\DocumentFactory
|
||||
*/
|
||||
|
@ -58,6 +65,11 @@ class Elasticsearch implements Singleton, ConnectionInterface
|
|||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var ObjectManagerInterface
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* Inject log manager to get concrete logger from it.
|
||||
*
|
||||
|
@ -68,10 +80,19 @@ 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
|
||||
*/
|
||||
|
@ -79,12 +100,14 @@ class Elasticsearch implements Singleton, ConnectionInterface
|
|||
Elasticsearch\Connection $connection,
|
||||
Elasticsearch\IndexFactory $indexFactory,
|
||||
Elasticsearch\TypeFactory $typeFactory,
|
||||
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;
|
||||
}
|
||||
|
@ -142,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();
|
||||
}
|
||||
|
@ -149,7 +179,7 @@ class Elasticsearch implements Singleton, ConnectionInterface
|
|||
/**
|
||||
* @param SearchRequestInterface $searchRequest
|
||||
*
|
||||
* @return \Elastica\ResultSet
|
||||
* @return SearchResultInterface
|
||||
*/
|
||||
public function search(SearchRequestInterface $searchRequest)
|
||||
{
|
||||
|
@ -157,11 +187,9 @@ class Elasticsearch implements Singleton, ConnectionInterface
|
|||
|
||||
$search = new \Elastica\Search($this->connection->getClient());
|
||||
$search->addIndex('typo3content');
|
||||
$search->setQuery($this->queryFactory->create($this, $searchRequest));
|
||||
$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();
|
||||
return $this->objectManager->get(SearchResult::class, $search->search());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
93
Classes/Connection/Elasticsearch/Facet.php
Normal file
93
Classes/Connection/Elasticsearch/Facet.php
Normal file
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
namespace Leonmrni\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 Leonmrni\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Leonmrni\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 Leonmrni\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 Leonmrni\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;
|
||||
}
|
||||
}
|
73
Classes/Connection/Elasticsearch/MappingFactory.php
Normal file
73
Classes/Connection/Elasticsearch/MappingFactory.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
namespace Leonmrni\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 Leonmrni\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Leonmrni\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 Leonmrni\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 Leonmrni\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()
|
||||
{
|
||||
throw new \BadMethodCallException('It\'s not possible to change the search result.', 1499179077);
|
||||
}
|
||||
|
||||
public function offsetUnset()
|
||||
{
|
||||
throw new \BadMethodCallException('It\'s not possible to change the search result.', 1499179077);
|
||||
}
|
||||
}
|
|
@ -20,12 +20,127 @@ namespace Leonmrni\SearchCore\Connection\Elasticsearch;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Connection\FacetInterface;
|
||||
use Leonmrni\SearchCore\Connection\ResultItemInterface;
|
||||
use Leonmrni\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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
39
Classes/Connection/FacetInterface.php
Normal file
39
Classes/Connection/FacetInterface.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
namespace Leonmrni\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 Leonmrni\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 Leonmrni\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 Leonmrni\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
|
||||
{
|
||||
|
||||
}
|
|
@ -21,9 +21,19 @@ 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();
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ class IndexerFactory implements Singleton
|
|||
|
||||
/**
|
||||
* @param ObjectManagerInterface $objectManager
|
||||
* @param ConfigurationContainerInterface $configuration
|
||||
*/
|
||||
public function __construct(
|
||||
ObjectManagerInterface $objectManager,
|
||||
|
|
|
@ -20,7 +20,6 @@ namespace Leonmrni\SearchCore\Domain\Index;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||
use Leonmrni\SearchCore\Connection\ConnectionInterface;
|
||||
|
||||
/**
|
||||
|
|
62
Classes/Domain/Model/FacetRequest.php
Normal file
62
Classes/Domain/Model/FacetRequest.php
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
namespace Leonmrni\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 Leonmrni\SearchCore\Connection\FacetRequestInterface;
|
||||
|
||||
class FacetRequest implements FacetRequestInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $identifier = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $field = '';
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ namespace Leonmrni\SearchCore\Domain\Model;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Connection\FacetRequestInterface;
|
||||
use Leonmrni\SearchCore\Connection\SearchRequestInterface;
|
||||
|
||||
/**
|
||||
|
@ -39,6 +40,11 @@ class SearchRequest implements SearchRequestInterface
|
|||
*/
|
||||
protected $filter = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $facets = [];
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
*/
|
||||
|
@ -68,7 +74,7 @@ class SearchRequest implements SearchRequestInterface
|
|||
*/
|
||||
public function setFilter(array $filter)
|
||||
{
|
||||
$this->filter = array_map('strval', $filter);
|
||||
$this->filter = array_filter(array_map('strval', $filter));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,4 +92,24 @@ class SearchRequest implements SearchRequestInterface
|
|||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,44 +23,117 @@ namespace Leonmrni\SearchCore\Domain\Search;
|
|||
use Leonmrni\SearchCore\Connection\ConnectionInterface;
|
||||
use Leonmrni\SearchCore\Connection\Elasticsearch\Query;
|
||||
use Leonmrni\SearchCore\Connection\SearchRequestInterface;
|
||||
use TYPO3\CMS\Extbase\Utility\ArrayUtility;
|
||||
|
||||
class QueryFactory
|
||||
{
|
||||
/**
|
||||
* @param ConnectionInterface $connection
|
||||
* @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(
|
||||
ConnectionInterface $connection,
|
||||
SearchRequestInterface $searchRequest
|
||||
) {
|
||||
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)
|
||||
{
|
||||
$query = [
|
||||
'bool' => [
|
||||
'must' => [
|
||||
[
|
||||
'match' => [
|
||||
'_all' => $searchRequest->getSearchTerm()
|
||||
$this->addSearch($searchRequest);
|
||||
$this->addFilter($searchRequest);
|
||||
$this->addFacets($searchRequest);
|
||||
|
||||
// TODO: Add logging here.
|
||||
$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()
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
]);
|
||||
}
|
||||
|
||||
if ($searchRequest->hasFilter()) {
|
||||
$query['bool']['filter'] = ['term' => $searchRequest->getFilter()];
|
||||
/**
|
||||
* @param SearchRequestInterface $searchRequest
|
||||
*/
|
||||
protected function addFilter(SearchRequestInterface $searchRequest)
|
||||
{
|
||||
if (! $searchRequest->hasFilter()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return new \Elastica\Query(['query' => $query]);
|
||||
$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(),
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,10 +20,13 @@ namespace Leonmrni\SearchCore\Domain\Search;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use Leonmrni\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||
use Leonmrni\SearchCore\Configuration\InvalidArgumentException;
|
||||
use Leonmrni\SearchCore\Connection\ConnectionInterface;
|
||||
use Leonmrni\SearchCore\Connection\SearchRequestInterface;
|
||||
use Leonmrni\SearchCore\Connection\SearchResultInterface;
|
||||
use Leonmrni\SearchCore\Domain\Model\SearchRequest;
|
||||
use Leonmrni\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']
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue