first commit
This commit is contained in:
290
Classes/Domain/Search/ResultSet/Facets/AbstractFacet.php
Normal file
290
Classes/Domain/Search/ResultSet/Facets/AbstractFacet.php
Normal file
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
|
||||
/**
|
||||
* Value object that represent a options facet.
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
abstract class AbstractFacet
|
||||
{
|
||||
const TYPE_ABSTRACT = 'abstract';
|
||||
|
||||
/**
|
||||
* String
|
||||
* @var string
|
||||
*/
|
||||
protected static $type = self::TYPE_ABSTRACT;
|
||||
|
||||
/**
|
||||
* The resultSet where this facet belongs to.
|
||||
*
|
||||
* @var SearchResultSet
|
||||
*/
|
||||
protected $resultSet = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $isAvailable = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isUsed = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $allRequirementsMet = true;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* AbstractFacet constructor.
|
||||
*
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $name
|
||||
* @param string $field
|
||||
* @param string $label
|
||||
* @param array $configuration Facet configuration passed from typoscript
|
||||
*/
|
||||
public function __construct(SearchResultSet $resultSet, $name, $field, $label = '', array $configuration = [])
|
||||
{
|
||||
$this->resultSet = $resultSet;
|
||||
$this->name = $name;
|
||||
$this->field = $field;
|
||||
$this->label = $label;
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the object manager
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
|
||||
*/
|
||||
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
|
||||
{
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get solr field name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getField()
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $label
|
||||
*/
|
||||
public function setLabel($label)
|
||||
{
|
||||
$this->label = $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLabel()
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $isAvailable
|
||||
*/
|
||||
public function setIsAvailable($isAvailable)
|
||||
{
|
||||
$this->isAvailable = $isAvailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function getIsAvailable()
|
||||
{
|
||||
return $this->isAvailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $isUsed
|
||||
*/
|
||||
public function setIsUsed($isUsed)
|
||||
{
|
||||
$this->isUsed = $isUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function getIsUsed()
|
||||
{
|
||||
return $this->isUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return static::$type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function getAllRequirementsMet()
|
||||
{
|
||||
return $this->allRequirementsMet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $allRequirementsMet
|
||||
*/
|
||||
public function setAllRequirementsMet($allRequirementsMet)
|
||||
{
|
||||
$this->allRequirementsMet = $allRequirementsMet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SearchResultSet
|
||||
*/
|
||||
public function getResultSet()
|
||||
{
|
||||
return $this->resultSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfiguration()
|
||||
{
|
||||
return $this->configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get facet partial name used for rendering the facet
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPartialName()
|
||||
{
|
||||
return 'Default';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getGroupName()
|
||||
{
|
||||
return isset($this->configuration['groupName']) ? $this->configuration['groupName'] : 'main';
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this facet should ne included in the available facets. When nothing is configured,
|
||||
* the method return TRUE.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getIncludeInAvailableFacets()
|
||||
{
|
||||
return ((int)$this->getFacetSettingOrDefaultValue('includeInAvailableFacets', 1)) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this facets should be included in the used facets. When nothing is configured,
|
||||
* the methods returns true.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getIncludeInUsedFacets()
|
||||
{
|
||||
|
||||
return ((int)$this->getFacetSettingOrDefaultValue('includeInUsedFacets', 1)) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured requirements
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRequirements()
|
||||
{
|
||||
return $this->getFacetSettingOrDefaultValue('requirements.', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of this method should return a "flatten" collection of all items.
|
||||
*
|
||||
* @return AbstractFacetItemCollection
|
||||
*/
|
||||
abstract public function getAllFacetItems();
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param mixed $defaultValue
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getFacetSettingOrDefaultValue($key, $defaultValue)
|
||||
{
|
||||
if (!isset($this->configuration[$key])) {
|
||||
return $defaultValue;
|
||||
}
|
||||
|
||||
return ($this->configuration[$key]);
|
||||
}
|
||||
}
|
116
Classes/Domain/Search/ResultSet/Facets/AbstractFacetItem.php
Normal file
116
Classes/Domain/Search/ResultSet/Facets/AbstractFacetItem.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
|
||||
/**
|
||||
* Abstract item that represent a value of a facet. E.g. an option or a node
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
abstract class AbstractFacetItem
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $label = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $documentCount = 0;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $selected = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $metrics = [];
|
||||
|
||||
/**
|
||||
* @var AbstractFacet
|
||||
*/
|
||||
protected $facet;
|
||||
|
||||
/**
|
||||
* @param AbstractFacet $facet
|
||||
* @param string $label
|
||||
* @param int $documentCount
|
||||
* @param bool $selected
|
||||
* @param array $metrics
|
||||
*/
|
||||
public function __construct(AbstractFacet $facet, $label = '', $documentCount = 0, $selected = false, $metrics = [])
|
||||
{
|
||||
$this->facet = $facet;
|
||||
$this->label = $label;
|
||||
$this->documentCount = $documentCount;
|
||||
$this->selected = $selected;
|
||||
$this->metrics = $metrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getDocumentCount()
|
||||
{
|
||||
return $this->documentCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacet
|
||||
*/
|
||||
public function getFacet()
|
||||
{
|
||||
return $this->facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLabel()
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function getSelected()
|
||||
{
|
||||
return $this->selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMetrics()
|
||||
{
|
||||
return $this->metrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getUriValue();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract function getCollectionKey();
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetItem;
|
||||
use WapplerSystems\Meilisearch\System\Data\AbstractCollection;
|
||||
|
||||
/**
|
||||
* Collection for facet options.
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
abstract class AbstractFacetItemCollection extends AbstractCollection
|
||||
{
|
||||
/**
|
||||
* @param AbstractFacetItem $item
|
||||
* @return AbstractFacetItemCollection
|
||||
*/
|
||||
public function add($item)
|
||||
{
|
||||
if ($item === null) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->data[$item->getCollectionKey()] = $item;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return AbstractFacetItem
|
||||
*/
|
||||
public function getByValue($value)
|
||||
{
|
||||
return isset($this->data[$value]) ? $this->data[$value] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the count (with get prefixed to be usable in fluid).
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCount()
|
||||
{
|
||||
return $this->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractFacetItemCollection
|
||||
*/
|
||||
public function getSelected()
|
||||
{
|
||||
return $this->getFilteredCopy(function(AbstractFacetItem $item) {
|
||||
return $item->getSelected();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $manualSorting
|
||||
* @return AbstractFacetItemCollection
|
||||
*/
|
||||
public function getManualSortedCopy(array $manualSorting)
|
||||
{
|
||||
$result = clone $this;
|
||||
$copiedItems = $result->data;
|
||||
$sortedOptions = [];
|
||||
foreach ($manualSorting as $item) {
|
||||
if (isset($copiedItems[$item])) {
|
||||
$sortedOptions[$item] = $copiedItems[$item];
|
||||
unset($copiedItems[$item]);
|
||||
}
|
||||
}
|
||||
// in the end all items get appended that are not configured in the manual sort order
|
||||
$sortedOptions = $sortedOptions + $copiedItems;
|
||||
$result->data = $sortedOptions;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractFacetItemCollection
|
||||
*/
|
||||
public function getReversedOrderCopy()
|
||||
{
|
||||
$result = clone $this;
|
||||
$result->data = array_reverse($result->data, true);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||
|
||||
/**
|
||||
* Class AbstractFacetPackage
|
||||
*/
|
||||
abstract class AbstractFacetPackage {
|
||||
/**
|
||||
* @var ObjectManagerInterface
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* @param ObjectManagerInterface $objectManager
|
||||
*/
|
||||
public function setObjectManager(ObjectManagerInterface $objectManager)
|
||||
{
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getParserClassName();
|
||||
|
||||
/**
|
||||
* @return FacetParserInterface
|
||||
* @throws InvalidFacetPackageException
|
||||
*/
|
||||
public function getParser()
|
||||
{
|
||||
$parser = $this->objectManager->get($this->getParserClassName());
|
||||
if (!$parser instanceof FacetParserInterface) {
|
||||
throw new InvalidFacetPackageException('Invalid parser for package ' . __CLASS__);
|
||||
}
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUrlDecoderClassName() {
|
||||
return (string)DefaultUrlDecoder::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidUrlDecoderException
|
||||
* @return FacetUrlDecoderInterface
|
||||
*/
|
||||
public function getUrlDecoder()
|
||||
{
|
||||
$urlDecoder = $this->objectManager->get($this->getUrlDecoderClassName());
|
||||
if (!$urlDecoder instanceof FacetUrlDecoderInterface) {
|
||||
throw new InvalidUrlDecoderException('Invalid urldecoder for package ' . __CLASS__);
|
||||
}
|
||||
return $urlDecoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getQueryBuilderClassName() {
|
||||
return (string)DefaultFacetQueryBuilder::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidQueryBuilderException
|
||||
* @return FacetQueryBuilderInterface
|
||||
*/
|
||||
public function getQueryBuilder()
|
||||
{
|
||||
$urlDecoder = $this->objectManager->get($this->getQueryBuilderClassName());
|
||||
if(!$urlDecoder instanceof FacetQueryBuilderInterface) {
|
||||
throw new InvalidQueryBuilderException('Invalid querybuilder for package ' . __CLASS__);
|
||||
}
|
||||
return $urlDecoder;
|
||||
}
|
||||
}
|
189
Classes/Domain/Search/ResultSet/Facets/AbstractFacetParser.php
Normal file
189
Classes/Domain/Search/ResultSet/Facets/AbstractFacetParser.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\AbstractOptionsFacet;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* Class AbstractFacetParser
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
abstract class AbstractFacetParser implements FacetParserInterface
|
||||
{
|
||||
/**
|
||||
* @var ContentObjectRenderer
|
||||
*/
|
||||
protected static $reUseAbleContentObject;
|
||||
|
||||
/**
|
||||
* @var ObjectManagerInterface
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* @param ObjectManagerInterface $objectManager
|
||||
*/
|
||||
public function injectObjectManager(ObjectManagerInterface $objectManager)
|
||||
{
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ContentObjectRenderer
|
||||
*/
|
||||
protected function getReUseAbleContentObject()
|
||||
{
|
||||
/** @var $contentObject ContentObjectRenderer */
|
||||
if (self::$reUseAbleContentObject !== null) {
|
||||
return self::$reUseAbleContentObject;
|
||||
}
|
||||
|
||||
self::$reUseAbleContentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
|
||||
return self::$reUseAbleContentObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $configuration
|
||||
* @return string
|
||||
*/
|
||||
protected function getPlainLabelOrApplyCObject($configuration)
|
||||
{
|
||||
// when no label is configured we return an empty string
|
||||
if (!isset($configuration['label'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// when no sub configuration is set, we use the string, configured as label
|
||||
if (!isset($configuration['label.'])) {
|
||||
return $configuration['label'];
|
||||
}
|
||||
|
||||
// when label and label. was set, we apply the cObject
|
||||
return $this->getReUseAbleContentObject()->cObjGetSingle($configuration['label'], $configuration['label.']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param integer $count
|
||||
* @param string $facetName
|
||||
* @param array $facetConfiguration
|
||||
* @return string
|
||||
*/
|
||||
protected function getLabelFromRenderingInstructions($value, $count, $facetName, $facetConfiguration)
|
||||
{
|
||||
$hasRenderingInstructions = isset($facetConfiguration['renderingInstruction']) && isset($facetConfiguration['renderingInstruction.']);
|
||||
if (!$hasRenderingInstructions) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$this->getReUseAbleContentObject()->start(['optionValue' => $value, 'optionCount' => $count, 'facetName' => $facetName]);
|
||||
return $this->getReUseAbleContentObject()->cObjGetSingle(
|
||||
$facetConfiguration['renderingInstruction'],
|
||||
$facetConfiguration['renderingInstruction.']
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the active facetValue for a facet from the search request.
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $facetName
|
||||
* @return array
|
||||
*/
|
||||
protected function getActiveFacetValuesFromRequest(SearchResultSet $resultSet, $facetName)
|
||||
{
|
||||
$activeFacetValues = $resultSet->getUsedSearchRequest()->getActiveFacetValuesByName($facetName);
|
||||
$activeFacetValues = is_array($activeFacetValues) ? $activeFacetValues : [];
|
||||
|
||||
return $activeFacetValues;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $facetValuesFromSolrResponse
|
||||
* @param array $facetValuesFromSearchRequest
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getMergedFacetValueFromSearchRequestAndSolrResponse($facetValuesFromSolrResponse, $facetValuesFromSearchRequest)
|
||||
{
|
||||
$facetValueItemsToCreate = $facetValuesFromSolrResponse;
|
||||
|
||||
foreach ($facetValuesFromSearchRequest as $valueFromRequest) {
|
||||
// if we have options in the request that have not been in the response we add them with a count of 0
|
||||
if (!isset($facetValueItemsToCreate[$valueFromRequest])) {
|
||||
$facetValueItemsToCreate[$valueFromRequest] = 0;
|
||||
}
|
||||
}
|
||||
return $facetValueItemsToCreate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AbstractOptionsFacet $facet
|
||||
* @param array $facetConfiguration
|
||||
* @return AbstractOptionsFacet
|
||||
*/
|
||||
protected function applyManualSortOrder(AbstractOptionsFacet $facet, array $facetConfiguration)
|
||||
{
|
||||
if (!isset($facetConfiguration['manualSortOrder'])) {
|
||||
return $facet;
|
||||
}
|
||||
$fields = GeneralUtility::trimExplode(',', $facetConfiguration['manualSortOrder']);
|
||||
// @extensionScannerIgnoreLine
|
||||
$sortedOptions = $facet->getOptions()->getManualSortedCopy($fields);
|
||||
|
||||
// @extensionScannerIgnoreLine
|
||||
$facet->setOptions($sortedOptions);
|
||||
|
||||
return $facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AbstractOptionsFacet $facet
|
||||
* @param array $facetConfiguration
|
||||
* @return AbstractOptionsFacet
|
||||
*/
|
||||
protected function applyReverseOrder(AbstractOptionsFacet $facet, array $facetConfiguration)
|
||||
{
|
||||
if (empty($facetConfiguration['reverseOrder'])) {
|
||||
return $facet;
|
||||
}
|
||||
|
||||
// @extensionScannerIgnoreLine
|
||||
$facet->setOptions($facet->getOptions()->getReversedOrderCopy());
|
||||
|
||||
return $facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $facetConfiguration
|
||||
* @return boolean
|
||||
*/
|
||||
protected function getIsExcludedFacetValue($value, array $facetConfiguration)
|
||||
{
|
||||
if (!isset($facetConfiguration['excludeValues'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$excludedValue = GeneralUtility::trimExplode(',', $facetConfiguration['excludeValues']);
|
||||
return in_array($value, $excludedValue);
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\System\Configuration\TypoScriptConfiguration;
|
||||
|
||||
class DefaultFacetQueryBuilder implements FacetQueryBuilderInterface {
|
||||
|
||||
/**
|
||||
* @param string $facetName
|
||||
* @param TypoScriptConfiguration $configuration
|
||||
* @return array
|
||||
*/
|
||||
public function build($facetName, TypoScriptConfiguration $configuration)
|
||||
{
|
||||
$facetParameters = [];
|
||||
$facetConfiguration = $configuration->getSearchFacetingFacetByName($facetName);
|
||||
|
||||
$tags = $this->buildExcludeTags($facetConfiguration, $configuration);
|
||||
$facetParameters['facet.field'][] = $tags . $facetConfiguration['field'];
|
||||
|
||||
$sortingExpression = new SortingExpression();
|
||||
$facetSortExpression = $sortingExpression->getForFacet($facetConfiguration['sortBy']);
|
||||
if (!empty($facetSortExpression)) {
|
||||
$facetParameters['f.' . $facetConfiguration['field'] . '.facet.sort'] = $facetSortExpression;
|
||||
}
|
||||
|
||||
return $facetParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $facetConfiguration
|
||||
* @param TypoScriptConfiguration $configuration
|
||||
* @return string
|
||||
*/
|
||||
protected function buildExcludeTags(array $facetConfiguration, TypoScriptConfiguration $configuration)
|
||||
{
|
||||
// simple for now, may add overrides f.<field_name>.facet.* later
|
||||
if ($configuration->getSearchFacetingKeepAllFacetsOnSelection()) {
|
||||
$facets = [];
|
||||
foreach ($configuration->getSearchFacetingFacets() as $facet) {
|
||||
$facets[] = $facet['field'];
|
||||
}
|
||||
|
||||
return '{!ex=' . implode(',', $facets) . '}';
|
||||
} elseif ($facetConfiguration['keepAllOptionsOnSelection'] == 1) {
|
||||
return '{!ex=' . $facetConfiguration['field'] . '}';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
31
Classes/Domain/Search/ResultSet/Facets/DefaultUrlDecoder.php
Normal file
31
Classes/Domain/Search/ResultSet/Facets/DefaultUrlDecoder.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
class DefaultUrlDecoder implements FacetUrlDecoderInterface {
|
||||
|
||||
/**
|
||||
* Parses the query filter from GET parameters in the URL and translates it
|
||||
* to a Lucene filter value.
|
||||
*
|
||||
* @param string $value the filter query from plugin
|
||||
* @param array $configuration Facet configuration
|
||||
* @return string Value to be used in a Lucene filter
|
||||
*/
|
||||
public function decode($value, array $configuration = [])
|
||||
{
|
||||
return '"' . addslashes($value) . '"';
|
||||
}
|
||||
}
|
78
Classes/Domain/Search/ResultSet/Facets/FacetCollection.php
Normal file
78
Classes/Domain/Search/ResultSet/Facets/FacetCollection.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
use WapplerSystems\Meilisearch\System\Data\AbstractCollection;
|
||||
|
||||
/**
|
||||
* Class FacetCollection
|
||||
*/
|
||||
class FacetCollection extends AbstractCollection
|
||||
{
|
||||
|
||||
/**
|
||||
* @param AbstractFacet $facet
|
||||
*/
|
||||
public function addFacet(AbstractFacet $facet)
|
||||
{
|
||||
$this->data[$facet->getName()] = $facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FacetCollection
|
||||
*/
|
||||
public function getUsed()
|
||||
{
|
||||
return $this->getFilteredCopy(
|
||||
function(AbstractFacet $facet) {
|
||||
return $facet->getIsUsed() && $facet->getIncludeInUsedFacets();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FacetCollection
|
||||
*/
|
||||
public function getAvailable()
|
||||
{
|
||||
return $this->getFilteredCopy(
|
||||
function(AbstractFacet $facet) {
|
||||
return $facet->getIsAvailable() && $facet->getIncludeInAvailableFacets() && $facet->getAllRequirementsMet();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $requiredGroup
|
||||
* @return AbstractCollection
|
||||
*/
|
||||
public function getByGroupName($requiredGroup = 'all')
|
||||
{
|
||||
return $this->getFilteredCopy(
|
||||
function(AbstractFacet $facet) use ($requiredGroup) {
|
||||
return $facet->getGroupName() == $requiredGroup;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $requiredName
|
||||
* @return AbstractCollection
|
||||
*/
|
||||
public function getByName($requiredName) {
|
||||
return $this->getFilteredCopy(
|
||||
function(AbstractFacet $facet) use ($requiredName) {
|
||||
return $facet->getName() == $requiredName;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $position
|
||||
* @return AbstractFacet
|
||||
*/
|
||||
public function getByPosition($position)
|
||||
{
|
||||
return parent::getByPosition($position);
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
|
||||
/**
|
||||
* Interface FacetParserInterface
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
interface FacetParserInterface
|
||||
{
|
||||
/**
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $facetName
|
||||
* @param array $facetConfiguration
|
||||
* @return AbstractFacet|null
|
||||
*/
|
||||
public function parse(SearchResultSet $resultSet, $facetName, array $facetConfiguration);
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2012-2015 Ingo Renner <ingo@typo3.org>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
use WapplerSystems\Meilisearch\System\Configuration\TypoScriptConfiguration;
|
||||
|
||||
/**
|
||||
* Query Filter Encoder Interface
|
||||
*
|
||||
* @author Ingo Renner <ingo@typo3.org>
|
||||
*/
|
||||
interface FacetQueryBuilderInterface
|
||||
{
|
||||
/**
|
||||
* @param string $facetName
|
||||
* @param TypoScriptConfiguration $configuration
|
||||
* @return array
|
||||
*/
|
||||
public function build($facetName, TypoScriptConfiguration $configuration);
|
||||
}
|
108
Classes/Domain/Search/ResultSet/Facets/FacetRegistry.php
Normal file
108
Classes/Domain/Search/ResultSet/Facets/FacetRegistry.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\Hierarchy\HierarchyPackage;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\Options\OptionsPackage;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\QueryGroup\QueryGroupPackage;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\DateRange\DateRangePackage;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\NumericRange\NumericRangePackage;
|
||||
use WapplerSystems\Meilisearch\System\Object\AbstractClassRegistry;
|
||||
|
||||
/**
|
||||
* Class FacetRegistry
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class FacetRegistry extends AbstractClassRegistry
|
||||
{
|
||||
/**
|
||||
* Array of available parser classNames
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $classMap = [
|
||||
'options' => OptionsPackage::class,
|
||||
'hierarchy' => HierarchyPackage::class,
|
||||
'queryGroup' => QueryGroupPackage::class,
|
||||
'dateRange' => DateRangePackage::class,
|
||||
'numericRange' => NumericRangePackage::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Default parser className
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultClass = OptionsPackage::class;
|
||||
|
||||
/**
|
||||
* Get defaultParser
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultPackage()
|
||||
{
|
||||
return $this->defaultClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set defaultParser
|
||||
*
|
||||
* @param string $defaultPackageClassName
|
||||
*/
|
||||
public function setDefaultPackage($defaultPackageClassName)
|
||||
{
|
||||
$this->defaultClass = $defaultPackageClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get registered parser classNames
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPackages()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $className
|
||||
* @param string $type
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function registerPackage($className, $type)
|
||||
{
|
||||
return $this->register($className, $type, AbstractFacetPackage::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get package
|
||||
*
|
||||
* @param string $type
|
||||
* @return AbstractFacetPackage
|
||||
* @throws InvalidFacetPackageException
|
||||
*/
|
||||
public function getPackage($type)
|
||||
{
|
||||
$instance = $this->getInstance($type);
|
||||
if (!$instance instanceof AbstractFacetPackage) {
|
||||
throw new InvalidFacetPackageException('Invalid class registered for ' . htmlspecialchars($type));
|
||||
}
|
||||
$instance->setObjectManager($this->objectManager);
|
||||
return $instance;
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2012-2015 Ingo Renner <ingo@typo3.org>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
/**
|
||||
* The facet url encode is responsible to encode and decode values for EXT:meilisearch urls.
|
||||
*
|
||||
* @author Ingo Renner <ingo@typo3.org>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
interface FacetUrlDecoderInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Parses the query filter from GET parameters in the URL and translates it
|
||||
* to a Lucene filter value.
|
||||
*
|
||||
* @param string $value the filter query from plugin
|
||||
* @param array $configuration Facet configuration
|
||||
* @return string Value to be used in a Lucene filter
|
||||
*/
|
||||
public function decode($value, array $configuration = []);
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2017- Timo Hund <timo.hund@dkd.de>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
class InvalidFacetPackageException extends \Exception {}
|
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2017- Timo Hund <timo.hund@dkd.de>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
class InvalidFacetParserException extends \Exception {}
|
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2017- Timo Hund <timo.hund@dkd.de>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
class InvalidQueryBuilderException extends \Exception {}
|
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2017- Timo Hund <timo.hund@dkd.de>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
class InvalidUrlDecoderException extends \Exception {}
|
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacet;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetItem;
|
||||
|
||||
/**
|
||||
* Base class for all facet items that are represented as option
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class AbstractOptionFacetItem extends AbstractFacetItem
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $value = '';
|
||||
|
||||
/**
|
||||
* @param AbstractFacet $facet
|
||||
* @param string $label
|
||||
* @param string $value
|
||||
* @param int $documentCount
|
||||
* @param bool $selected
|
||||
* @param array $metrics
|
||||
*/
|
||||
public function __construct(AbstractFacet $facet, $label = '', $value = '', $documentCount = 0, $selected = false, $metrics = [])
|
||||
{
|
||||
$this->value = $value;
|
||||
parent::__construct($facet, $label, $documentCount, $selected, $metrics);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUriValue()
|
||||
{
|
||||
return $this->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCollectionKey()
|
||||
{
|
||||
return $this->getValue();
|
||||
}
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacet;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
|
||||
/**
|
||||
* Class AbstractOptionsFacet
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class AbstractOptionsFacet extends AbstractFacet
|
||||
{
|
||||
|
||||
/**
|
||||
* @var OptionCollection
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* OptionsFacet constructor
|
||||
*
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $name
|
||||
* @param string $field
|
||||
* @param string $label
|
||||
* @param array $configuration Facet configuration passed from typoscript
|
||||
*/
|
||||
public function __construct(SearchResultSet $resultSet, $name, $field, $label = '', array $configuration = [])
|
||||
{
|
||||
parent::__construct($resultSet, $name, $field, $label, $configuration);
|
||||
$this->options = new OptionCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OptionCollection
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OptionCollection $options
|
||||
*/
|
||||
public function setOptions($options)
|
||||
{
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AbstractOptionFacetItem $option
|
||||
*/
|
||||
public function addOption(AbstractOptionFacetItem $option)
|
||||
{
|
||||
$this->options->add($option);
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of this method should return a "flatten" collection of all items.
|
||||
*
|
||||
* @return OptionCollection
|
||||
*/
|
||||
public function getAllFacetItems()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get facet partial name used for rendering the facet
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPartialName()
|
||||
{
|
||||
return !empty($this->configuration['partialName']) ? $this->configuration['partialName'] : 'Options';
|
||||
}
|
||||
}
|
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\Hierarchy;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacet;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetItemCollection;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
|
||||
/**
|
||||
* Value object that represent a options facet.
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class HierarchyFacet extends AbstractFacet
|
||||
{
|
||||
const TYPE_HIERARCHY = 'hierarchy';
|
||||
|
||||
/**
|
||||
* String
|
||||
* @var string
|
||||
*/
|
||||
protected static $type = self::TYPE_HIERARCHY;
|
||||
|
||||
/**
|
||||
* @var NodeCollection
|
||||
*/
|
||||
protected $childNodes;
|
||||
|
||||
/**
|
||||
* @var NodeCollection
|
||||
*/
|
||||
protected $allNodes;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $nodesByKey = [];
|
||||
|
||||
/**
|
||||
* OptionsFacet constructor
|
||||
*
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $name
|
||||
* @param string $field
|
||||
* @param string $label
|
||||
* @param array $configuration Facet configuration passed from typoscript
|
||||
*/
|
||||
public function __construct(SearchResultSet $resultSet, $name, $field, $label = '', array $configuration = [])
|
||||
{
|
||||
parent::__construct($resultSet, $name, $field, $label, $configuration);
|
||||
$this->childNodes = new NodeCollection();
|
||||
$this->allNodes = new NodeCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
*/
|
||||
public function addChildNode(Node $node)
|
||||
{
|
||||
$this->childNodes->add($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NodeCollection
|
||||
*/
|
||||
public function getChildNodes()
|
||||
{
|
||||
return $this->childNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new node on the right position with the right parent node.
|
||||
*
|
||||
* @param string $parentKey
|
||||
* @param string $key
|
||||
* @param string $label
|
||||
* @param string $value
|
||||
* @param integer $count
|
||||
* @param boolean $selected
|
||||
*/
|
||||
public function createNode($parentKey, $key, $label, $value, $count, $selected)
|
||||
{
|
||||
/** @var $parentNode Node|null */
|
||||
$parentNode = isset($this->nodesByKey[$parentKey]) ? $this->nodesByKey[$parentKey] : null;
|
||||
/** @var Node $node */
|
||||
$node = $this->objectManager->get(Node::class, $this, $parentNode, $key, $label, $value, $count, $selected);
|
||||
$this->nodesByKey[$key] = $node;
|
||||
|
||||
if ($parentNode === null) {
|
||||
$this->addChildNode($node);
|
||||
} else {
|
||||
$parentNode->addChildNode($node);
|
||||
}
|
||||
|
||||
$this->allNodes->add($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get facet partial name used for rendering the facet
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPartialName()
|
||||
{
|
||||
return !empty($this->configuration['partialName']) ? $this->configuration['partialName'] : 'Hierarchy';
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of this method should return a "flatten" collection of all items.
|
||||
*
|
||||
* @return AbstractFacetItemCollection
|
||||
*/
|
||||
public function getAllFacetItems()
|
||||
{
|
||||
return $this->allNodes;
|
||||
}
|
||||
}
|
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\Hierarchy;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetParser;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
use WapplerSystems\Meilisearch\System\Solr\ParsingUtil;
|
||||
|
||||
/**
|
||||
* Class HierarchyFacetParser
|
||||
*/
|
||||
class HierarchyFacetParser extends AbstractFacetParser
|
||||
{
|
||||
/**
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $facetName
|
||||
* @param array $facetConfiguration
|
||||
* @return HierarchyFacet|null
|
||||
*/
|
||||
public function parse(SearchResultSet $resultSet, $facetName, array $facetConfiguration)
|
||||
{
|
||||
$response = $resultSet->getResponse();
|
||||
$fieldName = $facetConfiguration['field'];
|
||||
$label = $this->getPlainLabelOrApplyCObject($facetConfiguration);
|
||||
$optionsFromSolrResponse = isset($response->facet_counts->facet_fields->{$fieldName}) ? ParsingUtil::getMapArrayFromFlatArray($response->facet_counts->facet_fields->{$fieldName}) : [];
|
||||
$optionsFromRequest = $this->getActiveFacetValuesFromRequest($resultSet, $facetName);
|
||||
$hasOptionsInResponse = !empty($optionsFromSolrResponse);
|
||||
$hasSelectedOptionsInRequest = count($optionsFromRequest) > 0;
|
||||
$hasNoOptionsToShow = !$hasOptionsInResponse && !$hasSelectedOptionsInRequest;
|
||||
$hideEmpty = !$resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration()->getSearchFacetingShowEmptyFacetsByName($facetName);
|
||||
|
||||
if ($hasNoOptionsToShow && $hideEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var $facet HierarchyFacet */
|
||||
$facet = $this->objectManager->get(HierarchyFacet::class, $resultSet, $facetName, $fieldName, $label, $facetConfiguration);
|
||||
|
||||
$hasActiveOptions = count($optionsFromRequest) > 0;
|
||||
$facet->setIsUsed($hasActiveOptions);
|
||||
|
||||
$facet->setIsAvailable($hasOptionsInResponse);
|
||||
|
||||
$nodesToCreate = $this->getMergedFacetValueFromSearchRequestAndSolrResponse($optionsFromSolrResponse, $optionsFromRequest);
|
||||
|
||||
if ($this->facetOptionsMustBeResorted($facetConfiguration)) {
|
||||
$nodesToCreate = $this->sortFacetOptionsInNaturalOrder($nodesToCreate);
|
||||
}
|
||||
|
||||
foreach ($nodesToCreate as $value => $count) {
|
||||
if ($this->getIsExcludedFacetValue($value, $facetConfiguration)) {
|
||||
continue;
|
||||
}
|
||||
$isActive = in_array($value, $optionsFromRequest);
|
||||
$delimiterPosition = strpos($value, '-');
|
||||
$path = substr($value, $delimiterPosition + 1);
|
||||
$pathArray = $this->getPathAsArray($path);
|
||||
$key = array_pop($pathArray);
|
||||
$parentKey = array_pop($pathArray);
|
||||
$value = '/' . $path;
|
||||
$label = $this->getLabelFromRenderingInstructions($key, $count, $facetName, $facetConfiguration);
|
||||
|
||||
$facet->createNode($parentKey, $key, $label, $value, $count, $isActive);
|
||||
}
|
||||
|
||||
return $facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts facet options in natural order.
|
||||
* Options must be sorted in natural order,
|
||||
* because lower nesting levels must be instantiated first, to serve as parents for higher nested levels.
|
||||
* See implementation of HierarchyFacet::createNode().
|
||||
*
|
||||
* @param array $flatOptionsListForFacet
|
||||
* @return void sorted list of facet options
|
||||
*/
|
||||
protected function sortFacetOptionsInNaturalOrder(array $flatOptionsListForHierarchyFacet)
|
||||
{
|
||||
uksort($flatOptionsListForHierarchyFacet, "strnatcmp");
|
||||
return $flatOptionsListForHierarchyFacet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if options must be resorted.
|
||||
*
|
||||
* Apache Solr facet.sort can be set globally or per facet.
|
||||
* Relevant TypoScript paths:
|
||||
* plugin.tx_meilisearch.search.faceting.sortBy causes facet.sort Apache Solr parameter
|
||||
* plugin.tx_meilisearch.search.faceting.facets.[facetName].sortBy causes f.<fieldname>.facet.sort parameter
|
||||
*
|
||||
* see: https://lucene.apache.org/solr/guide/6_6/faceting.html#Faceting-Thefacet.sortParameter
|
||||
* see: https://wiki.apache.org/solr/SimpleFacetParameters#facet.sort : "This parameter can be specified on a per field basis."
|
||||
*
|
||||
* @param array $facetConfiguration
|
||||
* @return bool
|
||||
*/
|
||||
protected function facetOptionsMustBeResorted(array $facetConfiguration)
|
||||
{
|
||||
if (isset($facetConfiguration['sortBy']) && $facetConfiguration['sortBy'] === 'index') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to get the path array from a hierarchical facet. It substitutes escaped slashes to keep them
|
||||
* when they are used inside a facetValue.
|
||||
*
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
protected function getPathAsArray($path)
|
||||
{
|
||||
$path = str_replace('\/', '@@@', $path);
|
||||
$path = rtrim($path, "/");
|
||||
$segments = explode('/', $path);
|
||||
return array_map(function($item) {
|
||||
return str_replace('@@@', '/', $item);
|
||||
}, $segments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the active facetValue for a facet from the search request.
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $facetName
|
||||
* @return array
|
||||
*/
|
||||
protected function getActiveFacetValuesFromRequest(SearchResultSet $resultSet, $facetName)
|
||||
{
|
||||
$activeFacetValues = [];
|
||||
$values = $resultSet->getUsedSearchRequest()->getActiveFacetValuesByName($facetName);
|
||||
|
||||
foreach (is_array($values) ? $values : [] as $valueFromRequest) {
|
||||
// Attach the 'depth' param again to the value
|
||||
if (strpos($valueFromRequest, '-') === false) {
|
||||
$valueFromRequest = HierarchyTool::substituteSlashes($valueFromRequest);
|
||||
$valueFromRequest = trim($valueFromRequest, '/');
|
||||
$valueFromRequest = (count(explode('/', $valueFromRequest)) - 1) . '-' . $valueFromRequest . '/';
|
||||
$valueFromRequest = HierarchyTool::unSubstituteSlashes($valueFromRequest);
|
||||
}
|
||||
$activeFacetValues[] = $valueFromRequest;
|
||||
}
|
||||
return $activeFacetValues;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\Hierarchy;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetPackage;
|
||||
|
||||
/**
|
||||
* Class HierarchyPackage
|
||||
*/
|
||||
class HierarchyPackage extends AbstractFacetPackage {
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getParserClassName() {
|
||||
return (string)HierarchyFacetParser::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUrlDecoderClassName() {
|
||||
return (string)HierarchyUrlDecoder::class;
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\Hierarchy;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
class HierarchyTool
|
||||
{
|
||||
/**
|
||||
* Replaces all escaped slashes in a hierarchy path with @@@slash@@@ to afterwards
|
||||
* only have slashes in the content that are real path separators.
|
||||
*
|
||||
* @param string $pathWithContentSlashes
|
||||
* @return string
|
||||
*/
|
||||
public static function substituteSlashes(string $pathWithContentSlashes): string
|
||||
{
|
||||
return (string)str_replace('\/', '@@@slash@@@', $pathWithContentSlashes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces @@@slash@@@ with \/ to have the path usable for solr again.
|
||||
*
|
||||
* @param string $pathWithReplacedContentSlashes
|
||||
* @return string
|
||||
*/
|
||||
public static function unSubstituteSlashes(string $pathWithReplacedContentSlashes): string
|
||||
{
|
||||
return (string)str_replace('@@@slash@@@', '\/', $pathWithReplacedContentSlashes);
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\Hierarchy;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2012-2015 Ingo Renner <ingo@typo3.org>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\FacetUrlDecoderInterface;
|
||||
|
||||
/**
|
||||
* Filter encoder to build Solr hierarchy queries from tx_meilisearch[filter]
|
||||
*
|
||||
* @author Ingo Renner <ingo@typo3.org>
|
||||
*/
|
||||
class HierarchyUrlDecoder implements FacetUrlDecoderInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Delimiter for hierarchies in the URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const DELIMITER = '/';
|
||||
|
||||
/**
|
||||
* Parses the given hierarchy filter and returns a Solr filter query.
|
||||
*
|
||||
* @param string $hierarchy The hierarchy filter query.
|
||||
* @param array $configuration Facet configuration
|
||||
* @return string Lucene query language filter to be used for querying Solr
|
||||
*/
|
||||
public function decode($hierarchy, array $configuration = [])
|
||||
{
|
||||
$escapedHierarchy = HierarchyTool::substituteSlashes($hierarchy);
|
||||
|
||||
$escapedHierarchy = substr($escapedHierarchy, 1);
|
||||
$escapedHierarchy = rtrim($escapedHierarchy, '/');
|
||||
$hierarchyItems = explode(self::DELIMITER, $escapedHierarchy);
|
||||
$filterContent = (count($hierarchyItems) - 1) . '-' . $escapedHierarchy . '/';
|
||||
|
||||
$filterContent = HierarchyTool::unSubstituteSlashes($filterContent);
|
||||
|
||||
return '"' . str_replace("\\", "\\\\", $filterContent) . '"';
|
||||
}
|
||||
}
|
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\Hierarchy;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\AbstractOptionFacetItem;
|
||||
|
||||
/**
|
||||
* Value object that represent an option of a options facet.
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class Node extends AbstractOptionFacetItem
|
||||
{
|
||||
|
||||
/**
|
||||
* @var NodeCollection
|
||||
*/
|
||||
protected $childNodes;
|
||||
|
||||
/**
|
||||
* @var Node
|
||||
*/
|
||||
protected $parentNode;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
protected $depth;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* @param HierarchyFacet $facet
|
||||
* @param Node $parentNode
|
||||
* @param string $key
|
||||
* @param string $label
|
||||
* @param string $value
|
||||
* @param int $documentCount
|
||||
* @param bool $selected
|
||||
*/
|
||||
public function __construct(HierarchyFacet $facet, $parentNode = null, $key = '', $label = '', $value = '', $documentCount = 0, $selected = false)
|
||||
{
|
||||
parent::__construct($facet, $label, $value, $documentCount, $selected);
|
||||
$this->value = $value;
|
||||
$this->childNodes = new NodeCollection();
|
||||
$this->parentNode = $parentNode;
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getKey()
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
*/
|
||||
public function addChildNode(Node $node)
|
||||
{
|
||||
$this->childNodes->add($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NodeCollection
|
||||
*/
|
||||
public function getChildNodes()
|
||||
{
|
||||
return $this->childNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Node|null
|
||||
*/
|
||||
public function getParentNode()
|
||||
{
|
||||
return $this->parentNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getHasParentNode()
|
||||
{
|
||||
return $this->parentNode !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getHasChildNodeSelected()
|
||||
{
|
||||
/** @var Node $childNode */
|
||||
foreach ($this->childNodes as $childNode) {
|
||||
if ($childNode->getSelected()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\Hierarchy;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetItemCollection;
|
||||
|
||||
/**
|
||||
* Collection for facet options.
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class NodeCollection extends AbstractFacetItemCollection
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
* @return NodeCollection
|
||||
*/
|
||||
public function add($node)
|
||||
{
|
||||
return parent::add($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $position
|
||||
* @return Node
|
||||
*/
|
||||
public function getByPosition($position)
|
||||
{
|
||||
return parent::getByPosition($position);
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetItemCollection;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\Options\Option;
|
||||
|
||||
/**
|
||||
* Collection for facet options.
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class OptionCollection extends AbstractFacetItemCollection
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of prefixes from the option labels.
|
||||
*
|
||||
* Red, Blue, Green => r, g, b
|
||||
*
|
||||
* Can be used in combination with getByPrefix() to group facet options by prefix (e.g. alphabetical).
|
||||
*
|
||||
* @param int $length
|
||||
* @return array
|
||||
*/
|
||||
public function getLowercaseLabelPrefixes($length = 1)
|
||||
{
|
||||
$prefixes = $this->getLabelPrefixes($length);
|
||||
return array_map('mb_strtolower', $prefixes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filteredPrefix
|
||||
* @return AbstractFacetItemCollection
|
||||
*/
|
||||
public function getByLowercaseLabelPrefix($filteredPrefix)
|
||||
{
|
||||
return $this->getFilteredCopy(function(Option $option) use ($filteredPrefix)
|
||||
{
|
||||
$filteredPrefixLength = mb_strlen($filteredPrefix);
|
||||
$currentPrefix = mb_substr(mb_strtolower($option->getLabel()), 0, $filteredPrefixLength);
|
||||
|
||||
return $currentPrefix === $filteredPrefix;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $length
|
||||
* @return array
|
||||
*/
|
||||
protected function getLabelPrefixes($length = 1) : array
|
||||
{
|
||||
$prefixes = [];
|
||||
foreach ($this->data as $option) {
|
||||
/** @var $option Option */
|
||||
$prefix = mb_substr($option->getLabel(), 0, $length);
|
||||
$prefixes[$prefix] = $prefix;
|
||||
}
|
||||
|
||||
return array_values($prefixes);
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\Options;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\AbstractOptionFacetItem;
|
||||
|
||||
/**
|
||||
* Value object that represent an option of a options facet.
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class Option extends AbstractOptionFacetItem
|
||||
{
|
||||
/**
|
||||
* @param OptionsFacet $facet
|
||||
* @param string $label
|
||||
* @param string $value
|
||||
* @param int $documentCount
|
||||
* @param bool $selected
|
||||
* @param array $metrics
|
||||
*/
|
||||
public function __construct(OptionsFacet $facet, $label = '', $value = '', $documentCount = 0, $selected = false, $metrics = [])
|
||||
{
|
||||
parent::__construct($facet, $label, $value, $documentCount, $selected, $metrics);
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\Options;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\AbstractOptionsFacet;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
|
||||
/**
|
||||
* Value object that represent a options facet.
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class OptionsFacet extends AbstractOptionsFacet
|
||||
{
|
||||
const TYPE_OPTIONS = 'options';
|
||||
|
||||
/**
|
||||
* String
|
||||
* @var string
|
||||
*/
|
||||
protected static $type = self::TYPE_OPTIONS;
|
||||
|
||||
/**
|
||||
* OptionsFacet constructor
|
||||
*
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $name
|
||||
* @param string $field
|
||||
* @param string $label
|
||||
* @param array $configuration Facet configuration passed from typoscript
|
||||
*/
|
||||
public function __construct(SearchResultSet $resultSet, $name, $field, $label = '', array $configuration = [])
|
||||
{
|
||||
parent::__construct($resultSet, $name, $field, $label, $configuration);
|
||||
}
|
||||
}
|
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\Options;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetParser;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
use WapplerSystems\Meilisearch\System\Solr\ResponseAdapter;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
|
||||
|
||||
/**
|
||||
* Class OptionsFacetParser
|
||||
*/
|
||||
class OptionsFacetParser extends AbstractFacetParser
|
||||
{
|
||||
/**
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $dispatcher;
|
||||
|
||||
/**
|
||||
* @param Dispatcher $dispatcher
|
||||
*/
|
||||
public function injectDispatcher(Dispatcher $dispatcher)
|
||||
{
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $facetName
|
||||
* @param array $facetConfiguration
|
||||
* @return OptionsFacet|null
|
||||
*/
|
||||
public function parse(SearchResultSet $resultSet, $facetName, array $facetConfiguration)
|
||||
{
|
||||
$response = $resultSet->getResponse();
|
||||
$fieldName = $facetConfiguration['field'];
|
||||
$label = $this->getPlainLabelOrApplyCObject($facetConfiguration);
|
||||
$optionsFromSolrResponse = $this->getOptionsFromSolrResponse($facetName, $response);
|
||||
$metricsFromSolrResponse = $this->getMetricsFromSolrResponse($facetName, $response);
|
||||
$optionsFromRequest = $this->getActiveFacetValuesFromRequest($resultSet, $facetName);
|
||||
$hasOptionsInResponse = !empty($optionsFromSolrResponse);
|
||||
$hasSelectedOptionsInRequest = count($optionsFromRequest) > 0;
|
||||
$hasNoOptionsToShow = !$hasOptionsInResponse && !$hasSelectedOptionsInRequest;
|
||||
$hideEmpty = !$resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration()->getSearchFacetingShowEmptyFacetsByName($facetName);
|
||||
|
||||
if ($hasNoOptionsToShow && $hideEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var $facet OptionsFacet */
|
||||
$facet = $this->objectManager->get(
|
||||
OptionsFacet::class,
|
||||
$resultSet,
|
||||
$facetName,
|
||||
$fieldName,
|
||||
$label,
|
||||
$facetConfiguration
|
||||
);
|
||||
|
||||
$hasActiveOptions = count($optionsFromRequest) > 0;
|
||||
$facet->setIsUsed($hasActiveOptions);
|
||||
$facet->setIsAvailable($hasOptionsInResponse);
|
||||
|
||||
$optionsToCreate = $this->getMergedFacetValueFromSearchRequestAndSolrResponse($optionsFromSolrResponse, $optionsFromRequest);
|
||||
foreach ($optionsToCreate as $optionsValue => $count) {
|
||||
if ($this->getIsExcludedFacetValue($optionsValue, $facetConfiguration)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$isOptionsActive = in_array($optionsValue, $optionsFromRequest);
|
||||
$label = $this->getLabelFromRenderingInstructions($optionsValue, $count, $facetName, $facetConfiguration);
|
||||
$facet->addOption($this->objectManager->get(Option::class, $facet, $label, $optionsValue, $count, $isOptionsActive, $metricsFromSolrResponse[$optionsValue]));
|
||||
}
|
||||
|
||||
// after all options have been created we apply a manualSortOrder if configured
|
||||
// the sortBy (lex,..) is done by the solr server and triggered by the query, therefore it does not
|
||||
// need to be handled in the frontend.
|
||||
$this->applyManualSortOrder($facet, $facetConfiguration);
|
||||
$this->applyReverseOrder($facet, $facetConfiguration);
|
||||
|
||||
if(!is_null($this->dispatcher)) {
|
||||
$this->dispatcher->dispatch(__CLASS__, 'optionsParsed', [&$facet, $facetConfiguration]);
|
||||
}
|
||||
|
||||
return $facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $facetName
|
||||
* @param ResponseAdapter $response
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptionsFromSolrResponse($facetName, ResponseAdapter $response)
|
||||
{
|
||||
$optionsFromSolrResponse = [];
|
||||
if (!isset($response->facets->{$facetName})) {
|
||||
return $optionsFromSolrResponse;
|
||||
}
|
||||
|
||||
foreach ($response->facets->{$facetName}->buckets as $bucket) {
|
||||
$optionValue = $bucket->val;
|
||||
$optionCount = $bucket->count;
|
||||
$optionsFromSolrResponse[$optionValue] = $optionCount;
|
||||
}
|
||||
|
||||
return $optionsFromSolrResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $facetName
|
||||
* @param ResponseAdapter $response
|
||||
* @return array
|
||||
*/
|
||||
protected function getMetricsFromSolrResponse($facetName, ResponseAdapter $response)
|
||||
{
|
||||
$metricsFromSolrResponse = [];
|
||||
|
||||
if (!isset($response->facets->{$facetName}->buckets)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($response->facets->{$facetName}->buckets as $bucket) {
|
||||
$bucketVariables = get_object_vars($bucket);
|
||||
foreach ($bucketVariables as $key => $value) {
|
||||
if (strpos($key, 'metrics_') === 0) {
|
||||
$metricsKey = str_replace('metrics_', '', $key);
|
||||
$metricsFromSolrResponse[$bucket->val][$metricsKey] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $metricsFromSolrResponse;
|
||||
}
|
||||
}
|
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\Options;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\DefaultFacetQueryBuilder;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\FacetQueryBuilderInterface;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\SortingExpression;
|
||||
use WapplerSystems\Meilisearch\System\Configuration\TypoScriptConfiguration;
|
||||
|
||||
/**
|
||||
* Class OptionsFacetQueryBuilder
|
||||
*
|
||||
* The Options facet query builder builds the facets as json structure
|
||||
*
|
||||
* @Todo: When we use json faceting for other facets some logic of this class can be moved to the base class.
|
||||
*/
|
||||
class OptionsFacetQueryBuilder extends DefaultFacetQueryBuilder implements FacetQueryBuilderInterface {
|
||||
|
||||
/**
|
||||
* @param string $facetName
|
||||
* @param TypoScriptConfiguration $configuration
|
||||
* @return array
|
||||
*/
|
||||
public function build($facetName, TypoScriptConfiguration $configuration)
|
||||
{
|
||||
$facetParameters = [];
|
||||
$facetConfiguration = $configuration->getSearchFacetingFacetByName($facetName);
|
||||
|
||||
$jsonFacetOptions = [
|
||||
'type' => 'terms',
|
||||
'field' => $facetConfiguration['field'],
|
||||
];
|
||||
|
||||
$jsonFacetOptions['limit'] = $this->buildLimitForJson($facetConfiguration, $configuration);
|
||||
$jsonFacetOptions['mincount'] = $this->buildMincountForJson($facetConfiguration, $configuration);
|
||||
|
||||
$sorting = $this->buildSortingForJson($facetConfiguration);
|
||||
if (!empty($sorting)) {
|
||||
$jsonFacetOptions['sort'] = $sorting;
|
||||
}
|
||||
|
||||
if (is_array($facetConfiguration['metrics.'])) {
|
||||
foreach ($facetConfiguration['metrics.'] as $key => $value) {
|
||||
$jsonFacetOptions['facet']['metrics_' . $key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$excludeTags = $this->buildExcludeTagsForJson($facetConfiguration, $configuration);
|
||||
if (!empty($excludeTags)) {
|
||||
$jsonFacetOptions['domain']['excludeTags'] = $excludeTags;
|
||||
}
|
||||
|
||||
$facetParameters['json.facet'][$facetName] = $jsonFacetOptions;
|
||||
|
||||
return $facetParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $facetConfiguration
|
||||
* @param TypoScriptConfiguration $configuration
|
||||
* @return string
|
||||
*/
|
||||
protected function buildExcludeTagsForJson(array $facetConfiguration, TypoScriptConfiguration $configuration)
|
||||
{
|
||||
$excludeFields = [];
|
||||
|
||||
if ($configuration->getSearchFacetingKeepAllFacetsOnSelection()) {
|
||||
if (!$configuration->getSearchFacetingCountAllFacetsForSelection()) {
|
||||
// keepAllOptionsOnSelection globally active
|
||||
foreach ($configuration->getSearchFacetingFacets() as $facet) {
|
||||
$excludeFields[] = $facet['field'];
|
||||
}
|
||||
} else {
|
||||
$excludeFields[] = $facetConfiguration['field'];
|
||||
}
|
||||
}
|
||||
|
||||
$isKeepAllOptionsActiveForSingleFacet = $facetConfiguration['keepAllOptionsOnSelection'] == 1;
|
||||
if ($isKeepAllOptionsActiveForSingleFacet) {
|
||||
$excludeFields[] = $facetConfiguration['field'];
|
||||
}
|
||||
|
||||
if (!empty($facetConfiguration['additionalExcludeTags'])) {
|
||||
$excludeFields[] = $facetConfiguration['additionalExcludeTags'];
|
||||
}
|
||||
|
||||
return implode(',', array_unique($excludeFields));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $facetConfiguration
|
||||
* @param TypoScriptConfiguration $configuration
|
||||
* @return int
|
||||
*/
|
||||
protected function buildLimitForJson(array $facetConfiguration, TypoScriptConfiguration $configuration)
|
||||
{
|
||||
if (isset($facetConfiguration['facetLimit'])) {
|
||||
return (int)$facetConfiguration['facetLimit'];
|
||||
} elseif (!is_null($configuration->getSearchFacetingFacetLimit()) && $configuration->getSearchFacetingFacetLimit() >= 0) {
|
||||
return $configuration->getSearchFacetingFacetLimit();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $facetConfiguration
|
||||
* @param TypoScriptConfiguration $configuration
|
||||
* @return int
|
||||
*/
|
||||
protected function buildMincountForJson(array $facetConfiguration, TypoScriptConfiguration $configuration)
|
||||
{
|
||||
if (isset($facetConfiguration['minimumCount'])) {
|
||||
return (int)$facetConfiguration['minimumCount'];
|
||||
} elseif (!is_null($configuration->getSearchFacetingMinimumCount()) && (int)$configuration->getSearchFacetingMinimumCount() >= 0) {
|
||||
return $configuration->getSearchFacetingMinimumCount();
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $facetConfiguration
|
||||
* @return string
|
||||
*/
|
||||
protected function buildSortingForJson(array $facetConfiguration) {
|
||||
if (isset($facetConfiguration['sortBy'])) {
|
||||
$sortingExpression = new SortingExpression();
|
||||
$sorting = $facetConfiguration['sortBy'];
|
||||
$direction = $facetConfiguration['sortDirection'];
|
||||
return $sortingExpression->getForJsonFacet($sorting, $direction);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\Options;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetPackage;
|
||||
|
||||
/**
|
||||
* Class OptionsPackage
|
||||
*/
|
||||
class OptionsPackage extends AbstractFacetPackage {
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getParserClassName() {
|
||||
return (string)OptionsFacetParser::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getQueryBuilderClassName() {
|
||||
return (string)OptionsFacetQueryBuilder::class;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\QueryGroup;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\AbstractOptionFacetItem;
|
||||
|
||||
/**
|
||||
* Value object that represent an option of a queryGroup facet.
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class Option extends AbstractOptionFacetItem
|
||||
{
|
||||
/**
|
||||
* @param QueryGroupFacet $facet
|
||||
* @param string $label
|
||||
* @param string $value
|
||||
* @param int $documentCount
|
||||
* @param bool $selected
|
||||
*/
|
||||
public function __construct(QueryGroupFacet $facet, $label = '', $value = '', $documentCount = 0, $selected = false)
|
||||
{
|
||||
parent::__construct($facet, $label, $value, $documentCount, $selected);
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\QueryGroup;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\AbstractOptionsFacet;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
|
||||
/**
|
||||
* Class QueryGroupFacet
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class QueryGroupFacet extends AbstractOptionsFacet
|
||||
{
|
||||
const TYPE_QUERY_GROUP = 'queryGroup';
|
||||
|
||||
/**
|
||||
* String
|
||||
* @var string
|
||||
*/
|
||||
protected static $type = self::TYPE_QUERY_GROUP;
|
||||
|
||||
/**
|
||||
* OptionsFacet constructor
|
||||
*
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $name
|
||||
* @param string $field
|
||||
* @param string $label
|
||||
* @param array $configuration Facet configuration passed from typoscript
|
||||
*/
|
||||
public function __construct(SearchResultSet $resultSet, $name, $field, $label = '', array $configuration = [])
|
||||
{
|
||||
parent::__construct($resultSet, $name, $field, $label, $configuration);
|
||||
}
|
||||
}
|
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\QueryGroup;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetParser;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
use WapplerSystems\Meilisearch\System\Solr\ResponseAdapter;
|
||||
|
||||
/**
|
||||
* Class QueryGroupFacetParser
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class QueryGroupFacetParser extends AbstractFacetParser
|
||||
{
|
||||
|
||||
/**
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $facetName
|
||||
* @param array $facetConfiguration
|
||||
* @return QueryGroupFacet|null
|
||||
*/
|
||||
public function parse(SearchResultSet $resultSet, $facetName, array $facetConfiguration)
|
||||
{
|
||||
$response = $resultSet->getResponse();
|
||||
$fieldName = $facetConfiguration['field'];
|
||||
$label = $this->getPlainLabelOrApplyCObject($facetConfiguration);
|
||||
|
||||
$rawOptions = $this->getRawOptions($response, $fieldName);
|
||||
$noOptionsInResponse = $rawOptions === [];
|
||||
$hideEmpty = !$resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration()->getSearchFacetingShowEmptyFacetsByName($facetName);
|
||||
|
||||
if ($noOptionsInResponse && $hideEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var QueryGroupFacet $facet */
|
||||
$facet = $this->objectManager->get(
|
||||
QueryGroupFacet::class,
|
||||
$resultSet,
|
||||
$facetName,
|
||||
$fieldName,
|
||||
$label,
|
||||
$facetConfiguration
|
||||
);
|
||||
|
||||
$activeFacets = $resultSet->getUsedSearchRequest()->getActiveFacetNames();
|
||||
$facet->setIsUsed(in_array($facetName, $activeFacets, true));
|
||||
|
||||
if (!$noOptionsInResponse) {
|
||||
$facet->setIsAvailable(true);
|
||||
foreach ($rawOptions as $query => $count) {
|
||||
$value = $this->getValueByQuery($query, $facetConfiguration);
|
||||
// Skip unknown queries
|
||||
if ($value === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->getIsExcludedFacetValue($query, $facetConfiguration)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$isOptionsActive = $resultSet->getUsedSearchRequest()->getHasFacetValue($facetName, $value);
|
||||
$label = $this->getLabelFromRenderingInstructions(
|
||||
$value,
|
||||
$count,
|
||||
$facetName,
|
||||
$facetConfiguration
|
||||
);
|
||||
$facet->addOption($this->objectManager->get(Option::class, $facet, $label, $value, $count, $isOptionsActive));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// after all options have been created we apply a manualSortOrder if configured
|
||||
// the sortBy (lex,..) is done by the solr server and triggered by the query, therefore it does not
|
||||
// need to be handled in the frontend.
|
||||
$this->applyManualSortOrder($facet, $facetConfiguration);
|
||||
$this->applyReverseOrder($facet, $facetConfiguration);
|
||||
|
||||
return $facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw query options
|
||||
*
|
||||
* @param ResponseAdapter $response
|
||||
* @param string $fieldName
|
||||
* @return array
|
||||
*/
|
||||
protected function getRawOptions(ResponseAdapter $response, $fieldName)
|
||||
{
|
||||
$options = [];
|
||||
|
||||
foreach ($response->facet_counts->facet_queries as $rawValue => $count) {
|
||||
if ((int)$count === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// todo: add test cases to check if this is needed https://forge.typo3.org/issues/45440
|
||||
// remove tags from the facet.query response, for facet.field
|
||||
// and facet.range Solr does that on its own automatically
|
||||
$rawValue = preg_replace('/^\{!ex=[^\}]*\}(.*)/', '\\1', $rawValue);
|
||||
|
||||
list($field, $query) = explode(':', $rawValue, 2);
|
||||
if ($field === $fieldName) {
|
||||
$options[$query] = $count;
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $facetConfiguration
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getValueByQuery($query, array $facetConfiguration)
|
||||
{
|
||||
$value = null;
|
||||
foreach ($facetConfiguration['queryGroup.'] as $valueKey => $config) {
|
||||
if (isset($config['query']) && $config['query'] === $query) {
|
||||
$value = rtrim($valueKey, '.');
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\QueryGroup;
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\DefaultFacetQueryBuilder;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\FacetQueryBuilderInterface;
|
||||
use WapplerSystems\Meilisearch\System\Configuration\TypoScriptConfiguration;
|
||||
|
||||
class QueryGroupFacetQueryBuilder extends DefaultFacetQueryBuilder implements FacetQueryBuilderInterface {
|
||||
|
||||
/**
|
||||
* @param string $facetName
|
||||
* @param TypoScriptConfiguration $configuration
|
||||
* @return array
|
||||
*/
|
||||
public function build($facetName, TypoScriptConfiguration $configuration)
|
||||
{
|
||||
$facetParameters = [];
|
||||
$facetConfiguration = $configuration->getSearchFacetingFacetByName($facetName);
|
||||
foreach ($facetConfiguration['queryGroup.'] as $queryName => $queryConfiguration) {
|
||||
$tags = $this->buildExcludeTags($facetConfiguration, $configuration);
|
||||
$facetParameters['facet.query'][] = $tags . $facetConfiguration['field'] . ':' . $queryConfiguration['query'];
|
||||
}
|
||||
|
||||
return $facetParameters;
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\QueryGroup;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetPackage;
|
||||
|
||||
/**
|
||||
* Class QueryGroupPackage
|
||||
*/
|
||||
class QueryGroupPackage extends AbstractFacetPackage {
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getParserClassName() {
|
||||
return (string)QueryGroupFacetParser::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getQueryBuilderClassName()
|
||||
{
|
||||
return (string)QueryGroupFacetQueryBuilder::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUrlDecoderClassName()
|
||||
{
|
||||
return (string)QueryGroupUrlDecoder::class;
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\OptionBased\QueryGroup;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2012-2015 Ingo Renner <ingo@typo3.org>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\FacetUrlDecoderInterface;
|
||||
|
||||
/**
|
||||
* Filter encoder to build facet query parameters
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
* @author Ingo Renner <ingo@typo3.org>
|
||||
*/
|
||||
class QueryGroupUrlDecoder implements FacetUrlDecoderInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Parses the query filter from GET parameters in the URL and translates it
|
||||
* to a Lucene filter value.
|
||||
*
|
||||
* @param string $filterValue the filter query from plugin
|
||||
* @param array $configuration options set in a facet's configuration
|
||||
* @return string Value to be used in a Lucene filter
|
||||
*/
|
||||
public function decode($filterValue, array $configuration = [])
|
||||
{
|
||||
return $configuration[$filterValue . '.']['query'];
|
||||
}
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetItem;
|
||||
|
||||
/**
|
||||
* Abstract class that is used as base class for range facet items
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
abstract class AbstractRangeFacetItem extends AbstractFacetItem
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $rangeCounts;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $gap;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUriValue()
|
||||
{
|
||||
return $this->getRangeString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCollectionKey()
|
||||
{
|
||||
return $this->getRangeString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRangeCounts()
|
||||
{
|
||||
return $this->rangeCounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getGap()
|
||||
{
|
||||
return $this->gap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getRangeString();
|
||||
}
|
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetParser;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
use WapplerSystems\Meilisearch\System\Solr\ParsingUtil;
|
||||
|
||||
/**
|
||||
* Class AbstractRangeFacetParser
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
abstract class AbstractRangeFacetParser extends AbstractFacetParser
|
||||
{
|
||||
/**
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $facetName
|
||||
* @param array $facetConfiguration
|
||||
* @param string $facetClass
|
||||
* @param string $facetItemClass
|
||||
* @param string $facetRangeCountClass
|
||||
* @return AbstractRangeFacet|null
|
||||
*/
|
||||
protected function getParsedFacet(SearchResultSet $resultSet, $facetName, array $facetConfiguration, $facetClass, $facetItemClass, $facetRangeCountClass)
|
||||
{
|
||||
$fieldName = $facetConfiguration['field'];
|
||||
$label = $this->getPlainLabelOrApplyCObject($facetConfiguration);
|
||||
$activeValue = $this->getActiveFacetValuesFromRequest($resultSet, $facetName);
|
||||
$response = $resultSet->getResponse();
|
||||
|
||||
$valuesFromResponse = isset($response->facet_counts->facet_ranges->{$fieldName}) ? get_object_vars($response->facet_counts->facet_ranges->{$fieldName}) : [];
|
||||
|
||||
$facet = $this->objectManager->get(
|
||||
$facetClass,
|
||||
$resultSet,
|
||||
$facetName,
|
||||
$fieldName,
|
||||
$label,
|
||||
$facetConfiguration
|
||||
);
|
||||
|
||||
$facet->setIsAvailable(count($valuesFromResponse) > 0);
|
||||
$facet->setIsUsed(count($activeValue) > 0);
|
||||
|
||||
if (is_array($valuesFromResponse)) {
|
||||
$rangeCounts = [];
|
||||
$allCount = 0;
|
||||
|
||||
$countsFromResponse = isset($valuesFromResponse['counts']) ? ParsingUtil::getMapArrayFromFlatArray($valuesFromResponse['counts']) : [];
|
||||
|
||||
foreach ($countsFromResponse as $rangeCountValue => $count) {
|
||||
$rangeCountValue = $this->parseResponseValue($rangeCountValue);
|
||||
$rangeCount = $this->objectManager->get($facetRangeCountClass, $rangeCountValue, $count);
|
||||
$rangeCounts[] = $rangeCount;
|
||||
$allCount += $count;
|
||||
}
|
||||
|
||||
$fromInResponse = $this->parseResponseValue($valuesFromResponse['start']);
|
||||
$toInResponse = $this->parseResponseValue($valuesFromResponse['end']);
|
||||
|
||||
if (preg_match('/(-?\d*?)-(-?\d*)/', $activeValue[0], $rawValues) == 1) {
|
||||
$rawFrom = $rawValues[1];
|
||||
$rawTo = $rawValues[2];
|
||||
} else {
|
||||
$rawFrom = 0;
|
||||
$rawTo = 0;
|
||||
}
|
||||
|
||||
$from = $this->parseRequestValue($rawFrom);
|
||||
$to = $this->parseRequestValue($rawTo);
|
||||
|
||||
$type = isset($facetConfiguration['type']) ? $facetConfiguration['type'] : 'numericRange';
|
||||
$gap = isset($facetConfiguration[$type . '.']['gap']) ? $facetConfiguration[$type . '.']['gap'] : 1;
|
||||
|
||||
$range = $this->objectManager->get($facetItemClass, $facet, $from, $to, $fromInResponse, $toInResponse, $gap, $allCount, $rangeCounts, true);
|
||||
$facet->setRange($range);
|
||||
}
|
||||
|
||||
return $facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $requestValue
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function parseRequestValue($requestValue);
|
||||
|
||||
/**
|
||||
* @param string $responseValue
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function parseResponseValue($responseValue);
|
||||
}
|
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\DateRange;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\AbstractRangeFacetItem;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Value object that represent an option of a options facet.
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class DateRange extends AbstractRangeFacetItem
|
||||
{
|
||||
/**
|
||||
* @var DateTime
|
||||
*/
|
||||
protected $startRequested;
|
||||
|
||||
/**
|
||||
* @var DateTime
|
||||
*/
|
||||
protected $endRequested;
|
||||
|
||||
/**
|
||||
* @var DateTime
|
||||
*/
|
||||
protected $startInResponse;
|
||||
|
||||
/**
|
||||
* @var DateTime
|
||||
*/
|
||||
protected $endInResponse;
|
||||
|
||||
/**
|
||||
* @param DateRangeFacet $facet
|
||||
* @param DateTime|null $startRequested
|
||||
* @param DateTime|null $endRequested
|
||||
* @param DateTime|null $startInResponse
|
||||
* @param DateTime|null $endInResponse
|
||||
* @param string $gap
|
||||
* @param int $documentCount
|
||||
* @param array $rangeCounts
|
||||
* @param bool $selected
|
||||
*/
|
||||
public function __construct(DateRangeFacet $facet, DateTime $startRequested = null, DateTime $endRequested = null, DateTime $startInResponse = null, DateTime $endInResponse = null, $gap = '', $documentCount = 0, $rangeCounts, $selected = false)
|
||||
{
|
||||
$this->startInResponse = $startInResponse;
|
||||
$this->endInResponse = $endInResponse;
|
||||
$this->startRequested = $startRequested;
|
||||
$this->endRequested = $endRequested;
|
||||
$this->rangeCounts = $rangeCounts;
|
||||
$this->gap = $gap;
|
||||
|
||||
$label = '';
|
||||
if ($startRequested instanceof DateTime && $endRequested instanceof DateTime) {
|
||||
$label = $this->getRangeString();
|
||||
}
|
||||
|
||||
|
||||
parent::__construct($facet, $label, $documentCount, $selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getRangeString()
|
||||
{
|
||||
return $this->startRequested->format('Ymd') . '0000-' . $this->endRequested->format('Ymd') . '0000';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the end date that was requested by the user for this facet.
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getEndRequested()
|
||||
{
|
||||
return $this->endRequested;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the start date that was requested by the used for the facet.
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getStartRequested()
|
||||
{
|
||||
return $this->startRequested;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the end date that was received from solr for this facet.
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getEndInResponse()
|
||||
{
|
||||
return $this->endInResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the start date that was received from solr for this facet.
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getStartInResponse()
|
||||
{
|
||||
return $this->startInResponse;
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\DateRange;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetItemCollection;
|
||||
|
||||
/**
|
||||
* Collection for facet options.
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class DateRangeCollection extends AbstractFacetItemCollection
|
||||
{
|
||||
|
||||
/**
|
||||
* @param DateRange $dateRange
|
||||
* @return DateRangeCollection
|
||||
*/
|
||||
public function add($dateRange)
|
||||
{
|
||||
return parent::add($dateRange);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $position
|
||||
* @return DateRange
|
||||
*/
|
||||
public function getByPosition($position)
|
||||
{
|
||||
return parent::getByPosition($position);
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\DateRange;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetItem;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Value object that represent an date range count. The count has a date and the count of documents
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class DateRangeCount
|
||||
{
|
||||
|
||||
/**
|
||||
* @var DateTime
|
||||
*/
|
||||
protected $date;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $documentCount = 0;
|
||||
|
||||
/**
|
||||
* @param $date
|
||||
* @param $documentCount
|
||||
*/
|
||||
public function __construct($date, $documentCount)
|
||||
{
|
||||
$this->date = $date;
|
||||
$this->documentCount = $documentCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getDate()
|
||||
{
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getDocumentCount()
|
||||
{
|
||||
return $this->documentCount;
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\DateRange;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacet;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetItemCollection;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
|
||||
/**
|
||||
* Value object that represent a date range facet.
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class DateRangeFacet extends AbstractFacet
|
||||
{
|
||||
const TYPE_DATE_RANGE = 'dateRange';
|
||||
|
||||
/**
|
||||
* String
|
||||
* @var string
|
||||
*/
|
||||
protected static $type = self::TYPE_DATE_RANGE;
|
||||
|
||||
/**
|
||||
* @var DateRange
|
||||
*/
|
||||
protected $range;
|
||||
|
||||
/**
|
||||
* OptionsFacet constructor
|
||||
*
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $name
|
||||
* @param string $field
|
||||
* @param string $label
|
||||
* @param array $configuration Facet configuration passed from typoscript
|
||||
*/
|
||||
public function __construct(SearchResultSet $resultSet, $name, $field, $label = '', array $configuration = [])
|
||||
{
|
||||
parent::__construct($resultSet, $name, $field, $label, $configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateRange $range
|
||||
*/
|
||||
public function setRange(DateRange $range)
|
||||
{
|
||||
$this->range = $range;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DateRange
|
||||
*/
|
||||
public function getRange()
|
||||
{
|
||||
return $this->range;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get facet partial name used for rendering the facet
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPartialName()
|
||||
{
|
||||
return !empty($this->configuration['partialName']) ? $this->configuration['partialName'] : 'RangeDate.html';
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the DateRange contains only one or two items when return a collection with the range only to
|
||||
* allow to render the date range as other facet items.
|
||||
*
|
||||
* @return AbstractFacetItemCollection
|
||||
*/
|
||||
public function getAllFacetItems()
|
||||
{
|
||||
return new DateRangeCollection([$this->range]);
|
||||
}
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\DateRange;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\AbstractRangeFacetParser;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
use WapplerSystems\Meilisearch\System\Data\DateTime;
|
||||
|
||||
/**
|
||||
* Class DateRangeFacetParser
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class DateRangeFacetParser extends AbstractRangeFacetParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $facetClass = DateRangeFacet::class;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $facetItemClass = DateRange::class;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $facetRangeCountClass = DateRangeCount::class;
|
||||
|
||||
/**
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $facetName
|
||||
* @param array $facetConfiguration
|
||||
* @return DateRangeFacet|null
|
||||
*/
|
||||
public function parse(SearchResultSet $resultSet, $facetName, array $facetConfiguration)
|
||||
{
|
||||
return $this->getParsedFacet(
|
||||
$resultSet,
|
||||
$facetName,
|
||||
$facetConfiguration,
|
||||
$this->facetClass,
|
||||
$this->facetItemClass,
|
||||
$this->facetRangeCountClass
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $rawDate
|
||||
* @return DateTime|null
|
||||
*/
|
||||
protected function parseRequestValue($rawDate)
|
||||
{
|
||||
$rawDate = \DateTime::createFromFormat('Ymd', substr($rawDate, 0, 8));
|
||||
if ($rawDate === false) {
|
||||
return null;
|
||||
}
|
||||
$date = new DateTime($rawDate->format(DateTime::ISO8601));
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $isoDateString
|
||||
* @return DateTime
|
||||
*/
|
||||
protected function parseResponseValue($isoDateString)
|
||||
{
|
||||
$rawDate = \DateTime::createFromFormat(\DateTime::ISO8601, $isoDateString);
|
||||
return new DateTime($rawDate->format(DateTime::ISO8601));
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\DateRange;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\FacetQueryBuilderInterface;
|
||||
use WapplerSystems\Meilisearch\System\Configuration\TypoScriptConfiguration;
|
||||
|
||||
class DateRangeFacetQueryBuilder implements FacetQueryBuilderInterface {
|
||||
|
||||
/**
|
||||
* @param string $facetName
|
||||
* @param TypoScriptConfiguration $configuration
|
||||
* @return array
|
||||
*/
|
||||
public function build($facetName, TypoScriptConfiguration $configuration)
|
||||
{
|
||||
$facetParameters = [];
|
||||
$facetConfiguration = $configuration->getSearchFacetingFacetByName($facetName);
|
||||
|
||||
$tag = '';
|
||||
if ($facetConfiguration['keepAllOptionsOnSelection'] == 1) {
|
||||
$tag = '{!ex=' . $facetConfiguration['field'] . '}';
|
||||
}
|
||||
$facetParameters['facet.range'][] = $tag . $facetConfiguration['field'];
|
||||
|
||||
$start = 'NOW/DAY-1YEAR';
|
||||
if ($facetConfiguration['dateRange.']['start']) {
|
||||
$start = $facetConfiguration['dateRange.']['start'];
|
||||
}
|
||||
$facetParameters['f.' . $facetConfiguration['field'] . '.facet.range.start'] = $start;
|
||||
|
||||
$end = 'NOW/DAY+1YEAR';
|
||||
if ($facetConfiguration['dateRange.']['end']) {
|
||||
$end = $facetConfiguration['dateRange.']['end'];
|
||||
}
|
||||
$facetParameters['f.' . $facetConfiguration['field'] . '.facet.range.end'] = $end;
|
||||
|
||||
$gap = '+1DAY';
|
||||
if ($facetConfiguration['dateRange.']['gap']) {
|
||||
$gap = $facetConfiguration['dateRange.']['gap'];
|
||||
}
|
||||
$facetParameters['f.' . $facetConfiguration['field'] . '.facet.range.gap'] = $gap;
|
||||
|
||||
return $facetParameters;
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\DateRange;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetPackage;
|
||||
|
||||
/**
|
||||
* Class DateRangePackage
|
||||
*/
|
||||
class DateRangePackage extends AbstractFacetPackage {
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getParserClassName() {
|
||||
return (string)DateRangeFacetParser::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getQueryBuilderClassName()
|
||||
{
|
||||
return (string)DateRangeFacetQueryBuilder::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUrlDecoderClassName()
|
||||
{
|
||||
return (string)DateRangeUrlDecoder::class;
|
||||
}
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\DateRange;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2011 Markus Goldbach <markus.goldbach@dkd.de>
|
||||
* (c) 2012-2015 Ingo Renner <ingo@typo3.org>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\FacetUrlDecoderInterface;
|
||||
use WapplerSystems\Meilisearch\System\DateTime\FormatService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Parser to build solr range queries from tx_meilisearch[filter]
|
||||
*
|
||||
* @author Markus Goldbach <markus.goldbach@dkd.de>
|
||||
*/
|
||||
class DateRangeUrlDecoder implements FacetUrlDecoderInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Delimiter for date parts in the URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const DELIMITER = '-';
|
||||
|
||||
/**
|
||||
* Parses the given date range from a GET parameter and returns a Solr
|
||||
* date range filter.
|
||||
*
|
||||
* @param string $dateRange The range filter query string from the query URL
|
||||
* @param array $configuration Facet configuration
|
||||
* @return string Lucene query language filter to be used for querying Solr
|
||||
*/
|
||||
public function decode($dateRange, array $configuration = [])
|
||||
{
|
||||
list($dateRangeStart, $dateRangeEnd) = explode(self::DELIMITER, $dateRange);
|
||||
|
||||
$formatService = GeneralUtility::makeInstance(FormatService::class);
|
||||
$fromPart = '*';
|
||||
if($dateRangeStart !== ''){
|
||||
$fromPart = $formatService->timestampToIso(strtotime($dateRangeStart));
|
||||
}
|
||||
|
||||
$toPart = '*';
|
||||
if($dateRangeEnd !== ''){
|
||||
$dateRangeEnd .= '59'; // adding 59 seconds
|
||||
$toPart = $formatService->timestampToIso(strtotime($dateRangeEnd));
|
||||
}
|
||||
|
||||
$dateRangeFilter = '[' . $fromPart . ' TO ' . $toPart . ']';
|
||||
return $dateRangeFilter;
|
||||
}
|
||||
}
|
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\NumericRange;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\AbstractRangeFacetItem;
|
||||
|
||||
/**
|
||||
* Value object that represent an option of a numric range facet.
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class NumericRange extends AbstractRangeFacetItem
|
||||
{
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
protected $startRequested;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
protected $endRequested;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
protected $startInResponse;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
protected $endInResponse;
|
||||
|
||||
/**
|
||||
* @param NumericRangeFacet $facet
|
||||
* @param float|null $startRequested
|
||||
* @param float|null $endRequested
|
||||
* @param float|null $startInResponse
|
||||
* @param float|null $endInResponse
|
||||
* @param string $gap
|
||||
* @param int $documentCount
|
||||
* @param array $rangeCounts
|
||||
* @param bool $selected
|
||||
*/
|
||||
public function __construct(NumericRangeFacet $facet, $startRequested = null, $endRequested = null, $startInResponse = null, $endInResponse = null, $gap = '', $documentCount = 0, $rangeCounts, $selected = false)
|
||||
{
|
||||
$this->startInResponse = $startInResponse;
|
||||
$this->endInResponse = $endInResponse;
|
||||
$this->startRequested = $startRequested;
|
||||
$this->endRequested = $endRequested;
|
||||
$this->rangeCounts = $rangeCounts;
|
||||
$this->gap = $gap;
|
||||
|
||||
$label = '';
|
||||
if ($startRequested !== null && $endRequested !== null) {
|
||||
$label = $this->getRangeString();
|
||||
}
|
||||
|
||||
|
||||
parent::__construct($facet, $label, $documentCount, $selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getRangeString()
|
||||
{
|
||||
return $this->startRequested . '-' . $this->endRequested;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the end date that was requested by the user for this facet.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getEndRequested()
|
||||
{
|
||||
return $this->endRequested;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the start date that was requested by the used for the facet.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getStartRequested()
|
||||
{
|
||||
return $this->startRequested;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the end date that was received from solr for this facet.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getEndInResponse()
|
||||
{
|
||||
return $this->endInResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the start date that was received from solr for this facet.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getStartInResponse()
|
||||
{
|
||||
return $this->startInResponse;
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\NumericRange;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetItemCollection;
|
||||
|
||||
/**
|
||||
* Collection for facet options.
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class NumericRangeCollection extends AbstractFacetItemCollection
|
||||
{
|
||||
|
||||
/**
|
||||
* @param NumericRange $numericRange
|
||||
* @return NumericRangeCollection
|
||||
*/
|
||||
public function add($numericRange)
|
||||
{
|
||||
return parent::add($numericRange);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $position
|
||||
* @return NumericRange
|
||||
*/
|
||||
public function getByPosition($position)
|
||||
{
|
||||
return parent::getByPosition($position);
|
||||
}
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\NumericRange;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetItem;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Value object that represent an date range count. The count has a date and the count of documents
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class NumericRangeCount
|
||||
{
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
protected $rangeItem;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $documentCount = 0;
|
||||
|
||||
/**
|
||||
* @param float $rangeItem
|
||||
* @param integer $documentCount
|
||||
*/
|
||||
public function __construct($rangeItem, $documentCount)
|
||||
{
|
||||
$this->rangeItem = $rangeItem;
|
||||
$this->documentCount = $documentCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getRangeItem()
|
||||
{
|
||||
return $this->rangeItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getDocumentCount()
|
||||
{
|
||||
return $this->documentCount;
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\NumericRange;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacet;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetItemCollection;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
|
||||
/**
|
||||
* Value object that represent a date range facet.
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class NumericRangeFacet extends AbstractFacet
|
||||
{
|
||||
const TYPE_NUMERIC_RANGE = 'numericRange';
|
||||
|
||||
/**
|
||||
* String
|
||||
* @var string
|
||||
*/
|
||||
protected static $type = self::TYPE_NUMERIC_RANGE;
|
||||
|
||||
/**
|
||||
* @var NumericRange
|
||||
*/
|
||||
protected $numericRange;
|
||||
|
||||
/**
|
||||
* OptionsFacet constructor
|
||||
*
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $name
|
||||
* @param string $field
|
||||
* @param string $label
|
||||
* @param array $configuration Facet configuration passed from typoscript
|
||||
*/
|
||||
public function __construct(SearchResultSet $resultSet, $name, $field, $label = '', array $configuration = [])
|
||||
{
|
||||
parent::__construct($resultSet, $name, $field, $label, $configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NumericRange $range
|
||||
*/
|
||||
public function setRange(NumericRange $range)
|
||||
{
|
||||
$this->numericRange = $range;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NumericRange
|
||||
*/
|
||||
public function getRange()
|
||||
{
|
||||
return $this->numericRange;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get facet partial name used for rendering the facet
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPartialName()
|
||||
{
|
||||
return !empty($this->configuration['partialName']) ? $this->configuration['partialName'] : 'RangeNumeric.html';
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the DateRange contains only one or two items when return a collection with the range only to
|
||||
* allow to render the date range as other facet items.
|
||||
*
|
||||
* @return AbstractFacetItemCollection
|
||||
*/
|
||||
public function getAllFacetItems()
|
||||
{
|
||||
return new NumericRangeCollection([$this->numericRange]);
|
||||
}
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\NumericRange;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\AbstractRangeFacetParser;
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
|
||||
|
||||
/**
|
||||
* Class NumericRangeFacetParser
|
||||
*
|
||||
* @author Frans Saris <frans@beech.it>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class NumericRangeFacetParser extends AbstractRangeFacetParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $facetClass = NumericRangeFacet::class;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $facetItemClass = NumericRange::class;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $facetRangeCountClass = NumericRangeCount::class;
|
||||
|
||||
/**
|
||||
* @param SearchResultSet $resultSet
|
||||
* @param string $facetName
|
||||
* @param array $facetConfiguration
|
||||
* @return NumericRangeFacet|null
|
||||
*/
|
||||
public function parse(SearchResultSet $resultSet, $facetName, array $facetConfiguration)
|
||||
{
|
||||
return $this->getParsedFacet(
|
||||
$resultSet,
|
||||
$facetName,
|
||||
$facetConfiguration,
|
||||
$this->facetClass,
|
||||
$this->facetItemClass,
|
||||
$this->facetRangeCountClass
|
||||
);
|
||||
}
|
||||
/**
|
||||
* @param mixed $rawValue
|
||||
* @return mixed (numeric value)
|
||||
*/
|
||||
protected function parseRequestValue($rawValue)
|
||||
{
|
||||
return is_numeric($rawValue) ? $rawValue : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $rawValue
|
||||
* @return mixed (numeric value)
|
||||
*/
|
||||
protected function parseResponseValue($rawValue)
|
||||
{
|
||||
return is_numeric($rawValue) ? $rawValue : 0;
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\NumericRange;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\FacetQueryBuilderInterface;
|
||||
use WapplerSystems\Meilisearch\System\Configuration\TypoScriptConfiguration;
|
||||
|
||||
class NumericRangeFacetQueryBuilder implements FacetQueryBuilderInterface {
|
||||
|
||||
/**
|
||||
* @param string $facetName
|
||||
* @param TypoScriptConfiguration $configuration
|
||||
* @return array
|
||||
*/
|
||||
public function build($facetName, TypoScriptConfiguration $configuration)
|
||||
{
|
||||
$facetParameters = [];
|
||||
$facetConfiguration = $configuration->getSearchFacetingFacetByName($facetName);
|
||||
|
||||
$tag = '';
|
||||
if ($facetConfiguration['keepAllOptionsOnSelection'] == 1) {
|
||||
$tag = '{!ex=' . $facetConfiguration['field'] . '}';
|
||||
}
|
||||
$facetParameters['facet.range'][] = $tag . $facetConfiguration['field'];
|
||||
|
||||
$facetParameters['f.' . $facetConfiguration['field'] . '.facet.range.start'] = $facetConfiguration['numericRange.']['start'];
|
||||
$facetParameters['f.' . $facetConfiguration['field'] . '.facet.range.end'] = $facetConfiguration['numericRange.']['end'];
|
||||
$facetParameters['f.' . $facetConfiguration['field'] . '.facet.range.gap'] = $facetConfiguration['numericRange.']['gap'];
|
||||
|
||||
return $facetParameters;
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\NumericRange;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\AbstractFacetPackage;
|
||||
|
||||
/**
|
||||
* Class NumericRangePackage
|
||||
*/
|
||||
class NumericRangePackage extends AbstractFacetPackage {
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getParserClassName() {
|
||||
return (string)NumericRangeFacetParser::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getQueryBuilderClassName()
|
||||
{
|
||||
return (string)NumericRangeFacetQueryBuilder::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUrlDecoderClassName()
|
||||
{
|
||||
return (string)NumericRangeUrlDecoder::class;
|
||||
}
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RangeBased\NumericRange;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2011 Markus Goldbach <markus.goldbach@dkd.de>
|
||||
* (c) 2012-2015 Ingo Renner <ingo@typo3.org>
|
||||
* (c) 2016 Markus Friedrich <markus.friedrich@dkd.de>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\FacetUrlDecoderInterface;
|
||||
|
||||
/**
|
||||
* Parser to build Solr range queries from tx_meilisearch[filter]
|
||||
*
|
||||
* @author Markus Goldbach <markus.goldbach@dkd.de>
|
||||
* @author Ingo Renner <ingo@typo3.org>
|
||||
* @author Markus Friedrich <markus.friedrich@dkd.de>
|
||||
*/
|
||||
class NumericRangeUrlDecoder implements FacetUrlDecoderInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Delimiter for ranges in the URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const DELIMITER = '-';
|
||||
|
||||
/**
|
||||
* Parses the given range from a GET parameter and returns a Solr range
|
||||
* filter.
|
||||
*
|
||||
* @param string $range The range filter from the URL.
|
||||
* @param array $configuration Facet configuration
|
||||
* @return string Lucene query language filter to be used for querying Solr
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function decode($range, array $configuration = [])
|
||||
{
|
||||
preg_match('/(-?\d*?)' . self::DELIMITER . '(-?\d*)/', $range, $filterParts);
|
||||
if ($filterParts[1] == '' || $filterParts[2] == '') {
|
||||
throw new \InvalidArgumentException(
|
||||
'Invalid numeric range given',
|
||||
1466062730
|
||||
);
|
||||
}
|
||||
|
||||
return '[' . (int)$filterParts[1] . ' TO ' . (int)$filterParts[2] . ']';
|
||||
}
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets\RenderingInstructions;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2015-2016 Hendrik Putzek <hendrik.putzek@dkd.de>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\System\DateTime\FormatService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Formats a given date string to another format
|
||||
*
|
||||
* Accepted typoscript parameters:
|
||||
* inputFormat -> The format of the input string
|
||||
* outputFormat -> The format of the processed string (output)
|
||||
*
|
||||
* If the input date can not be processed by the given inputFormat string it is
|
||||
* returned unprocessed.
|
||||
*
|
||||
* @author Hendrik Putzek <hendrik.putzek@dkd.de>
|
||||
*/
|
||||
class FormatDate
|
||||
{
|
||||
|
||||
/**
|
||||
* @var FormatService
|
||||
*/
|
||||
protected $formatService = null;
|
||||
|
||||
/**
|
||||
* FormatDate constructor.
|
||||
* @param FormatService|null $formatService
|
||||
*/
|
||||
public function __construct(FormatService $formatService = null)
|
||||
{
|
||||
$this->formatService = $formatService ?? GeneralUtility::makeInstance(FormatService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a given date string to another format
|
||||
*
|
||||
* @param string $content the content to process
|
||||
* @param array $conf typoscript configuration
|
||||
* @return string formatted date
|
||||
*/
|
||||
public function format($content, $conf)
|
||||
{
|
||||
// set default values
|
||||
$inputFormat = $conf['inputFormat'] ?: 'Y-m-d\TH:i:s\Z';
|
||||
$outputFormat = $conf['outputFormat'] ?: '';
|
||||
return $this->formatService->format($content, $inputFormat, $outputFormat);
|
||||
}
|
||||
}
|
124
Classes/Domain/Search/ResultSet/Facets/RequirementsService.php
Normal file
124
Classes/Domain/Search/ResultSet/Facets/RequirementsService.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Service class to check for a facet if allRequirements are met for that facet.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class RequirementsService
|
||||
{
|
||||
/**
|
||||
* Checks if facet meets all requirements.
|
||||
*
|
||||
* Evaluates configuration in "plugin.tx_meilisearch.search.faceting.facets.[facetName].requirements",
|
||||
*
|
||||
* @param AbstractFacet $facet
|
||||
* @return bool true if facet might be rendered
|
||||
*/
|
||||
public function getAllRequirementsMet(AbstractFacet $facet)
|
||||
{
|
||||
$requirements = $facet->getRequirements();
|
||||
if (!is_array($requirements) || count($requirements) === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($requirements as $requirement) {
|
||||
$requirementMet = $this->getRequirementMet($facet, $requirement);
|
||||
$requirementMet = $this->getNegationWhenConfigured($requirementMet, $requirement);
|
||||
|
||||
if (!$requirementMet) {
|
||||
// early return as soon as one requirement is not met
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a single requirement is met.
|
||||
*
|
||||
* @param AbstractFacet $facet
|
||||
* @param array $requirement
|
||||
* @return bool
|
||||
*/
|
||||
protected function getRequirementMet(AbstractFacet $facet, $requirement = []) {
|
||||
$selectedItemValues = $this->getSelectedItemValues($facet, $requirement['facet']);
|
||||
$csvActiveFacetItemValues = implode(', ', $selectedItemValues);
|
||||
$requirementValues = GeneralUtility::trimExplode(',', $requirement['values']);
|
||||
|
||||
foreach ($requirementValues as $value) {
|
||||
$noFacetOptionSelectedRequirementMet = ($value === '__none' && empty($selectedItemValues));
|
||||
$anyFacetOptionSelectedRequirementMet = ($value === '__any' && !empty($selectedItemValues));
|
||||
|
||||
if ($noFacetOptionSelectedRequirementMet || $anyFacetOptionSelectedRequirementMet || in_array($value, $selectedItemValues) || fnmatch($value, $csvActiveFacetItemValues)) {
|
||||
// when we find a single matching requirement we can exit and return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the active item values of a facet
|
||||
*
|
||||
* @param string $facetNameToCheckRequirementsOn
|
||||
* @return AbstractFacetItem[]
|
||||
*/
|
||||
protected function getSelectedItemValues(AbstractFacet $facet, $facetNameToCheckRequirementsOn)
|
||||
{
|
||||
$facetToCheckRequirements = $facet->getResultSet()->getFacets()->getByName($facetNameToCheckRequirementsOn)->getByPosition(0);
|
||||
if (!$facetToCheckRequirements instanceof AbstractFacet) {
|
||||
throw new \InvalidArgumentException('Requirement for unexisting facet configured');
|
||||
}
|
||||
|
||||
if (!$facetToCheckRequirements->getIsUsed()) {
|
||||
// unused facets do not have active values.
|
||||
return [];
|
||||
}
|
||||
|
||||
$itemValues = [];
|
||||
$activeFacetItems = $facetToCheckRequirements->getAllFacetItems();
|
||||
foreach ($activeFacetItems as $item) {
|
||||
/** @var AbstractFacetItem $item */
|
||||
if ($item->getSelected()) {
|
||||
$itemValues[] = $item->getUriValue();
|
||||
}
|
||||
}
|
||||
|
||||
return $itemValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Negates the result when configured.
|
||||
*
|
||||
* @param boolean $value
|
||||
* @param array $configuration
|
||||
* @return boolean
|
||||
*/
|
||||
protected function getNegationWhenConfigured($value, $configuration)
|
||||
{
|
||||
if (!is_array($configuration) || empty($configuration['negate']) || (bool)$configuration['negate'] === false) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return !((bool)$value);
|
||||
}
|
||||
}
|
71
Classes/Domain/Search/ResultSet/Facets/SortingExpression.php
Normal file
71
Classes/Domain/Search/ResultSet/Facets/SortingExpression.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Facets;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2017- Timo Hund <timo.hund@dkd.de>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
/**
|
||||
* Expression for facet sorting
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
* @author Jens Jacobsen <jens.jacobsen@ueberbit.de>
|
||||
*/
|
||||
class SortingExpression
|
||||
{
|
||||
/**
|
||||
* Return expression for facet sorting
|
||||
*
|
||||
* @param string $sorting
|
||||
* @return string
|
||||
*/
|
||||
public function getForFacet($sorting)
|
||||
{
|
||||
$noSortingSet = $sorting !== 0 && $sorting !== FALSE && empty($sorting);
|
||||
$sortingIsCount = $sorting === 'count' || $sorting === 1 || $sorting === '1' || $sorting === TRUE;
|
||||
if ($noSortingSet) {
|
||||
return '';
|
||||
} elseif ($sortingIsCount) {
|
||||
return 'count';
|
||||
} else {
|
||||
return 'index';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return expression for facet sorting combined with direction
|
||||
*
|
||||
* @param string $sorting
|
||||
* @param string $direction
|
||||
* @return string
|
||||
*/
|
||||
public function getForJsonFacet($sorting, $direction)
|
||||
{
|
||||
$isMetricSorting = strpos($sorting, 'metrics_') === 0;
|
||||
$expression = $isMetricSorting ? $sorting : $this->getForFacet($sorting);
|
||||
$direction = strtolower($direction);
|
||||
if (!empty($direction) && in_array($direction, ['asc', 'desc'])) {
|
||||
$expression .= ' ' . $direction;
|
||||
}
|
||||
return $expression;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user