Commit 69da203d authored by Markus Kalkbrenner's avatar Markus Kalkbrenner Committed by GitHub

Issue #378 support JSON Facet API (#590)

solves issue #378 support JSON Facet API
parent e9844b8e
...@@ -6,8 +6,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ...@@ -6,8 +6,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased] ## [Unreleased]
### Added ### Added
- Support for JSON Facet API
### Changed ### Changed
- Constants FacetSet::FACET_* became FacetSetInterface::FACET_*
### Deprecated ### Deprecated
......
...@@ -9,15 +9,8 @@ use Solarium\Core\Configurable; ...@@ -9,15 +9,8 @@ use Solarium\Core\Configurable;
* *
* @see http://wiki.apache.org/solr/SimpleFacetParameters * @see http://wiki.apache.org/solr/SimpleFacetParameters
*/ */
abstract class AbstractFacet extends Configurable abstract class AbstractFacet extends Configurable implements FacetInterface
{ {
/**
* Exclude tags for this facet.
*
* @var array
*/
protected $excludes = [];
/** /**
* Must be implemented by the facet types and return one of the constants. * Must be implemented by the facet types and return one of the constants.
* *
...@@ -48,106 +41,4 @@ abstract class AbstractFacet extends Configurable ...@@ -48,106 +41,4 @@ abstract class AbstractFacet extends Configurable
{ {
return $this->setOption('key', $value); return $this->setOption('key', $value);
} }
/**
* Add an exclude tag.
*
* @param string $tag
*
* @return self Provides fluent interface
*/
public function addExclude($tag)
{
$this->excludes[$tag] = true;
return $this;
}
/**
* Add multiple exclude tags.
*
* @param array $excludes
*
* @return self Provides fluent interface
*/
public function addExcludes(array $excludes)
{
foreach ($excludes as $exclude) {
$this->addExclude($exclude);
}
return $this;
}
/**
* Get all excludes.
*
* @return array
*/
public function getExcludes()
{
return array_keys($this->excludes);
}
/**
* Remove a single exclude tag.
*
* @param string $exclude
*
* @return self Provides fluent interface
*/
public function removeExclude($exclude)
{
if (isset($this->excludes[$exclude])) {
unset($this->excludes[$exclude]);
}
return $this;
}
/**
* Remove all excludes.
*
* @return self Provides fluent interface
*/
public function clearExcludes()
{
$this->excludes = [];
return $this;
}
/**
* Set multiple excludes.
*
* This overwrites any existing excludes
*
* @param array $excludes
*/
public function setExcludes($excludes)
{
$this->clearExcludes();
$this->addExcludes($excludes);
}
/**
* Initialize options.
*/
protected function init()
{
foreach ($this->options as $name => $value) {
switch ($name) {
case 'key':
$this->setKey($value);
break;
case 'exclude':
if (!is_array($value)) {
$value = explode(',', $value);
}
$this->setExcludes($value);
unset($this->options['exclude']);
break;
}
}
}
} }
<?php
namespace Solarium\Component\Facet;
/**
* Facet query.
*
* @see http://wiki.apache.org/solr/SimpleFacetParameters#Field_Value_Faceting_Parameters
*/
abstract class AbstractField extends AbstractFacet
{
/**
* Facet sort type index.
*/
const SORT_INDEX = 'index';
/**
* Facet sort type count.
*/
const SORT_COUNT = 'count';
/**
* Default options.
*
* @var array
*/
protected $options = [
'field' => 'id',
];
/**
* Set the field name.
*
* @param string $field
*
* @return self Provides fluent interface
*/
public function setField($field)
{
return $this->setOption('field', $field);
}
/**
* Get the field name.
*
* @return string
*/
public function getField()
{
return $this->getOption('field');
}
/**
* Set the facet sort order.
*
* Use one of the SORT_* constants as the value
*
* @param string $sort
*
* @return self Provides fluent interface
*/
public function setSort($sort)
{
return $this->setOption('sort', $sort);
}
/**
* Get the facet sort order.
*
* @return string
*/
public function getSort()
{
return $this->getOption('sort');
}
/**
* Limit the terms for faceting by a prefix.
*
* @param string $prefix
*
* @return self Provides fluent interface
*/
public function setPrefix($prefix)
{
return $this->setOption('prefix', $prefix);
}
/**
* Get the facet prefix.
*
* @return string
*/
public function getPrefix()
{
return $this->getOption('prefix');
}
/**
* Set the facet limit.
*
* @param mixed $limit
*
* @return self Provides fluent interface
*/
public function setLimit($limit)
{
return $this->setOption('limit', $limit);
}
/**
* Get the facet limit.
*
* @return string
*/
public function getLimit()
{
return $this->getOption('limit');
}
/**
* Set the facet offset.
*
* @param int $offset
*
* @return self Provides fluent interface
*/
public function setOffset($offset)
{
return $this->setOption('offset', $offset);
}
/**
* Get the facet offset.
*
* @return int
*/
public function getOffset()
{
return $this->getOption('offset');
}
/**
* Set the facet mincount.
*
* @param int $minCount
*
* @return self Provides fluent interface
*/
public function setMinCount($minCount)
{
return $this->setOption('mincount', $minCount);
}
/**
* Get the facet mincount.
*
* @return int
*/
public function getMinCount()
{
return $this->getOption('mincount');
}
/**
* Set the missing count option.
*
* @param bool $missing
*
* @return self Provides fluent interface
*/
public function setMissing($missing)
{
return $this->setOption('missing', $missing);
}
/**
* Get the facet missing option.
*
* @return bool
*/
public function getMissing()
{
return $this->getOption('missing');
}
/**
* Set the facet method.
*
* Use one of the METHOD_* constants as value
*
* @param string $method
*
* @return self Provides fluent interface
*/
public function setMethod($method)
{
return $this->setOption('method', $method);
}
/**
* Get the facet method.
*
* @return string
*/
public function getMethod()
{
return $this->getOption('method');
}
}
<?php
namespace Solarium\Component\Facet;
/**
* Facet range.
*
* @see http://wiki.apache.org/solr/SimpleFacetParameters#Facet_by_Range
*/
abstract class AbstractRange extends AbstractFacet
{
/**
* Value for the 'other' option.
*/
const OTHER_BEFORE = 'before';
/**
* Value for the 'other' option.
*/
const OTHER_AFTER = 'after';
/**
* Value for the 'other' option.
*/
const OTHER_BETWEEN = 'between';
/**
* Value for the 'other' option.
*/
const OTHER_ALL = 'all';
/**
* Value for the 'other' option.
*/
const OTHER_NONE = 'none';
/**
* Value for the 'include' option.
*/
const INCLUDE_LOWER = 'lower';
/**
* Value for the 'include' option.
*/
const INCLUDE_UPPER = 'upper';
/**
* Value for the 'include' option.
*/
const INCLUDE_EDGE = 'edge';
/**
* Value for the 'include' option.
*/
const INCLUDE_OUTER = 'outer';
/**
* Value for the 'include' option.
*/
const INCLUDE_ALL = 'all';
/**
* Set the field name.
*
* @param string $field
*
* @return self Provides fluent interface
*/
public function setField($field)
{
return $this->setOption('field', $field);
}
/**
* Get the field name.
*
* @return string
*/
public function getField()
{
return $this->getOption('field');
}
/**
* Set the lower bound of the range.
*
* @param string $start
*
* @return self Provides fluent interface
*/
public function setStart($start)
{
return $this->setOption('start', $start);
}
/**
* Get the lower bound of the range.
*
* @return string
*/
public function getStart()
{
return $this->getOption('start');
}
/**
* Set the upper bound of the range.
*
* @param string $end
*
* @return self Provides fluent interface
*/
public function setEnd($end)
{
return $this->setOption('end', $end);
}
/**
* Get the upper bound of the range.
*
* @return string
*/
public function getEnd()
{
return $this->getOption('end');
}
/**
* Set range gap.
*
* The size of each range expressed as a value to be added to the lower bound
*
* @param string $gap
*
* @return self Provides fluent interface
*/
public function setGap($gap)
{
return $this->setOption('gap', $gap);
}
/**
* Get range gap.
*
* The size of each range expressed as a value to be added to the lower bound
*
* @return string
*/
public function getGap()
{
return $this->getOption('gap');
}
/**
* Set hardend option.
*
* A Boolean parameter instructing Solr what to do in the event that facet.range.gap
* does not divide evenly between facet.range.start and facet.range.end
*
* @param bool $hardend
*
* @return self Provides fluent interface
*/
public function setHardend($hardend)
{
return $this->setOption('hardend', $hardend);
}
/**
* Get hardend option.
*
* @return bool
*/
public function getHardend()
{
return $this->getOption('hardend');
}
/**
* Set other counts.
*
* Use one of the constants as value.
* If you want to use multiple values supply an array or comma separated string
*
* @param string|array $other
*
* @return self Provides fluent interface
*/
public function setOther($other)
{
if (is_string($other)) {
$other = explode(',', $other);
$other = array_map('trim', $other);
}
return $this->setOption('other', $other);
}
/**
* Get other counts.
*
* @return array
*/
public function getOther()
{
$other = $this->getOption('other');
if (null === $other) {
$other = [];
}
return $other;
}
/**
* Set include option.
*
* Use one of the constants as value.
* If you want to use multiple values supply an array or comma separated string
*
* @param string|array $include
*
* @return self Provides fluent interface
*/
public function setInclude($include)
{
if (is_string($include)) {
$include = explode(',', $include);
$include = array_map('trim', $include);
}
return $this->setOption('include', $include);
}
/**
* Get include option.
*
* @return array
*/
public function getInclude()
{
$include = $this->getOption('include');
if (null === $include) {
$include = [];
}
return $include;
}
/**
* Initialize options.
*
* Several options need some extra checks or setup work, for these options
* the setters are called.
*/
protected function init()
{
foreach ($this->options as $name => $value) {
switch ($name) {
case 'include':
$this->setInclude($value);
break;
case 'other':
$this->setOther($value);
break;
}
}
}
}
<?php
namespace Solarium\Component\Facet;
/**
* Exclude tags for facets.
*
* @see http://wiki.apache.org/solr/SimpleFacetParameters
*/
interface ExcludeTagsInterface
{
/**
* Add an exclude tag.
*
* @param string $tag
*
* @return self Provides fluent interface
*/
public function addExclude($tag);
/**
* Add multiple exclude tags.
*
* @param array $excludes
*
* @return self Provides fluent interface
*/
public function addExcludes(array $excludes);
/**
* Get all excludes.
*
* @return array
*/
public function getExcludes();
/**
* Remove a single exclude tag.
*
* @param string $exclude
*
* @return self Provides fluent interface
*/
public function removeExclude($exclude);
/**
* Remove all excludes.
*
* @return self Provides fluent interface
*/
public function clearExcludes();
/**
* Set multiple excludes.
*
* This overwrites any existing excludes
*
* @param array $excludes
*/
public function setExcludes($excludes);
}
<?php
namespace Solarium\Component\Facet;
/**
* Exclude tags for facets.
*
* @see http://wiki.apache.org/solr/SimpleFacetParameters
*/
trait ExcludeTagsTrait
{
/**
* Exclude tags for this facet.
*
* @var array
*/
protected $excludes = [];
/**
* Add an exclude tag.
*
* @param string $tag
*
* @return self Provides fluent interface
*/
public function addExclude($tag)
{
$this->excludes[$tag] = true;
return $this;
}
/**
* Add multiple exclude tags.
*
* @param array $excludes
*
* @return self Provides fluent interface
*/
public function addExcludes(array $excludes)
{
foreach ($excludes as $exclude) {
$this->addExclude($exclude);
}
return $this;
}
/**
* Get all excludes.
*
* @return array
*/
public function getExcludes()
{
return array_keys($this->excludes);
}
/**
* Remove a single exclude tag.
*
* @param string $exclude
*
* @return self Provides fluent interface
*/
public function removeExclude($exclude)
{
if (isset($this->excludes[$exclude])) {
unset($this->excludes[$exclude]);
}
return $this;
}
/**
* Remove all excludes.
*
* @return self Provides fluent interface
*/
public function clearExcludes()
{
$this->excludes = [];
return $this;
}
/**
* Set multiple excludes.
*
* This overwrites any existing excludes
*
* @param array $excludes
*/
public function setExcludes($excludes)
{
$this->clearExcludes();
$this->addExcludes($excludes);
}
/**
* Initialize options.
*/
protected function init()
{
foreach ($this->options as $name => $value) {
switch ($name) {
case 'exclude':
if (!is_array($value)) {
$value = explode(',', $value);
}
$this->setExcludes($value);
unset($this->options['exclude']);
break;
}
}
}
}
<?php
namespace Solarium\Component\Facet;
/**
* Facet base class.
*
* @see http://wiki.apache.org/solr/SimpleFacetParameters
*/
interface FacetInterface
{
/**
* Must be implemented by the facet types and return one of the constants.
*
* @abstract
*
* @return string
*/
public function getType();
/**
* Get key value.
*
* @return string
*/
public function getKey();
/**
* Set key value.
*
* @param string $value
*
* @return FacetInterface
*/
public function setKey($value);
}
...@@ -2,24 +2,16 @@ ...@@ -2,24 +2,16 @@
namespace Solarium\Component\Facet; namespace Solarium\Component\Facet;
use Solarium\Component\FacetSet; use Solarium\Component\FacetSetInterface;
/** /**
* Facet query. * Facet query.
* *
* @see http://wiki.apache.org/solr/SimpleFacetParameters#Field_Value_Faceting_Parameters * @see http://wiki.apache.org/solr/SimpleFacetParameters#Field_Value_Faceting_Parameters
*/ */
class Field extends AbstractFacet class Field extends AbstractField implements ExcludeTagsInterface
{ {
/** use ExcludeTagsTrait;
* Facet sort type index.
*/
const SORT_INDEX = 'index';
/**
* Facet sort type count.
*/
const SORT_COUNT = 'count';
/** /**
* Facet method enum. * Facet method enum.
...@@ -31,15 +23,6 @@ class Field extends AbstractFacet ...@@ -31,15 +23,6 @@ class Field extends AbstractFacet
*/ */
const METHOD_FC = 'fc'; const METHOD_FC = 'fc';
/**
* Default options.
*
* @var array
*/
protected $options = [
'field' => 'id',
];
/** /**
* Get the facet type. * Get the facet type.
* *
...@@ -47,75 +30,7 @@ class Field extends AbstractFacet ...@@ -47,75 +30,7 @@ class Field extends AbstractFacet
*/ */
public function getType() public function getType()
{ {
return FacetSet::FACET_FIELD; return FacetSetInterface::FACET_FIELD;
}
/**
* Set the field name.
*
* @param string $field
*
* @return self Provides fluent interface
*/
public function setField($field)
{
return $this->setOption('field', $field);
}
/**
* Get the field name.
*
* @return string
*/
public function getField()
{
return $this->getOption('field');
}
/**
* Set the facet sort order.
*
* Use one of the SORT_* constants as the value
*
* @param string $sort
*
* @return self Provides fluent interface
*/
public function setSort($sort)
{
return $this->setOption('sort', $sort);
}
/**
* Get the facet sort order.
*
* @return string
*/
public function getSort()
{
return $this->getOption('sort');
}
/**
* Limit the terms for faceting by a prefix.
*
* @param string $prefix
*
* @return self Provides fluent interface
*/
public function setPrefix($prefix)
{
return $this->setOption('prefix', $prefix);
}
/**
* Get the facet prefix.
*
* @return string
*/
public function getPrefix()
{
return $this->getOption('prefix');
} }
/** /**
...@@ -169,116 +84,4 @@ class Field extends AbstractFacet ...@@ -169,116 +84,4 @@ class Field extends AbstractFacet
{ {
return $this->getOption('containsignorecase'); return $this->getOption('containsignorecase');
} }
/**
* Set the facet limit.
*
* @param mixed $limit
*
* @return self Provides fluent interface
*/
public function setLimit($limit)
{
return $this->setOption('limit', $limit);
}
/**
* Get the facet limit.
*
* @return string
*/
public function getLimit()
{
return $this->getOption('limit');
}
/**
* Set the facet offset.
*
* @param int $offset
*
* @return self Provides fluent interface
*/
public function setOffset($offset)
{
return $this->setOption('offset', $offset);
}
/**
* Get the facet offset.
*
* @return int
*/
public function getOffset()
{
return $this->getOption('offset');
}
/**
* Set the facet mincount.
*
* @param int $minCount
*
* @return self Provides fluent interface
*/
public function setMinCount($minCount)
{
return $this->setOption('mincount', $minCount);
}
/**
* Get the facet mincount.
*
* @return int
*/
public function getMinCount()
{
return $this->getOption('mincount');
}
/**
* Set the missing count option.
*
* @param bool $missing
*
* @return self Provides fluent interface
*/
public function setMissing($missing)
{
return $this->setOption('missing', $missing);
}
/**
* Get the facet missing option.
*
* @return bool
*/
public function getMissing()
{
return $this->getOption('missing');
}
/**
* Set the facet method.
*
* Use one of the METHOD_* constants as value
*
* @param string $method
*
* @return self Provides fluent interface
*/
public function setMethod($method)
{
return $this->setOption('method', $method);
}
/**
* Get the facet method.
*
* @return string
*/
public function getMethod()
{
return $this->getOption('method');
}
} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
namespace Solarium\Component\Facet; namespace Solarium\Component\Facet;
use Solarium\Component\FacetSet; use Solarium\Component\FacetSetInterface;
/** /**
* Facet interval. * Facet interval.
...@@ -11,6 +11,10 @@ use Solarium\Component\FacetSet; ...@@ -11,6 +11,10 @@ use Solarium\Component\FacetSet;
*/ */
class Interval extends AbstractFacet class Interval extends AbstractFacet
{ {
use ExcludeTagsTrait {
init as excludeTagsInit;
}
/** /**
* Get the facet type. * Get the facet type.
* *
...@@ -18,7 +22,7 @@ class Interval extends AbstractFacet ...@@ -18,7 +22,7 @@ class Interval extends AbstractFacet
*/ */
public function getType() public function getType()
{ {
return FacetSet::FACET_INTERVAL; return FacetSetInterface::FACET_INTERVAL;
} }
/** /**
...@@ -86,7 +90,7 @@ class Interval extends AbstractFacet ...@@ -86,7 +90,7 @@ class Interval extends AbstractFacet
*/ */
protected function init() protected function init()
{ {
parent::init(); $this->excludeTagsInit();
foreach ($this->options as $name => $value) { foreach ($this->options as $name => $value) {
switch ($name) { switch ($name) {
......
<?php
namespace Solarium\Component\Facet;
use Solarium\Component\FacetSetInterface;
/**
* JSON facet aggregation.
*
* @see https://lucene.apache.org/solr/guide/7_3/json-facet-api.html
*/
class JsonAggregation extends AbstractFacet implements JsonFacetInterface
{
/**
* Get the facet type.
*
* @return string
*/
public function getType()
{
return FacetSetInterface::JSON_FACET_AGGREGATION;
}
/**
* Set the function string.
*
* This overwrites the current value
*
* @param string $function
*
* @return self Provides fluent interface
*/
public function setFunction($function)
{
return $this->setOption('function', $function);
}
/**
* Get the function string.
*
* @return string
*/
public function getFunction()
{
return $this->getOption('function');
}
/**
* Serializes nested facets as option "facet" and returns that array structure.
*
* @return array|string
*/
public function serialize()
{
return $this->getFunction();
}
}
<?php
namespace Solarium\Component\Facet;
/**
* Json facets.
*
* @see https://lucene.apache.org/solr/guide/7_3/json-facet-api.html
*/
interface JsonFacetInterface
{
/**
* Serializes nested facets as option "facet" and returns that array structure.
*
* @return array|string
*/
public function serialize();
}
<?php
namespace Solarium\Component\Facet;
use Solarium\Component\FacetSetInterface;
use Solarium\Component\FacetSetTrait;
use Solarium\Core\Query\Helper;
use Solarium\Exception\InvalidArgumentException;
/**
* Json facets.
*
* @see https://lucene.apache.org/solr/guide/7_3/json-facet-api.html
*/
trait JsonFacetTrait
{
use FacetSetTrait {
addFacet as facetSetAddFacet;
removeFacet as facetSetRemoveFacet;
clearFacets as facetSetClearFacets;
}
/**
* Facet type mapping.
*
* @var array
*/
protected $facetTypes = [
FacetSetInterface::JSON_FACET_TERMS => 'Solarium\Component\Facet\JsonTerms',
FacetSetInterface::JSON_FACET_QUERY => 'Solarium\Component\Facet\JsonQuery',
FacetSetInterface::JSON_FACET_RANGE => 'Solarium\Component\Facet\JsonRange',
FacetSetInterface::JSON_FACET_AGGREGATION => 'Solarium\Component\Facet\JsonAggregation',
];
/**
* Get the domain filter.
*
* @return string
*/
public function getDomainFilter()
{
$domain = $this->getOption('domain');
if ($domain && isset($domain['filter'])) {
return $domain['filter'];
}
}
/**
* Set the domain filter query string.
*
* This overwrites the current value.
*
* @param string $query
* @param array $bind Bind values for placeholders in the query string
*
* @return self Provides fluent interface
*/
public function setDomainFilterQuery(string $query, array $bind = null)
{
if (null !== $bind) {
$helper = new Helper();
$query = $helper->assemble($query, $bind);
}
$filter = $this->getDomainFilter();
if (!$filter || is_string($filter)) {
return $this->setOption('domain', ['filter' => $query]);
} else {
foreach ($filter as &$param_or_query) {
if (is_string($param_or_query)) {
$param_or_query = $query;
return $this->setOption('domain', ['filter' => $filter]);
}
}
$filter[] = $query;
return $this->setOption('domain', ['filter' => $filter]);
}
}
/**
* Adds a domain filter parameter.
*
* @param string $param
*
* @return self Provides fluent interface
*/
public function addDomainFilterParameter(string $param)
{
$filter = $this->getDomainFilter();
if (!$filter) {
return $this->setOption('domain', ['filter' => ['param' => $param]]);
} elseif (is_string($filter) || 1 == count($filter)) {
return $this->setOption('domain', ['filter' => [$filter, ['param' => $param]]]);
} else {
foreach ($filter as &$param_or_query) {
if (is_array($param_or_query) && $param_or_query['param'] == $param) {
return $this;
}
}
$filter[] = ['param' => $param];
return $this->setOption('domain', ['filter' => $filter]);
}
}
/**
* Serializes nested facets as option "facet" and returns that array structure.
*
* @return array|string
*/
public function serialize()
{
// Strip 'json_' prefix.
$this->setOption('type', substr($this->getType(), 5));
$facets = [];
foreach ($this->getFacets() as $key => $facet) {
$facets[$key] = $facet->serialize();
}
if ($facets) {
$this->setOption('facet', $facets);
} elseif (isset($this->options['facet'])) {
unset($this->options['facet']);
}
$options = $this->getOptions();
unset($options['key']);
return $options;
}
/**
* Add a facet.
*
*
* @param FacetInterface|array $facet
*
* @throws InvalidArgumentException
*
* @return self Provides fluent interface
*/
public function addFacet($facet)
{
if ($facet instanceof JsonFacetInterface) {
$this->facetSetAddFacet($facet);
$this->serialize();
return $this;
} else {
throw new InvalidArgumentException('Only JSON facets can be nested.');
}
}
/**
* Remove a single facet.
*
* You can remove a facet by passing its key or the facet instance
*
* @param string|FacetInterface $facet
*
* @return self Provides fluent interface
*/
public function removeFacet($facet)
{
$this->facetSetRemoveFacet($facet);
$this->serialize();
return $this;
}
/**
* Remove all facets.
*
* @return self Provides fluent interface
*/
public function clearFacets()
{
$this->facetSetClearFacets();
$this->serialize();
return $this;
}
}
<?php
namespace Solarium\Component\Facet;
use Solarium\Component\FacetSetInterface;
use Solarium\Core\Query\Helper;
/**
* Facet query.
*
* @see https://lucene.apache.org/solr/guide/7_3/json-facet-api.html
*/
class JsonQuery extends AbstractFacet implements JsonFacetInterface, FacetSetInterface
{
use JsonFacetTrait;
/**
* Default options.
*
* @var array
*/
protected $options = [
'q' => '*:*',
];
/**
* Get the facet type.
*
* @return string
*/
public function getType()
{
return FacetSetInterface::JSON_FACET_QUERY;
}
/**
* Set the query string.
*
* This overwrites the current value
*
* @param string $query
* @param array $bind Bind values for placeholders in the query string
*
* @return self Provides fluent interface
*/
public function setQuery($query, $bind = null)
{
if (null !== $bind) {
$helper = new Helper();
$query = $helper->assemble($query, $bind);
}
return $this->setOption('q', $query);
}
/**
* Get the query string.
*
* @return string
*/
public function getQuery()
{
return $this->getOption('q');
}
}
<?php
namespace Solarium\Component\Facet;
use Solarium\Component\FacetSetInterface;
/**
* Facet range.
*
* @see http://wiki.apache.org/solr/SimpleFacetParameters#Facet_by_Range
*/
class JsonRange extends AbstractRange implements JsonFacetInterface, FacetSetInterface
{
use JsonFacetTrait {
init as jsonFacetInit;
}
/**
* Get the facet type.
*
* @return string
*/
public function getType()
{
return FacetSetInterface::JSON_FACET_RANGE;
}
/**
* Initialize options.
*
* Several options need some extra checks or setup work, for these options
* the setters are called.
*/
protected function init()
{
parent::init();
$this->jsonFacetInit();
}
}
<?php
namespace Solarium\Component\Facet;
use Solarium\Component\FacetSetInterface;
/**
* Facet query.
*
* @see http://wiki.apache.org/solr/SimpleFacetParameters#Field_Value_Faceting_Parameters
*/
class JsonTerms extends AbstractField implements JsonFacetInterface, FacetSetInterface
{
use JsonFacetTrait;
/**
* Facet method "dv" DocValues, collect into ordinal array.
*/
const METHOD_DV = 'dv';
/**
* Facet method "uif" UnInvertedField, collect into ordinal array.
*/
const METHOD_UIF = 'uif';
/**
* Facet method "dvhash" DocValues, collect into hash - improves efficiency over high cardinality fields.
*/
const METHOD_DVHASH = 'dvhash';
/**
* Facet method "enum" TermsEnum then intersect DocSet (stream-able).
*/
const METHOD_ENUM = 'enum';
/**
* Facet method "stream" Presently equivalent to "enum".
*/
const METHOD_STREAM = 'stream';
/**
* Facet method "smart" Pick the best method for the field type (this is the default).
*/
const METHOD_SMART = 'smart';
/**
* Get the facet type.
*
* @return string
*/
public function getType()
{
return FacetSetInterface::JSON_FACET_TERMS;
}
/**
* Set the refine parameter.
*
* If true, turns on distributed facet refining. This uses a second phase to retrieve selected stats from shards so
* that every shard contributes to every returned bucket in this facet and any sub-facets. This makes stats for
* returned buckets exact.
*
* @param bool $refine
*
* @return self Provides fluent interface
*/
public function setRefine(bool $refine)
{
return $this->setOption('refine', $refine);
}
/**
* Get the refine parameter.
*
* @return bool
*/
public function getRefine()
{
return $this->getOption('refine');
}
/**
* Set the overrequest parameter.
*
* Number of buckets beyond the limit to request internally during distributed search. -1 means default.
*
* @param int $overrequest
*
* @return self Provides fluent interface
*/
public function setOverRequest(int $overrequest)
{
return $this->setOption('overrequest', $overrequest);
}
/**
* Get the refine parameter.
*
* @return int
*/
public function getOverRequest()
{
return $this->getOption('overrequest');
}
/**
* Set the numBuckets parameter.
*
* A boolean. If true, adds “numBuckets” to the response, an integer representing the number of buckets for the
* facet (as opposed to the number of buckets returned). Defaults to false.
*
* @param bool $numBuckets
*
* @return self Provides fluent interface
*/
public function setNumBuckets(bool $numBuckets)
{
return $this->setOption('numBuckets', $numBuckets);
}
/**
* Get the numBuckets parameter.
*
* @return bool
*/
public function getNumBuckets()
{
return $this->getOption('numBuckets');
}
/**
* Set the allBuckets parameter.
*
* A boolean. If true, adds an “allBuckets” bucket to the response, representing the union of all of the buckets.
* For multi-valued fields, this is different than a bucket for all of the documents in the domain since a single
* document can belong to multiple buckets. Defaults to false.
*
* @param bool $allBuckets
*
* @return self Provides fluent interface
*/
public function setAllBuckets(bool $allBuckets)
{
return $this->setOption('allBuckets', $allBuckets);
}
/**
* Get the allBuckets parameter.
*
* @return bool
*/
public function getAllBuckets()
{
return $this->getOption('allBuckets');
}
}
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
namespace Solarium\Component\Facet; namespace Solarium\Component\Facet;
use Solarium\Component\Facet\Query as FacetQuery; use Solarium\Component\Facet\Query as FacetQuery;
use Solarium\Component\FacetSet; use Solarium\Component\FacetSetInterface;
use Solarium\Exception\InvalidArgumentException; use Solarium\Exception\InvalidArgumentException;
/** /**
...@@ -14,6 +14,13 @@ use Solarium\Exception\InvalidArgumentException; ...@@ -14,6 +14,13 @@ use Solarium\Exception\InvalidArgumentException;
*/ */
class MultiQuery extends AbstractFacet class MultiQuery extends AbstractFacet
{ {
use ExcludeTagsTrait {
init as excludeTagsInit;
addExclude as excludeTagsAddExclude;
removeExclude as excludeTagsRemoveExclude;
clearExcludes as excludeTagsClearExcludes;
}
/** /**
* Facet query objects. * Facet query objects.
* *
...@@ -28,7 +35,7 @@ class MultiQuery extends AbstractFacet ...@@ -28,7 +35,7 @@ class MultiQuery extends AbstractFacet
*/ */
public function getType() public function getType()
{ {
return FacetSet::FACET_MULTIQUERY; return FacetSetInterface::FACET_MULTIQUERY;
} }
/** /**
...@@ -207,7 +214,7 @@ class MultiQuery extends AbstractFacet ...@@ -207,7 +214,7 @@ class MultiQuery extends AbstractFacet
$facetQuery->addExclude($tag); $facetQuery->addExclude($tag);
} }
return parent::addExclude($tag); return $this->excludeTagsAddExclude($tag);
} }
/** /**
...@@ -229,7 +236,7 @@ class MultiQuery extends AbstractFacet ...@@ -229,7 +236,7 @@ class MultiQuery extends AbstractFacet
$facetQuery->removeExclude($exclude); $facetQuery->removeExclude($exclude);
} }
return parent::removeExclude($exclude); return $this->excludeTagsRemoveExclude($exclude);
} }
/** /**
...@@ -249,7 +256,7 @@ class MultiQuery extends AbstractFacet ...@@ -249,7 +256,7 @@ class MultiQuery extends AbstractFacet
$facetQuery->clearExcludes(); $facetQuery->clearExcludes();
} }
return parent::clearExcludes(); return $this->excludeTagsClearExcludes();
} }
/** /**
...@@ -260,7 +267,7 @@ class MultiQuery extends AbstractFacet ...@@ -260,7 +267,7 @@ class MultiQuery extends AbstractFacet
*/ */
protected function init() protected function init()
{ {
parent::init(); $this->excludeTagsInit();
foreach ($this->options as $name => $value) { foreach ($this->options as $name => $value) {
switch ($name) { switch ($name) {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
namespace Solarium\Component\Facet; namespace Solarium\Component\Facet;
use Solarium\Component\FacetSet; use Solarium\Component\FacetSetInterface;
/** /**
* Facet pivot. * Facet pivot.
...@@ -11,6 +11,8 @@ use Solarium\Component\FacetSet; ...@@ -11,6 +11,8 @@ use Solarium\Component\FacetSet;
*/ */
class Pivot extends AbstractFacet class Pivot extends AbstractFacet
{ {
use ExcludeTagsTrait;
/** /**
* Fields to use. * Fields to use.
* *
...@@ -32,7 +34,7 @@ class Pivot extends AbstractFacet ...@@ -32,7 +34,7 @@ class Pivot extends AbstractFacet
*/ */
public function getType() public function getType()
{ {
return FacetSet::FACET_PIVOT; return FacetSetInterface::FACET_PIVOT;
} }
/** /**
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
namespace Solarium\Component\Facet; namespace Solarium\Component\Facet;
use Solarium\Component\FacetSet; use Solarium\Component\FacetSetInterface;
use Solarium\Core\Query\Helper; use Solarium\Core\Query\Helper;
/** /**
...@@ -12,6 +12,8 @@ use Solarium\Core\Query\Helper; ...@@ -12,6 +12,8 @@ use Solarium\Core\Query\Helper;
*/ */
class Query extends AbstractFacet class Query extends AbstractFacet
{ {
use ExcludeTagsTrait;
/** /**
* Default options. * Default options.
* *
...@@ -28,7 +30,7 @@ class Query extends AbstractFacet ...@@ -28,7 +30,7 @@ class Query extends AbstractFacet
*/ */
public function getType() public function getType()
{ {
return FacetSet::FACET_QUERY; return FacetSetInterface::FACET_QUERY;
} }
/** /**
......
...@@ -2,64 +2,18 @@ ...@@ -2,64 +2,18 @@
namespace Solarium\Component\Facet; namespace Solarium\Component\Facet;
use Solarium\Component\FacetSet; use Solarium\Component\FacetSetInterface;
/** /**
* Facet range. * Facet range.
* *
* @see http://wiki.apache.org/solr/SimpleFacetParameters#Facet_by_Range * @see http://wiki.apache.org/solr/SimpleFacetParameters#Facet_by_Range
*/ */
class Range extends AbstractFacet class Range extends AbstractRange implements ExcludeTagsInterface
{ {
/** use ExcludeTagsTrait {
* Value for the 'other' option. init as excludeTagsInit;
*/ }
const OTHER_BEFORE = 'before';
/**
* Value for the 'other' option.
*/
const OTHER_AFTER = 'after';
/**
* Value for the 'other' option.
*/
const OTHER_BETWEEN = 'between';
/**
* Value for the 'other' option.
*/
const OTHER_ALL = 'all';
/**
* Value for the 'other' option.
*/
const OTHER_NONE = 'none';
/**
* Value for the 'include' option.
*/
const INCLUDE_LOWER = 'lower';
/**
* Value for the 'include' option.
*/
const INCLUDE_UPPER = 'upper';
/**
* Value for the 'include' option.
*/
const INCLUDE_EDGE = 'edge';
/**
* Value for the 'include' option.
*/
const INCLUDE_OUTER = 'outer';
/**
* Value for the 'include' option.
*/
const INCLUDE_ALL = 'all';
/** /**
* Get the facet type. * Get the facet type.
...@@ -68,194 +22,7 @@ class Range extends AbstractFacet ...@@ -68,194 +22,7 @@ class Range extends AbstractFacet
*/ */
public function getType() public function getType()
{ {
return FacetSet::FACET_RANGE; return FacetSetInterface::FACET_RANGE;
}
/**
* Set the field name.
*
* @param string $field
*
* @return self Provides fluent interface
*/
public function setField($field)
{
return $this->setOption('field', $field);
}
/**
* Get the field name.
*
* @return string
*/
public function getField()
{
return $this->getOption('field');
}
/**
* Set the lower bound of the range.
*
* @param string $start
*
* @return self Provides fluent interface
*/
public function setStart($start)
{
return $this->setOption('start', $start);
}
/**
* Get the lower bound of the range.
*
* @return string
*/
public function getStart()
{
return $this->getOption('start');
}
/**
* Set the upper bound of the range.
*
* @param string $end
*
* @return self Provides fluent interface
*/
public function setEnd($end)
{
return $this->setOption('end', $end);
}
/**
* Get the upper bound of the range.
*
* @return string
*/
public function getEnd()
{
return $this->getOption('end');
}
/**
* Set range gap.
*
* The size of each range expressed as a value to be added to the lower bound
*
* @param string $gap
*
* @return self Provides fluent interface
*/
public function setGap($gap)
{
return $this->setOption('gap', $gap);
}
/**
* Get range gap.
*
* The size of each range expressed as a value to be added to the lower bound
*
* @return string
*/
public function getGap()
{
return $this->getOption('gap');
}
/**
* Set hardend option.
*
* A Boolean parameter instructing Solr what to do in the event that facet.range.gap
* does not divide evenly between facet.range.start and facet.range.end
*
* @param bool $hardend
*
* @return self Provides fluent interface
*/
public function setHardend($hardend)
{
return $this->setOption('hardend', $hardend);
}
/**
* Get hardend option.
*
* @return bool
*/
public function getHardend()
{
return $this->getOption('hardend');
}
/**
* Set other counts.
*
* Use one of the constants as value.
* If you want to use multiple values supply an array or comma separated string
*
* @param string|array $other
*
* @return self Provides fluent interface
*/
public function setOther($other)
{
if (is_string($other)) {
$other = explode(',', $other);
$other = array_map('trim', $other);
}
return $this->setOption('other', $other);
}
/**
* Get other counts.
*
* @return array
*/
public function getOther()
{
$other = $this->getOption('other');
if (null === $other) {
$other = [];
}
return $other;
}
/**
* Set include option.
*
* Use one of the constants as value.
* If you want to use multiple values supply an array or comma separated string
*
* @param string|array $include
*
* @return self Provides fluent interface
*/
public function setInclude($include)
{
if (is_string($include)) {
$include = explode(',', $include);
$include = array_map('trim', $include);
}
return $this->setOption('include', $include);
}
/**
* Get include option.
*
* @return array
*/
public function getInclude()
{
$include = $this->getOption('include');
if (null === $include) {
$include = [];
}
return $include;
} }
/** /**
...@@ -289,16 +56,6 @@ class Range extends AbstractFacet ...@@ -289,16 +56,6 @@ class Range extends AbstractFacet
protected function init() protected function init()
{ {
parent::init(); parent::init();
$this->excludeTagsInit();
foreach ($this->options as $name => $value) {
switch ($name) {
case 'include':
$this->setInclude($value);
break;
case 'other':
$this->setOther($value);
break;
}
}
} }
} }
...@@ -2,48 +2,16 @@ ...@@ -2,48 +2,16 @@
namespace Solarium\Component; namespace Solarium\Component;
use Solarium\Component\Facet\AbstractFacet; use Solarium\Component\Facet\FacetInterface;
use Solarium\Component\RequestBuilder\FacetSet as RequestBuilder; use Solarium\Component\RequestBuilder\FacetSet as RequestBuilder;
use Solarium\Component\ResponseParser\FacetSet as ResponseParser; use Solarium\Component\ResponseParser\FacetSet as ResponseParser;
use Solarium\Exception\InvalidArgumentException;
use Solarium\Exception\OutOfBoundsException;
/** /**
* MoreLikeThis component. * FacetSet component.
*
* @see http://wiki.apache.org/solr/MoreLikeThis
*/ */
class FacetSet extends AbstractComponent class FacetSet extends AbstractComponent implements FacetSetInterface
{ {
/** use FacetSetTrait;
* Facet type field.
*/
const FACET_FIELD = 'field';
/**
* Facet type query.
*/
const FACET_QUERY = 'query';
/**
* Facet type multiquery.
*/
const FACET_MULTIQUERY = 'multiquery';
/**
* Facet type range.
*/
const FACET_RANGE = 'range';
/**
* Facet type pivot.
*/
const FACET_PIVOT = 'pivot';
/**
* Facet type interval.
*/
const FACET_INTERVAL = 'interval';
/** /**
* Facet type mapping. * Facet type mapping.
...@@ -51,25 +19,21 @@ class FacetSet extends AbstractComponent ...@@ -51,25 +19,21 @@ class FacetSet extends AbstractComponent
* @var array * @var array
*/ */
protected $facetTypes = [ protected $facetTypes = [
self::FACET_FIELD => 'Solarium\Component\Facet\Field', FacetSetInterface::FACET_FIELD => 'Solarium\Component\Facet\Field',
self::FACET_QUERY => 'Solarium\Component\Facet\Query', FacetSetInterface::FACET_QUERY => 'Solarium\Component\Facet\Query',
self::FACET_MULTIQUERY => 'Solarium\Component\Facet\MultiQuery', FacetSetInterface::FACET_MULTIQUERY => 'Solarium\Component\Facet\MultiQuery',
self::FACET_RANGE => 'Solarium\Component\Facet\Range', FacetSetInterface::FACET_RANGE => 'Solarium\Component\Facet\Range',
self::FACET_PIVOT => 'Solarium\Component\Facet\Pivot', FacetSetInterface::FACET_PIVOT => 'Solarium\Component\Facet\Pivot',
self::FACET_INTERVAL => 'Solarium\Component\Facet\Interval', FacetSetInterface::FACET_INTERVAL => 'Solarium\Component\Facet\Interval',
FacetSetInterface::JSON_FACET_TERMS => 'Solarium\Component\Facet\JsonTerms',
FacetSetInterface::JSON_FACET_QUERY => 'Solarium\Component\Facet\JsonQuery',
FacetSetInterface::JSON_FACET_RANGE => 'Solarium\Component\Facet\JsonRange',
]; ];
/**
* Default options.
*
* @var array
*/
protected $options = [];
/** /**
* Facets. * Facets.
* *
* @var AbstractFacet[] * @var FacetInterface[]
*/ */
protected $facets = []; protected $facets = [];
...@@ -310,174 +274,6 @@ class FacetSet extends AbstractComponent ...@@ -310,174 +274,6 @@ class FacetSet extends AbstractComponent
return $this->getOption('missing'); return $this->getOption('missing');
} }
/**
* Add a facet.
*
*
* @param \Solarium\Component\Facet\AbstractFacet|array $facet
*
* @throws InvalidArgumentException
*
* @return self Provides fluent interface
*/
public function addFacet($facet)
{
if (is_array($facet)) {
$facet = $this->createFacet($facet['type'], $facet, false);
}
$key = $facet->getKey();
if (0 === strlen($key)) {
throw new InvalidArgumentException('A facet must have a key value');
}
//double add calls for the same facet are ignored, but non-unique keys cause an exception
if (array_key_exists($key, $this->facets) && $this->facets[$key] !== $facet) {
throw new InvalidArgumentException('A facet must have a unique key value within a query');
}
$this->facets[$key] = $facet;
return $this;
}
/**
* Add multiple facets.
*
* @param array $facets
*
* @return self Provides fluent interface
*/
public function addFacets(array $facets)
{
foreach ($facets as $key => $facet) {
// in case of a config array: add key to config
if (is_array($facet) && !isset($facet['key'])) {
$facet['key'] = $key;
}
$this->addFacet($facet);
}
return $this;
}
/**
* Get a facet.
*
* @param string $key
*
* @return string
*/
public function getFacet($key)
{
if (isset($this->facets[$key])) {
return $this->facets[$key];
}
}
/**
* Get all facets.
*
* @return AbstractFacet[]
*/
public function getFacets()
{
return $this->facets;
}
/**
* Remove a single facet.
*
* You can remove a facet by passing its key or the facet instance
*
* @param string|\Solarium\Component\Facet\AbstractFacet $facet
*
* @return self Provides fluent interface
*/
public function removeFacet($facet)
{
if (is_object($facet)) {
$facet = $facet->getKey();
}
if (isset($this->facets[$facet])) {
unset($this->facets[$facet]);
}
return $this;
}
/**
* Remove all facets.
*
* @return self Provides fluent interface
*/
public function clearFacets()
{
$this->facets = [];
return $this;
}
/**
* Set multiple facets.
*
* This overwrites any existing facets
*
* @param array $facets
*/
public function setFacets($facets)
{
$this->clearFacets();
$this->addFacets($facets);
}
/**
* Create a facet instance.
*
* If you supply a string as the first arguments ($options) it will be used as the key for the facet
* and it will be added to this query.
* If you supply an options array/object that contains a key the facet will also be added to the query.
*
* When no key is supplied the facet cannot be added, in that case you will need to add it manually
* after setting the key, by using the addFacet method.
*
*
* @param string $type
* @param array|object|null $options
* @param bool $add
*
* @throws OutOfBoundsException
*
* @return \Solarium\Component\Facet\AbstractFacet
*/
public function createFacet($type, $options = null, $add = true)
{
$type = strtolower($type);
if (!isset($this->facetTypes[$type])) {
throw new OutOfBoundsException('Facettype unknown: '.$type);
}
$class = $this->facetTypes[$type];
if (is_string($options)) {
/** @var \Solarium\Component\Facet\Facet $facet */
$facet = new $class();
$facet->setKey($options);
} else {
$facet = new $class($options);
}
if ($add && null !== $facet->getKey()) {
$this->addFacet($facet);
}
return $facet;
}
/** /**
* Get a facet field instance. * Get a facet field instance.
* *
...@@ -488,7 +284,7 @@ class FacetSet extends AbstractComponent ...@@ -488,7 +284,7 @@ class FacetSet extends AbstractComponent
*/ */
public function createFacetField($options = null, $add = true) public function createFacetField($options = null, $add = true)
{ {
return $this->createFacet(self::FACET_FIELD, $options, $add); return $this->createFacet(FacetSetInterface::FACET_FIELD, $options, $add);
} }
/** /**
...@@ -501,7 +297,7 @@ class FacetSet extends AbstractComponent ...@@ -501,7 +297,7 @@ class FacetSet extends AbstractComponent
*/ */
public function createFacetQuery($options = null, $add = true) public function createFacetQuery($options = null, $add = true)
{ {
return $this->createFacet(self::FACET_QUERY, $options, $add); return $this->createFacet(FacetSetInterface::FACET_QUERY, $options, $add);
} }
/** /**
...@@ -514,7 +310,7 @@ class FacetSet extends AbstractComponent ...@@ -514,7 +310,7 @@ class FacetSet extends AbstractComponent
*/ */
public function createFacetMultiQuery($options = null, $add = true) public function createFacetMultiQuery($options = null, $add = true)
{ {
return $this->createFacet(self::FACET_MULTIQUERY, $options, $add); return $this->createFacet(FacetSetInterface::FACET_MULTIQUERY, $options, $add);
} }
/** /**
...@@ -527,7 +323,7 @@ class FacetSet extends AbstractComponent ...@@ -527,7 +323,7 @@ class FacetSet extends AbstractComponent
*/ */
public function createFacetRange($options = null, $add = true) public function createFacetRange($options = null, $add = true)
{ {
return $this->createFacet(self::FACET_RANGE, $options, $add); return $this->createFacet(FacetSetInterface::FACET_RANGE, $options, $add);
} }
/** /**
...@@ -540,7 +336,7 @@ class FacetSet extends AbstractComponent ...@@ -540,7 +336,7 @@ class FacetSet extends AbstractComponent
*/ */
public function createFacetPivot($options = null, $add = true) public function createFacetPivot($options = null, $add = true)
{ {
return $this->createFacet(self::FACET_PIVOT, $options, $add); return $this->createFacet(FacetSetInterface::FACET_PIVOT, $options, $add);
} }
/** /**
...@@ -553,25 +349,45 @@ class FacetSet extends AbstractComponent ...@@ -553,25 +349,45 @@ class FacetSet extends AbstractComponent
*/ */
public function createFacetInterval($options = null, $add = true) public function createFacetInterval($options = null, $add = true)
{ {
return $this->createFacet(self::FACET_INTERVAL, $options, $add); return $this->createFacet(FacetSetInterface::FACET_INTERVAL, $options, $add);
} }
/** /**
* Initialize options. * Get a json facet terms instance.
*
* @param mixed $options
* @param bool $add
* *
* Several options need some extra checks or setup work, for these options * @return \Solarium\Component\Facet\JsonTerms
* the setters are called.
*/ */
protected function init() public function createJsonFacetTerms($options = null, $add = true)
{ {
if (isset($this->options['facet'])) { return $this->createFacet(FacetSetInterface::JSON_FACET_TERMS, $options, $add);
foreach ($this->options['facet'] as $key => $config) {
if (!isset($config['key'])) {
$config['key'] = $key;
} }
$this->addFacet($config); /**
} * Get a json facet query instance.
*
* @param mixed $options
* @param bool $add
*
* @return \Solarium\Component\Facet\JsonQuery
*/
public function createJsonFacetQuery($options = null, $add = true)
{
return $this->createFacet(FacetSetInterface::JSON_FACET_QUERY, $options, $add);
} }
/**
* Get a json facet range instance.
*
* @param mixed $options
* @param bool $add
*
* @return \Solarium\Component\Facet\JsonRange
*/
public function createJsonFacetRange($options = null, $add = true)
{
return $this->createFacet(FacetSetInterface::JSON_FACET_RANGE, $options, $add);
} }
} }
<?php
namespace Solarium\Component;
use Solarium\Component\Facet\FacetInterface;
use Solarium\Exception\InvalidArgumentException;
use Solarium\Exception\OutOfBoundsException;
/**
* FacetSet interface.
*/
interface FacetSetInterface
{
/**
* Facet type field.
*/
const FACET_FIELD = 'field';
/**
* Facet type query.
*/
const FACET_QUERY = 'query';
/**
* Facet type multiquery.
*/
const FACET_MULTIQUERY = 'multiquery';
/**
* Facet type range.
*/
const FACET_RANGE = 'range';
/**
* Facet type pivot.
*/
const FACET_PIVOT = 'pivot';
/**
* Facet type interval.
*/
const FACET_INTERVAL = 'interval';
/**
* Facet type field.
*/
const JSON_FACET_TERMS = 'json_terms';
/**
* Facet type query.
*/
const JSON_FACET_QUERY = 'json_query';
/**
* Facet type range.
*/
const JSON_FACET_RANGE = 'json_range';
/**
* Facet type range.
*/
const JSON_FACET_AGGREGATION = 'json_aggregation';
/**
* Add a facet.
*
* @param FacetInterface|array $facet
*
* @throws InvalidArgumentException
*
* @return \Solarium\Component\FacetSet
*/
public function addFacet($facet);
/**
* Add multiple facets.
*
* @param array $facets
*
* @return \Solarium\Component\FacetSet
*/
public function addFacets(array $facets);
/**
* Get a facet.
*
* @param string $key
*
* @return FacetInterface
*/
public function getFacet($key);
/**
* Get all facets.
*
* @return FacetInterface[]
*/
public function getFacets();
/**
* Remove a single facet.
*
* You can remove a facet by passing its key or the facet instance
*
* @param string|FacetInterface $facet
*
* @return \Solarium\Component\FacetSet
*/
public function removeFacet($facet);
/**
* Remove all facets.
*
* @return \Solarium\Component\FacetSet
*/
public function clearFacets();
/**
* Set multiple facets.
*
* This overwrites any existing facets
*
* @param FacetInterface[] $facets
*/
public function setFacets($facets);
/**
* Create a facet instance.
*
* If you supply a string as the first arguments ($options) it will be used as the key for the facet
* and it will be added to this query.
* If you supply an options array/object that contains a key the facet will also be added to the query.
*
* When no key is supplied the facet cannot be added, in that case you will need to add it manually
* after setting the key, by using the addFacet method.
*
*
* @param string $type
* @param array|object|null $options
* @param bool $add
*
* @throws OutOfBoundsException
*
* @return FacetInterface
*/
public function createFacet(string $type, $options = null, bool $add = true);
}
<?php
namespace Solarium\Component;
use Solarium\Component\Facet\FacetInterface;
use Solarium\Exception\InvalidArgumentException;
use Solarium\Exception\OutOfBoundsException;
/**
* FacetSet trait.
*/
trait FacetSetTrait
{
/**
* Facets.
*
* @var FacetInterface[]
*/
protected $facets = [];
/**
* Add a facet.
*
*
* @param \Solarium\Component\Facet\FacetInterface|array $facet
*
* @throws InvalidArgumentException
*
* @return self Provides fluent interface
*/
public function addFacet($facet)
{
if (is_array($facet)) {
$facet = $this->createFacet($facet['type'], $facet, false);
}
$key = $facet->getKey();
if (0 === strlen($key)) {
throw new InvalidArgumentException('A facet must have a key value');
}
//double add calls for the same facet are ignored, but non-unique keys cause an exception
if (array_key_exists($key, $this->facets) && $this->facets[$key] !== $facet) {
throw new InvalidArgumentException('A facet must have a unique key value within a query');
}
$this->facets[$key] = $facet;
return $this;
}
/**
* Add multiple facets.
*
* @param array $facets
*
* @return self Provides fluent interface
*/
public function addFacets(array $facets)
{
foreach ($facets as $key => $facet) {
// in case of a config array: add key to config
if (is_array($facet) && !isset($facet['key'])) {
$facet['key'] = $key;
}
$this->addFacet($facet);
}
return $this;
}
/**
* Get a facet.
*
* @param string $key
*
* @return FacetInterface
*/
public function getFacet($key)
{
if (isset($this->facets[$key])) {
return $this->facets[$key];
}
}
/**
* Get all facets.
*
* @return FacetInterface[]
*/
public function getFacets()
{
return $this->facets;
}
/**
* Remove a single facet.
*
* You can remove a facet by passing its key or the facet instance
*
* @param string|\Solarium\Component\Facet\FacetInterface $facet
*
* @return self Provides fluent interface
*/
public function removeFacet($facet)
{
if (is_object($facet)) {
$facet = $facet->getKey();
}
if (isset($this->facets[$facet])) {
unset($this->facets[$facet]);
}
return $this;
}
/**
* Remove all facets.
*
* @return self Provides fluent interface
*/
public function clearFacets()
{
$this->facets = [];
return $this;
}
/**
* Set multiple facets.
*
* This overwrites any existing facets
*
* @param array $facets
*/
public function setFacets($facets)
{
$this->clearFacets();
$this->addFacets($facets);
}
/**
* Create a facet instance.
*
* If you supply a string as the first arguments ($options) it will be used as the key for the facet
* and it will be added to this query.
* If you supply an options array/object that contains a key the facet will also be added to the query.
*
* When no key is supplied the facet cannot be added, in that case you will need to add it manually
* after setting the key, by using the addFacet method.
*
*
* @param string $type
* @param array|object|null $options
* @param bool $add
*
* @throws OutOfBoundsException
*
* @return \Solarium\Component\Facet\FacetInterface
*/
public function createFacet(string $type, $options = null, bool $add = true)
{
$type = strtolower($type);
if (!isset($this->facetTypes[$type])) {
throw new OutOfBoundsException('Facettype unknown: '.$type);
}
$class = $this->facetTypes[$type];
if (is_string($options)) {
/** @var FacetInterface $facet */
$facet = new $class();
$facet->setKey($options);
} else {
$facet = new $class($options);
}
if ($add && null !== $facet->getKey()) {
$this->addFacet($facet);
}
return $facet;
}
/**
* Initialize options.
*
* Several options need some extra checks or setup work, for these options
* the setters are called.
*/
protected function init()
{
if (isset($this->options['facet'])) {
foreach ($this->options['facet'] as $key => $config) {
if (!isset($config['key'])) {
$config['key'] = $key;
}
$this->addFacet($config);
}
}
}
}
...@@ -4,11 +4,13 @@ namespace Solarium\Component\RequestBuilder; ...@@ -4,11 +4,13 @@ namespace Solarium\Component\RequestBuilder;
use Solarium\Component\Facet\Field as FacetField; use Solarium\Component\Facet\Field as FacetField;
use Solarium\Component\Facet\Interval as FacetInterval; use Solarium\Component\Facet\Interval as FacetInterval;
use Solarium\Component\Facet\JsonFacetInterface;
use Solarium\Component\Facet\MultiQuery as FacetMultiQuery; use Solarium\Component\Facet\MultiQuery as FacetMultiQuery;
use Solarium\Component\Facet\Pivot as FacetPivot; use Solarium\Component\Facet\Pivot as FacetPivot;
use Solarium\Component\Facet\Query as FacetQuery; use Solarium\Component\Facet\Query as FacetQuery;
use Solarium\Component\Facet\Range as FacetRange; use Solarium\Component\Facet\Range as FacetRange;
use Solarium\Component\FacetSet as FacetsetComponent; use Solarium\Component\FacetSet as FacetsetComponent;
use Solarium\Component\FacetSetInterface;
use Solarium\Core\Client\Request; use Solarium\Core\Client\Request;
use Solarium\Exception\UnexpectedValueException; use Solarium\Exception\UnexpectedValueException;
use Solarium\QueryType\Select\RequestBuilder; use Solarium\QueryType\Select\RequestBuilder;
...@@ -33,42 +35,69 @@ class FacetSet extends RequestBuilder implements ComponentRequestBuilderInterfac ...@@ -33,42 +35,69 @@ class FacetSet extends RequestBuilder implements ComponentRequestBuilderInterfac
{ {
$facets = $component->getFacets(); $facets = $component->getFacets();
if (0 !== count($facets)) { if (0 !== count($facets)) {
// enable faceting $non_json = false;
$request->addParam('facet', 'true'); $json_facets = [];
foreach ($facets as $key => $facet) {
// global facet params
$request->addParam('facet.sort', $component->getSort());
$request->addParam('facet.prefix', $component->getPrefix());
$request->addParam('facet.contains', $component->getContains());
$request->addParam('facet.contains.ignoreCase', null === ($ignoreCase = $component->getContainsIgnoreCase()) ? null : ($ignoreCase ? 'true' : 'false'));
$request->addParam('facet.missing', $component->getMissing());
$request->addParam('facet.mincount', $component->getMinCount());
$request->addParam('facet.limit', $component->getLimit());
foreach ($facets as $facet) {
switch ($facet->getType()) { switch ($facet->getType()) {
case FacetsetComponent::FACET_FIELD: case FacetSetInterface::FACET_FIELD:
/* @var FacetField $facet */
$this->addFacetField($request, $facet); $this->addFacetField($request, $facet);
$non_json = true;
break; break;
case FacetsetComponent::FACET_QUERY: case FacetSetInterface::FACET_QUERY:
/* @var FacetQuery $facet */
$this->addFacetQuery($request, $facet); $this->addFacetQuery($request, $facet);
$non_json = true;
break; break;
case FacetsetComponent::FACET_MULTIQUERY: case FacetSetInterface::FACET_MULTIQUERY:
/* @var FacetMultiQuery $facet */
$this->addFacetMultiQuery($request, $facet); $this->addFacetMultiQuery($request, $facet);
$non_json = true;
break; break;
case FacetsetComponent::FACET_RANGE: case FacetSetInterface::FACET_RANGE:
/* @var FacetRange $facet */
$this->addFacetRange($request, $facet); $this->addFacetRange($request, $facet);
$non_json = true;
break; break;
case FacetsetComponent::FACET_PIVOT: case FacetSetInterface::FACET_PIVOT:
/* @var FacetPivot $facet */
$this->addFacetPivot($request, $facet); $this->addFacetPivot($request, $facet);
$non_json = true;
break; break;
case FacetsetComponent::FACET_INTERVAL: case FacetSetInterface::FACET_INTERVAL:
/* @var FacetInterval $facet */
$this->addFacetInterval($request, $facet); $this->addFacetInterval($request, $facet);
$non_json = true;
break;
case FacetSetInterface::JSON_FACET_TERMS:
case FacetSetInterface::JSON_FACET_QUERY:
case FacetSetInterface::JSON_FACET_RANGE:
case FacetSetInterface::JSON_FACET_AGGREGATION:
/* @var JsonFacetInterface $facet */
$json_facets[$key] = $facet->serialize();
break; break;
default: default:
throw new UnexpectedValueException('Unknown facet type'); throw new UnexpectedValueException('Unknown facet type');
} }
} }
if ($non_json) {
// enable non-json faceting
$request->addParam('facet', 'true');
// global facet params
$request->addParam('facet.sort', $component->getSort());
$request->addParam('facet.prefix', $component->getPrefix());
$request->addParam('facet.contains', $component->getContains());
$request->addParam('facet.contains.ignoreCase', null === ($ignoreCase = $component->getContainsIgnoreCase()) ? null : ($ignoreCase ? 'true' : 'false'));
$request->addParam('facet.missing', $component->getMissing());
$request->addParam('facet.mincount', $component->getMinCount());
$request->addParam('facet.limit', $component->getLimit());
}
if ($json_facets) {
$request->addParam('json.facet', json_encode($json_facets));
}
} }
return $request; return $request;
......
...@@ -8,6 +8,10 @@ use Solarium\Component\Facet\Pivot as QueryFacetPivot; ...@@ -8,6 +8,10 @@ use Solarium\Component\Facet\Pivot as QueryFacetPivot;
use Solarium\Component\Facet\Query as QueryFacetQuery; use Solarium\Component\Facet\Query as QueryFacetQuery;
use Solarium\Component\Facet\Range as QueryFacetRange; use Solarium\Component\Facet\Range as QueryFacetRange;
use Solarium\Component\FacetSet as QueryFacetSet; use Solarium\Component\FacetSet as QueryFacetSet;
use Solarium\Component\FacetSetInterface;
use Solarium\Component\Result\Facet\Aggregation;
use Solarium\Component\Result\Facet\Bucket;
use Solarium\Component\Result\Facet\Buckets;
use Solarium\Component\Result\Facet\Field as ResultFacetField; use Solarium\Component\Result\Facet\Field as ResultFacetField;
use Solarium\Component\Result\Facet\Interval as ResultFacetInterval; use Solarium\Component\Result\Facet\Interval as ResultFacetInterval;
use Solarium\Component\Result\Facet\MultiQuery as ResultFacetMultiQuery; use Solarium\Component\Result\Facet\MultiQuery as ResultFacetMultiQuery;
...@@ -73,27 +77,33 @@ class FacetSet extends ResponseParserAbstract implements ComponentParserInterfac ...@@ -73,27 +77,33 @@ class FacetSet extends ResponseParserAbstract implements ComponentParserInterfac
$facets = []; $facets = [];
foreach ($facetSet->getFacets() as $key => $facet) { foreach ($facetSet->getFacets() as $key => $facet) {
$result = null;
switch ($facet->getType()) { switch ($facet->getType()) {
case QueryFacetSet::FACET_FIELD: case FacetSetInterface::FACET_FIELD:
$result = $this->facetField($query, $facet, $data); $result = $this->facetField($query, $facet, $data);
break; break;
case QueryFacetSet::FACET_QUERY: case FacetSetInterface::FACET_QUERY:
$result = $this->facetQuery($facet, $data); $result = $this->facetQuery($facet, $data);
break; break;
case QueryFacetSet::FACET_MULTIQUERY: case FacetSetInterface::FACET_MULTIQUERY:
$result = $this->facetMultiQuery($facet, $data); $result = $this->facetMultiQuery($facet, $data);
break; break;
case QueryFacetSet::FACET_RANGE: case FacetSetInterface::FACET_RANGE:
$result = $this->facetRange($query, $facet, $data); $result = $this->facetRange($query, $facet, $data);
break; break;
case QueryFacetSet::FACET_PIVOT: case FacetSetInterface::FACET_PIVOT:
$result = $this->facetPivot($query, $facet, $data); $result = $this->facetPivot($query, $facet, $data);
break; break;
case QueryFacetSet::FACET_INTERVAL: case FacetSetInterface::FACET_INTERVAL:
$result = $this->facetInterval($query, $facet, $data); $result = $this->facetInterval($query, $facet, $data);
break; break;
case FacetSetInterface::JSON_FACET_AGGREGATION:
case FacetSetInterface::JSON_FACET_QUERY:
case FacetSetInterface::JSON_FACET_RANGE:
case FacetSetInterface::JSON_FACET_TERMS:
break;
default: default:
throw new RuntimeException('Unknown facet type'); throw new RuntimeException(sprintf('Unknown facet type %s', $facet->getType()));
} }
if (null !== $result) { if (null !== $result) {
...@@ -101,9 +111,46 @@ class FacetSet extends ResponseParserAbstract implements ComponentParserInterfac ...@@ -101,9 +111,46 @@ class FacetSet extends ResponseParserAbstract implements ComponentParserInterfac
} }
} }
if (!empty($data['facets'])) {
$facets += $this->parseJsonFacetSet($data['facets']);
}
return $this->createFacetSet($facets); return $this->createFacetSet($facets);
} }
/**
* Parse JSON facets.
*
* @param array $facets
*
* @return array
*/
protected function parseJsonFacetSet($facets)
{
$buckets_and_aggregations = [];
foreach ($facets as $key => $values) {
if (is_array($values)) {
if (isset($values['buckets'])) {
$buckets = [];
// Parse buckets.
foreach ($values['buckets'] as $bucket) {
$val = $bucket['val'];
$count = $bucket['count'];
unset($bucket['val']);
unset($bucket['count']);
$buckets[] = new Bucket($val, $count, new ResultFacetSet($this->parseJsonFacetSet($bucket)));
}
$buckets_and_aggregations[$key] = new Buckets($buckets);
} else {
$buckets_and_aggregations[$key] = new ResultFacetSet($this->parseJsonFacetSet($values));
}
} else {
$buckets_and_aggregations[$key] = new Aggregation($values);
}
}
return $buckets_and_aggregations;
}
/** /**
* Create a facetset result object. * Create a facetset result object.
* *
......
<?php
namespace Solarium\Component\Result\Facet;
/**
* Aggregation.
*/
class Aggregation
{
/**
* Value.
*
* @var float|int
*/
protected $value;
/**
* Constructor.
*
* @param float $value
*/
public function __construct($value)
{
$this->value = $value;
}
/**
* Get value.
*
* @return float|int
*/
public function getValue()
{
return $this->value;
}
}
<?php
namespace Solarium\Component\Result\Facet;
use Solarium\Component\Facet\FacetInterface;
use Solarium\Component\Result\FacetSet;
/**
* Select field facet result.
*
* A field facet will usually return a dataset of multiple rows, in each row a
* value and its count. You can access the values as an array using
* {@link getValues()} or iterate this object.
*/
class Bucket implements \IteratorAggregate, \Countable
{
/**
* Value.
*
* @var string
*/
protected $value;
/**
* Count.
*
* @var int
*/
protected $count;
/**
* Facet set.
*
* @var FacetSet
*/
protected $facetSet;
/**
* Bucket constructor.
*
* @param string $value
* @param int $count
* @param FacetSet $facets
*/
public function __construct(string $value, int $count, FacetSet $facets)
{
$this->value = $value;
$this->count = $count;
$this->facetSet = $facets;
}
/**
* Get the value.
*
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* Get the count.
*
* @return int
*/
public function getCount()
{
return $this->count;
}
/**
* Get nested facets.
*
* @return FacetInterface[]
*/
public function getFacets()
{
return $this->facetSet->getFacets();
}
/**
* IteratorAggregate implementation.
*
* @return \ArrayIterator
*/
public function getIterator()
{
return $this->facetSet->getIterator();
}
/**
* Countable implementation.
*
* @return int the amount of nested facets
*/
public function count()
{
return count($this->facetSet->getFacets());
}
}
<?php
namespace Solarium\Component\Result\Facet;
/**
* Select field facet result.
*
* A field facet will usually return a dataset of multiple rows, in each row a
* value and its count. You can access the values as an array using
* {@link getValues()} or iterate this object.
*/
class Buckets implements \IteratorAggregate, \Countable
{
/**
* Value array.
*
* @var Bucket[]
*/
protected $buckets;
/**
* Constructor.
*
* @param Bucket[] $values
*/
public function __construct(array $buckets)
{
$this->buckets = $buckets;
}
/**
* Get all values.
*
* @return Bucket[]
*/
public function getBuckets()
{
return $this->buckets;
}
/**
* IteratorAggregate implementation.
*
* @return \ArrayIterator
*/
public function getIterator()
{
return new \ArrayIterator($this->buckets);
}
/**
* Countable implementation.
*
* @return int
*/
public function count()
{
return count($this->buckets);
}
}
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
namespace Solarium\Component\Result; namespace Solarium\Component\Result;
use Solarium\Component\Facet\FacetInterface;
/** /**
* Select component facetset result. * Select component facetset result.
*/ */
...@@ -29,7 +31,7 @@ class FacetSet implements \IteratorAggregate, \Countable ...@@ -29,7 +31,7 @@ class FacetSet implements \IteratorAggregate, \Countable
* *
* @param mixed $key * @param mixed $key
* *
* @return mixed * @return FacetInterface
*/ */
public function getFacet($key) public function getFacet($key)
{ {
...@@ -41,7 +43,7 @@ class FacetSet implements \IteratorAggregate, \Countable ...@@ -41,7 +43,7 @@ class FacetSet implements \IteratorAggregate, \Countable
/** /**
* Get all results. * Get all results.
* *
* @return array * @return FacetInterface[]
*/ */
public function getFacets() public function getFacets()
{ {
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
namespace Solarium\QueryType\Select\Result; namespace Solarium\QueryType\Select\Result;
use Solarium\Component\ComponentAwareQueryInterface; use Solarium\Component\ComponentAwareQueryInterface;
use Solarium\Component\FacetSet;
use Solarium\Component\Result\Debug\Result as DebugResult; use Solarium\Component\Result\Debug\Result as DebugResult;
use Solarium\Component\Result\FacetSet as FacetSetResult;
use Solarium\Component\Result\Grouping\Result as GroupingResult; use Solarium\Component\Result\Grouping\Result as GroupingResult;
use Solarium\Component\Result\Highlighting\Highlighting; use Solarium\Component\Result\Highlighting\Highlighting;
use Solarium\Component\Result\MoreLikeThis\Result as MoreLikeThisResult; use Solarium\Component\Result\MoreLikeThis\Result as MoreLikeThisResult;
...@@ -272,7 +272,7 @@ class Result extends BaseResult implements \IteratorAggregate, \Countable ...@@ -272,7 +272,7 @@ class Result extends BaseResult implements \IteratorAggregate, \Countable
* *
* This is a convenience method that maps presets to getComponent * This is a convenience method that maps presets to getComponent
* *
* @return FacetSet|null * @return FacetSetResult|null
*/ */
public function getFacetSet() public function getFacetSet()
{ {
......
...@@ -5,6 +5,10 @@ namespace Solarium\Tests\Component\RequestBuilder; ...@@ -5,6 +5,10 @@ namespace Solarium\Tests\Component\RequestBuilder;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Solarium\Component\Facet\Field as FacetField; use Solarium\Component\Facet\Field as FacetField;
use Solarium\Component\Facet\Interval as FacetInterval; use Solarium\Component\Facet\Interval as FacetInterval;
use Solarium\Component\Facet\JsonAggregation;
use Solarium\Component\Facet\JsonQuery;
use Solarium\Component\Facet\JsonRange;
use Solarium\Component\Facet\JsonTerms;
use Solarium\Component\Facet\MultiQuery as FacetMultiQuery; use Solarium\Component\Facet\MultiQuery as FacetMultiQuery;
use Solarium\Component\Facet\Pivot as FacetPivot; use Solarium\Component\Facet\Pivot as FacetPivot;
use Solarium\Component\Facet\Query as FacetQuery; use Solarium\Component\Facet\Query as FacetQuery;
...@@ -12,6 +16,7 @@ use Solarium\Component\Facet\Range as FacetRange; ...@@ -12,6 +16,7 @@ use Solarium\Component\Facet\Range as FacetRange;
use Solarium\Component\FacetSet as Component; use Solarium\Component\FacetSet as Component;
use Solarium\Component\RequestBuilder\FacetSet as RequestBuilder; use Solarium\Component\RequestBuilder\FacetSet as RequestBuilder;
use Solarium\Core\Client\Request; use Solarium\Core\Client\Request;
use Solarium\Exception\InvalidArgumentException;
use Solarium\Exception\UnexpectedValueException; use Solarium\Exception\UnexpectedValueException;
class FacetSetTest extends TestCase class FacetSetTest extends TestCase
...@@ -60,7 +65,145 @@ class FacetSetTest extends TestCase ...@@ -60,7 +65,145 @@ class FacetSetTest extends TestCase
$this->assertNull($request->getRawData()); $this->assertNull($request->getRawData());
$this->assertEquals( $this->assertEquals(
'?facet=true&facet.field={!key=f1}owner&facet.query={!key=f2}category:23&facet.query={!key=f4}category:40', '?facet.field={!key=f1}owner&facet.query={!key=f2}category:23&facet.query={!key=f4}category:40&facet=true',
urldecode($request->getUri())
);
}
public function testBuildWithJsonFacets()
{
$this->component->addFacet(new JsonTerms(['key' => 'f1', 'field' => 'owner']));
$this->component->addFacet(new JsonQuery(['key' => 'f2', 'q' => 'category:23']));
$request = $this->builder->buildComponent($this->component, $this->request);
$this->assertNull($request->getRawData());
$this->assertEquals(
'?json.facet={"f1":{"field":"owner","type":"terms"},"f2":{"q":"category:23","type":"query"}}',
urldecode($request->getUri())
);
}
public function testBuildWithJsonFacetFilterQuery()
{
$terms = new JsonTerms(['key' => 'f1', 'field' => 'owner']);
$terms->setDomainFilterQuery('popularity:[5 TO 10]');
$this->component->addFacet($terms);
$request = $this->builder->buildComponent($this->component, $this->request);
$this->assertNull($request->getRawData());
$this->assertEquals(
'?json.facet={"f1":{"field":"owner","domain":{"filter":"popularity:[5 TO 10]"},"type":"terms"}}',
urldecode($request->getUri())
);
}
public function testBuildWithJsonFacetFilterParams()
{
$terms = new JsonTerms(['key' => 'f1', 'field' => 'owner']);
$terms->addDomainFilterParameter('myparam1');
$terms->addDomainFilterParameter('myparam2');
$terms->addDomainFilterParameter('myparam3');
$terms->addDomainFilterParameter('myparam1');
$this->component->addFacet($terms);
$request = $this->builder->buildComponent($this->component, $this->request);
$this->assertNull($request->getRawData());
$this->assertEquals(
'?json.facet={"f1":{"field":"owner","domain":{"filter":[{"param":"myparam1"},{"param":"myparam2"},{"param":"myparam3"}]},"type":"terms"}}',
urldecode($request->getUri())
);
}
public function testBuildWithJsonFacetFilterQueryAndParams()
{
$terms = new JsonTerms(['key' => 'f1', 'field' => 'owner']);
$terms->setDomainFilterQuery('popularity:[5 TO 10]');
$terms->addDomainFilterParameter('myparam1');
$terms->addDomainFilterParameter('myparam2');
$this->component->addFacet($terms);
$request = $this->builder->buildComponent($this->component, $this->request);
$this->assertNull($request->getRawData());
$this->assertEquals(
'?json.facet={"f1":{"field":"owner","domain":{"filter":["popularity:[5 TO 10]",{"param":"myparam1"},{"param":"myparam2"}]},"type":"terms"}}',
urldecode($request->getUri())
);
}
public function testBuildWithJsonFacetFilterParamsAndQuery()
{
$terms = new JsonTerms(['key' => 'f1', 'field' => 'owner']);
$terms->addDomainFilterParameter('myparam1');
$terms->addDomainFilterParameter('myparam2');
$terms->setDomainFilterQuery('popularity:[5 TO 10]');
$this->component->addFacet($terms);
$request = $this->builder->buildComponent($this->component, $this->request);
$this->assertNull($request->getRawData());
$this->assertEquals(
'?json.facet={"f1":{"field":"owner","domain":{"filter":[{"param":"myparam1"},{"param":"myparam2"},"popularity:[5 TO 10]"]},"type":"terms"}}',
urldecode($request->getUri())
);
}
public function testBuildWithFacetsAndJsonFacets()
{
$this->component->addFacet(new FacetField(['key' => 'f1', 'field' => 'owner']));
$this->component->addFacet(new JsonTerms(['key' => 'f2', 'field' => 'customer']));
$this->component->addFacet(new JsonQuery(['key' => 'f3', 'q' => 'category:23']));
$this->component->addFacet(
new FacetMultiQuery(['key' => 'f4', 'query' => ['f5' => ['query' => 'category:40']]])
);
$request = $this->builder->buildComponent($this->component, $this->request);
$this->assertNull($request->getRawData());
$this->assertEquals(
'?facet.field={!key=f1}owner&facet.query={!key=f5}category:40&facet=true&json.facet={"f2":{"field":"customer","type":"terms"},"f3":{"q":"category:23","type":"query"}}',
urldecode($request->getUri())
);
}
public function testBuildWithAggregationFacet()
{
$this->component->addFacet(new JsonAggregation(['key' => 'f1', 'function' => 'avg(mul(price,popularity))']));
$request = $this->builder->buildComponent($this->component, $this->request);
$this->assertNull($request->getRawData());
$this->assertEquals(
'?json.facet={"f1":"avg(mul(price,popularity))"}',
urldecode($request->getUri())
);
}
public function testBuildWithNestedFacets()
{
$terms = new JsonTerms(['key' => 'f1', 'field' => 'owner']);
// Only JSON facets could be nested.
$this->expectException(InvalidArgumentException::class);
$terms->addFacet(new FacetQuery(['key' => 'f2', 'q' => 'category:23']));
}
public function testBuildWithNestedJsonFacets()
{
$terms = new JsonTerms(['key' => 'f1', 'field' => 'owner']);
$query = new JsonQuery(['key' => 'f2', 'q' => 'category:23']);
$query->addFacet(new JsonAggregation(['key' => 'f1', 'function' => 'avg(mul(price,popularity))']));
$query->addFacet(new JsonAggregation(['key' => 'f2', 'function' => 'unique(popularity)']));
$terms->addFacet($query);
$this->component->addFacet($terms);
$request = $this->builder->buildComponent($this->component, $this->request);
$this->assertNull($request->getRawData());
$this->assertEquals(
'?json.facet={"f1":{"field":"owner","type":"terms","facet":{"f2":{"q":"category:23","type":"query","facet":{"f1":"avg(mul(price,popularity))","f2":"unique(popularity)"}}}}}',
urldecode($request->getUri()) urldecode($request->getUri())
); );
} }
...@@ -84,7 +227,31 @@ class FacetSetTest extends TestCase ...@@ -84,7 +227,31 @@ class FacetSetTest extends TestCase
$this->assertNull($request->getRawData()); $this->assertNull($request->getRawData());
$this->assertEquals( $this->assertEquals(
'?facet=true&facet.range={!key=f1}price&f.price.facet.range.start=1&f.price.facet.range.end=100&f.price.facet.range.gap=10&f.price.facet.mincount=123&f.price.facet.range.other=all&f.price.facet.range.include=outer', '?facet.range={!key=f1}price&f.price.facet.range.start=1&f.price.facet.range.end=100&f.price.facet.range.gap=10&f.price.facet.mincount=123&f.price.facet.range.other=all&f.price.facet.range.include=outer&facet=true',
urldecode($request->getUri())
);
}
public function testBuildWithJsonRangeFacet()
{
$this->component->addFacet(new JsonRange(
[
'key' => 'f1',
'field' => 'price',
'start' => '1',
'end' => 100,
'gap' => 10,
'other' => 'all',
'include' => 'outer',
'mincount' => 123,
]
));
$request = $this->builder->buildComponent($this->component, $this->request);
$this->assertNull($request->getRawData());
$this->assertEquals(
'?json.facet={"f1":{"field":"price","start":"1","end":100,"gap":10,"other":["all"],"include":["outer"],"mincount":123,"type":"range"}}',
urldecode($request->getUri()) urldecode($request->getUri())
); );
} }
...@@ -107,8 +274,7 @@ class FacetSetTest extends TestCase ...@@ -107,8 +274,7 @@ class FacetSetTest extends TestCase
$this->assertNull($request->getRawData()); $this->assertNull($request->getRawData());
$this->assertEquals( $this->assertEquals(
'?facet=true&facet.range={!key=f1}price&f.price.facet.range.start=1&f.price.facet.range.end=100'. '?facet.range={!key=f1}price&f.price.facet.range.start=1&f.price.facet.range.end=100&f.price.facet.range.gap=10&facet=true',
'&f.price.facet.range.gap=10',
urldecode($request->getUri()) urldecode($request->getUri())
); );
} }
...@@ -131,8 +297,7 @@ class FacetSetTest extends TestCase ...@@ -131,8 +297,7 @@ class FacetSetTest extends TestCase
); );
static::assertEquals( static::assertEquals(
'?facet=true&facet.missing=true&facet.limit=10&facet.field={!key=f1}owner&facet.query={!key=f2}category:23'. '?facet.field={!key=f1}owner&facet.query={!key=f2}category:23&facet.query={!key=f4}category:40&facet=true&facet.missing=true&facet.limit=10',
'&facet.query={!key=f4}category:40',
urldecode($request->getUri()) urldecode($request->getUri())
); );
} }
...@@ -164,7 +329,7 @@ class FacetSetTest extends TestCase ...@@ -164,7 +329,7 @@ class FacetSetTest extends TestCase
); );
$this->assertEquals( $this->assertEquals(
'?facet=true&facet.pivot={!key=f1 ex=owner}cat,inStock&facet.pivot.mincount=123', '?facet.pivot={!key=f1 ex=owner}cat,inStock&facet.pivot.mincount=123&facet=true',
urldecode($request->getUri()) urldecode($request->getUri())
); );
} }
...@@ -185,7 +350,7 @@ class FacetSetTest extends TestCase ...@@ -185,7 +350,7 @@ class FacetSetTest extends TestCase
$this->assertNull($request->getRawData()); $this->assertNull($request->getRawData());
$this->assertEquals( $this->assertEquals(
'?facet=true&facet.pivot={!stats=piv1}cat,inStock', '?facet.pivot={!stats=piv1}cat,inStock&facet=true',
urldecode($request->getUri()) urldecode($request->getUri())
); );
} }
...@@ -209,7 +374,7 @@ class FacetSetTest extends TestCase ...@@ -209,7 +374,7 @@ class FacetSetTest extends TestCase
$this->assertNull($request->getRawData()); $this->assertNull($request->getRawData());
$this->assertEquals( $this->assertEquals(
'?facet=true&facet.contains=bar&facet.contains.ignoreCase=false&facet.field={!key=f1}owner&f.owner.facet.contains=foo&f.owner.facet.contains.ignoreCase=true', '?facet.field={!key=f1}owner&f.owner.facet.contains=foo&f.owner.facet.contains.ignoreCase=true&facet=true&facet.contains=bar&facet.contains.ignoreCase=false',
urldecode($request->getUri()) urldecode($request->getUri())
); );
} }
...@@ -231,7 +396,7 @@ class FacetSetTest extends TestCase ...@@ -231,7 +396,7 @@ class FacetSetTest extends TestCase
$this->assertNull($request->getRawData()); $this->assertNull($request->getRawData());
$this->assertEquals( $this->assertEquals(
'?facet=true&facet.interval={!key=f1}&f..facet.interval.set=int1&f..facet.interval.set={!key="one"}int2', '?facet.interval={!key=f1}&f..facet.interval.set=int1&f..facet.interval.set={!key="one"}int2&facet=true',
urldecode($request->getUri()) urldecode($request->getUri())
); );
} }
......
...@@ -252,7 +252,7 @@ class FacetSetTest extends TestCase ...@@ -252,7 +252,7 @@ class FacetSetTest extends TestCase
public function testInvalidFacetType() public function testInvalidFacetType()
{ {
$facetStub = $this->createMock(Field::class); $facetStub = $this->createMock(Field::class);
$facetStub->expects($this->once()) $facetStub->expects($this->any())
->method('getType') ->method('getType')
->will($this->returnValue('invalidfacettype')); ->will($this->returnValue('invalidfacettype'));
$facetStub->expects($this->any()) $facetStub->expects($this->any())
...@@ -264,4 +264,90 @@ class FacetSetTest extends TestCase ...@@ -264,4 +264,90 @@ class FacetSetTest extends TestCase
$this->expectException(RuntimeException::class); $this->expectException(RuntimeException::class);
$this->parser->parse($this->query, $this->facetSet, []); $this->parser->parse($this->query, $this->facetSet, []);
} }
public function testParseJsonFacet()
{
$data = [
'facets' => [
'top_genres' => [
'buckets' => [
[
'val' => 'Fantasy',
'count' => 5432,
'top_authors' => [
'buckets' => [
[
'val' => 'Mercedes Lackey',
'count' => 121,
],
[
'val' => 'Piers Anthony',
'count' => 98,
],
],
],
'highpop' => [
'count' => 876,
'publishers' => [
'buckets' => [
[
'val' => 'Bantam Books',
'count' => 346,
],
[
'val' => 'Tor',
'count' => 217,
],
],
],
],
],
[
'val' => 'Science Fiction',
'count' => 4188,
'top_authors' => [
'buckets' => [
[
'val' => 'Terry Pratchett',
'count' => 87,
],
],
],
'highpop' => [
'count' => 876,
'publishers' => [
'buckets' => [
[
'val' => 'Harper Collins',
'count' => 43,
],
],
],
],
],
],
],
],
];
$result = $this->parser->parse($this->query, $this->facetSet, $data);
$facets = $result->getFacets();
$this->assertEquals(['top_genres'], array_keys($facets));
$buckets = $facets['top_genres']->getBuckets();
$this->assertEquals(
'Fantasy',
$buckets[0]->getValue()
);
$this->assertEquals(
5432,
$buckets[0]->getCount()
);
$nested_facets = $buckets[0]->getFacets();
$this->assertEquals(['top_authors', 'highpop'], array_keys($nested_facets));
}
} }
...@@ -4,11 +4,13 @@ namespace Solarium\Tests\QueryType\Select\Query\Component\Facet; ...@@ -4,11 +4,13 @@ namespace Solarium\Tests\QueryType\Select\Query\Component\Facet;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Solarium\Component\Facet\AbstractFacet; use Solarium\Component\Facet\AbstractFacet;
use Solarium\Component\Facet\ExcludeTagsTrait;
use Solarium\Component\Facet\FacetInterface;
class FacetTest extends TestCase class FacetTest extends TestCase
{ {
/** /**
* @var AbstractFacet * @var FacetInterface
*/ */
protected $facet; protected $facet;
...@@ -72,6 +74,8 @@ class FacetTest extends TestCase ...@@ -72,6 +74,8 @@ class FacetTest extends TestCase
class TestFacet extends AbstractFacet class TestFacet extends AbstractFacet
{ {
use ExcludeTagsTrait;
public function getType() public function getType()
{ {
return 'test'; return 'test';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment