mirror of
https://github.com/Codappix/search_core.git
synced 2024-11-25 22:56:16 +01:00
Merge pull request #82 from Codappix/feature/add-pagination
FEATURE: Implement necessary logic to support PaginateViewHelper
This commit is contained in:
commit
fcaf1efc79
9 changed files with 261 additions and 74 deletions
|
@ -189,7 +189,7 @@ class Elasticsearch implements Singleton, ConnectionInterface
|
||||||
$search->addIndex('typo3content');
|
$search->addIndex('typo3content');
|
||||||
$search->setQuery($this->queryFactory->create($searchRequest));
|
$search->setQuery($this->queryFactory->create($searchRequest));
|
||||||
|
|
||||||
return $this->objectManager->get(SearchResult::class, $search->search());
|
return $this->objectManager->get(SearchResult::class, $searchRequest, $search->search());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -22,11 +22,17 @@ namespace Codappix\SearchCore\Connection\Elasticsearch;
|
||||||
|
|
||||||
use Codappix\SearchCore\Connection\FacetInterface;
|
use Codappix\SearchCore\Connection\FacetInterface;
|
||||||
use Codappix\SearchCore\Connection\ResultItemInterface;
|
use Codappix\SearchCore\Connection\ResultItemInterface;
|
||||||
|
use Codappix\SearchCore\Connection\SearchRequestInterface;
|
||||||
use Codappix\SearchCore\Connection\SearchResultInterface;
|
use Codappix\SearchCore\Connection\SearchResultInterface;
|
||||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||||
|
|
||||||
class SearchResult implements SearchResultInterface
|
class SearchResult implements SearchResultInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var SearchRequestInterface
|
||||||
|
*/
|
||||||
|
protected $searchRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Elastica\ResultSet
|
* @var \Elastica\ResultSet
|
||||||
*/
|
*/
|
||||||
|
@ -47,8 +53,12 @@ class SearchResult implements SearchResultInterface
|
||||||
*/
|
*/
|
||||||
protected $objectManager;
|
protected $objectManager;
|
||||||
|
|
||||||
public function __construct(\Elastica\ResultSet $result, ObjectManagerInterface $objectManager)
|
public function __construct(
|
||||||
{
|
SearchRequestInterface $searchRequest,
|
||||||
|
\Elastica\ResultSet $result,
|
||||||
|
ObjectManagerInterface $objectManager
|
||||||
|
) {
|
||||||
|
$this->searchRequest = $searchRequest;
|
||||||
$this->result = $result;
|
$this->result = $result;
|
||||||
$this->objectManager = $objectManager;
|
$this->objectManager = $objectManager;
|
||||||
}
|
}
|
||||||
|
@ -75,25 +85,37 @@ class SearchResult implements SearchResultInterface
|
||||||
return $this->facets;
|
return $this->facets;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getCurrentCount()
|
||||||
* Returns the total sum of matching results.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getTotalCount()
|
|
||||||
{
|
{
|
||||||
return $this->result->getTotalHits();
|
return $this->result->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
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[$aggregationName] = $this->objectManager->get(Facet::class, $aggregationName, $aggregation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Countable - Interface
|
// Countable - Interface
|
||||||
/**
|
|
||||||
* Returns the total sum of results contained in this result.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function count()
|
public function count()
|
||||||
{
|
{
|
||||||
return $this->result->count();
|
return $this->result->getTotalHits();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterator - Interface
|
// Iterator - Interface
|
||||||
|
@ -122,25 +144,41 @@ class SearchResult implements SearchResultInterface
|
||||||
$this->result->rewind();
|
$this->result->rewind();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function initResults()
|
// Extbase QueryResultInterface - Implemented to support Pagination of Fluid.
|
||||||
|
|
||||||
|
public function getQuery()
|
||||||
{
|
{
|
||||||
if ($this->results !== []) {
|
return $this->searchRequest;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->result->getResults() as $result) {
|
public function getFirst()
|
||||||
$this->results[] = new ResultItem($result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function initFacets()
|
|
||||||
{
|
{
|
||||||
if ($this->facets !== [] || !$this->result->hasAggregations()) {
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502195121);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->result->getAggregations() as $aggregationName => $aggregation) {
|
public function toArray()
|
||||||
$this->facets[$aggregationName] = $this->objectManager->get(Facet::class, $aggregationName, $aggregation);
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502195135);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function offsetExists($offset)
|
||||||
|
{
|
||||||
|
// Return false to allow Fluid to use appropriate getter methods.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetGet($offset)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Use getter to fetch properties.', 1502196933);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetSet($offset, $value)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('You are not allowed to modify the result.', 1502196934);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetUnset($offset)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('You are not allowed to modify the result.', 1502196936);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,9 @@ namespace Codappix\SearchCore\Connection;
|
||||||
* 02110-1301, USA.
|
* 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
|
||||||
*
|
|
||||||
*/
|
interface SearchRequestInterface extends QueryInterface
|
||||||
interface SearchRequestInterface
|
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Returns the actual string the user searched for.
|
* Returns the actual string the user searched for.
|
||||||
|
@ -41,11 +40,4 @@ interface SearchRequestInterface
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getFilter();
|
public function getFilter();
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines how many results should be fetched.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getSize();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,12 @@ namespace Codappix\SearchCore\Connection;
|
||||||
* 02110-1301, USA.
|
* 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A search result.
|
* A search result.
|
||||||
*/
|
*/
|
||||||
interface SearchResultInterface extends \Iterator, \Countable
|
interface SearchResultInterface extends \Iterator, \Countable, QueryResultInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @return array<ResultItemInterface>
|
* @return array<ResultItemInterface>
|
||||||
|
@ -38,18 +40,9 @@ interface SearchResultInterface extends \Iterator, \Countable
|
||||||
public function getFacets();
|
public function getFacets();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the total sum of matching results.
|
* Returns the number of results in current result
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function getTotalCount();
|
public function getCurrentCount();
|
||||||
|
|
||||||
// Countable - Interface
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the total sum of results contained in this result.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function count();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ namespace Codappix\SearchCore\Domain\Model;
|
||||||
* 02110-1301, USA.
|
* 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Codappix\SearchCore\Connection\ConnectionInterface;
|
||||||
use Codappix\SearchCore\Connection\FacetRequestInterface;
|
use Codappix\SearchCore\Connection\FacetRequestInterface;
|
||||||
use Codappix\SearchCore\Connection\SearchRequestInterface;
|
use Codappix\SearchCore\Connection\SearchRequestInterface;
|
||||||
|
|
||||||
|
@ -35,11 +36,6 @@ class SearchRequest implements SearchRequestInterface
|
||||||
*/
|
*/
|
||||||
protected $query = '';
|
protected $query = '';
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $size = 10;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
|
@ -50,6 +46,23 @@ class SearchRequest implements SearchRequestInterface
|
||||||
*/
|
*/
|
||||||
protected $facets = [];
|
protected $facets = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $offset = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $limit = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for QueryInterface implementation to allow execute method to work.
|
||||||
|
*
|
||||||
|
* @var ConnectionInterface
|
||||||
|
*/
|
||||||
|
protected $connection = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $query
|
* @param string $query
|
||||||
*/
|
*/
|
||||||
|
@ -119,18 +132,162 @@ class SearchRequest implements SearchRequestInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return int
|
* Define connection to use for this request.
|
||||||
|
* Necessary to allow implementation of execute for interface.
|
||||||
|
*
|
||||||
|
* @param ConnectionInterface $connection
|
||||||
*/
|
*/
|
||||||
public function getSize()
|
public function setConnection(ConnectionInterface $connection)
|
||||||
{
|
{
|
||||||
return $this->size;
|
$this->connection = $connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Extbase QueryInterface
|
||||||
* @param int $size
|
// Current implementation covers only paginate widget support.
|
||||||
*/
|
public function execute($returnRawQueryResult = false)
|
||||||
public function setSize($size)
|
|
||||||
{
|
{
|
||||||
$this->size = (int) $size;
|
if ($this->connection instanceof ConnectionInterface) {
|
||||||
|
return $this->connection->search($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'Connection was not set before, therefore execute can not work. Use `setConnection` before.',
|
||||||
|
1502197732
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLimit($limit)
|
||||||
|
{
|
||||||
|
$this->limit = (int) $limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setOffset($offset)
|
||||||
|
{
|
||||||
|
$this->offset = (int) $offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLimit()
|
||||||
|
{
|
||||||
|
return $this->limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOffset()
|
||||||
|
{
|
||||||
|
return $this->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSource()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196146);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setOrderings(array $orderings)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196163);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function matching($constraint)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196197);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logicalAnd($constraint1)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196166);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logicalOr($constraint1)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196198);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logicalNot(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196166);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function equals($propertyName, $operand, $caseSensitive = true)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196199);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function like($propertyName, $operand, $caseSensitive = true)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196167);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function contains($propertyName, $operand)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function in($propertyName, $operand)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196167);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function lessThan($propertyName, $operand)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196201);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function lessThanOrEqual($propertyName, $operand)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196168);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function greaterThan($propertyName, $operand)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196202);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function greaterThanOrEqual($propertyName, $operand)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196168);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196203);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setQuerySettings(\TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196168);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQuerySettings()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196205);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function count()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196169);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOrderings()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196206);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConstraint()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196171);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEmpty($propertyName)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196207);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSource(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196172);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStatement()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Method is not implemented yet.', 1502196208);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,8 +94,8 @@ class QueryFactory
|
||||||
protected function addSize(SearchRequestInterface $searchRequest, array &$query)
|
protected function addSize(SearchRequestInterface $searchRequest, array &$query)
|
||||||
{
|
{
|
||||||
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
|
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
|
||||||
'from' => 0,
|
'from' => $searchRequest->getOffset(),
|
||||||
'size' => $searchRequest->getSize(),
|
'size' => $searchRequest->getLimit(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,7 @@ class SearchService
|
||||||
*/
|
*/
|
||||||
public function search(SearchRequestInterface $searchRequest)
|
public function search(SearchRequestInterface $searchRequest)
|
||||||
{
|
{
|
||||||
|
$searchRequest->setConnection($this->connection);
|
||||||
$this->addSize($searchRequest);
|
$this->addSize($searchRequest);
|
||||||
$this->addConfiguredFacets($searchRequest);
|
$this->addConfiguredFacets($searchRequest);
|
||||||
|
|
||||||
|
@ -82,7 +83,7 @@ class SearchService
|
||||||
*/
|
*/
|
||||||
protected function addSize(SearchRequestInterface $searchRequest)
|
protected function addSize(SearchRequestInterface $searchRequest)
|
||||||
{
|
{
|
||||||
$searchRequest->setSize(
|
$searchRequest->setLimit(
|
||||||
$this->configuration->getIfExists('searching.size') ?: 10
|
$this->configuration->getIfExists('searching.size') ?: 10
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,13 +157,19 @@ class QueryFactoryTest extends AbstractUnitTestCase
|
||||||
->method('get')
|
->method('get')
|
||||||
->will($this->throwException(new InvalidArgumentException));
|
->will($this->throwException(new InvalidArgumentException));
|
||||||
$searchRequest = new SearchRequest('SearchWord');
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
$searchRequest->setSize(45);
|
$searchRequest->setLimit(45);
|
||||||
|
$searchRequest->setOffset(35);
|
||||||
|
|
||||||
$query = $this->subject->create($searchRequest);
|
$query = $this->subject->create($searchRequest);
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
45,
|
45,
|
||||||
$query->toArray()['size'],
|
$query->toArray()['size'],
|
||||||
'Size was not added to query.'
|
'Limit was not added to query.'
|
||||||
|
);
|
||||||
|
$this->assertSame(
|
||||||
|
35,
|
||||||
|
$query->toArray()['from'],
|
||||||
|
'From was not added to query.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ class SearchServiceTest extends AbstractUnitTestCase
|
||||||
$this->connection->expects($this->once())
|
$this->connection->expects($this->once())
|
||||||
->method('search')
|
->method('search')
|
||||||
->with($this->callback(function ($searchRequest) {
|
->with($this->callback(function ($searchRequest) {
|
||||||
return $searchRequest->getSize() === 45;
|
return $searchRequest->getLimit() === 45;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
$searchRequest = new SearchRequest('SearchWord');
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
|
@ -86,7 +86,7 @@ class SearchServiceTest extends AbstractUnitTestCase
|
||||||
$this->connection->expects($this->once())
|
$this->connection->expects($this->once())
|
||||||
->method('search')
|
->method('search')
|
||||||
->with($this->callback(function ($searchRequest) {
|
->with($this->callback(function ($searchRequest) {
|
||||||
return $searchRequest->getSize() === 10;
|
return $searchRequest->getLimit() === 10;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
$searchRequest = new SearchRequest('SearchWord');
|
$searchRequest = new SearchRequest('SearchWord');
|
||||||
|
|
Loading…
Reference in a new issue