diff --git a/Bool/Bool.php b/Bool/Bool.php index 589be164b1bb2f6aec8845d9d477e005691ed336..69194fe9003a43166099705d67797513c1081cf9 100644 --- a/Bool/Bool.php +++ b/Bool/Bool.php @@ -50,11 +50,13 @@ class Bool implements BuilderInterface */ public function addToBool(BuilderInterface $bool, $type = self::MUST) { - if (in_array($type, [ self::MUST, self::MUST_NOT, self::SHOULD ])) { - $this->container[$type][] = $bool; - } else { + $constants = (new \ReflectionObject($this))->getConstants(); + + if (!in_array($type, $constants)) { throw new \UnexpectedValueException(sprintf('The bool operator %s is not supported', $type)); } + + $this->container[$type][] = $bool; } /** diff --git a/Highlight/Highlight.php b/Highlight/Highlight.php index cee68eed71c641bf66e0fdb1b859f4294127dcef..68601de46c1e77e80580c7fc9c19aec3c0bd97f3 100644 --- a/Highlight/Highlight.php +++ b/Highlight/Highlight.php @@ -11,13 +11,14 @@ namespace ONGR\ElasticsearchBundle\DSL\Highlight; +use ONGR\ElasticsearchBundle\DSL\BuilderInterface; use ONGR\ElasticsearchBundle\DSL\NamedBuilderBag; use ONGR\ElasticsearchBundle\DSL\NamedBuilderInterface; /** * Data holder for highlight api. */ -class Highlight extends NamedBuilderBag +class Highlight extends NamedBuilderBag implements BuilderInterface { const TYPE_PLAIN = 'plain'; const TYPE_POSTINGS = 'postings'; @@ -170,6 +171,14 @@ class Highlight extends NamedBuilderBag return $this; } + /** + * {@inheritdoc} + */ + public function getType() + { + return 'highlight'; + } + /** * {@inheritdoc} */ @@ -182,6 +191,7 @@ class Highlight extends NamedBuilderBag 'fragment_size' => $this->fragmentSize, 'number_of_fragments' => $this->numberOfFragments, 'tags_schema' => $this->tagsSchema, + 'fields' => $this->getFields(), ] ); @@ -197,11 +207,21 @@ class Highlight extends NamedBuilderBag } } - /** @var NamedBuilderInterface $field */ - foreach ($this->all() as $field) { - $highlight['fields'][$field->getName()] = $field->toArray(); + return $highlight; + } + + /** + * Returns fields as array. + * + * @return array + */ + private function getFields() + { + $out = []; + foreach ($this->all() as $builder) { + $out = array_merge($out, [$builder->getName() => $builder->toArray()]); } - return $highlight; + return $out; } } diff --git a/NamedBuilderBag.php b/NamedBuilderBag.php index e808d5dd526014a1f15028cc8b4dda7322d061c6..a73f6e0613229f1aca5ae00fe687ca834e1c6843 100644 --- a/NamedBuilderBag.php +++ b/NamedBuilderBag.php @@ -111,4 +111,17 @@ class NamedBuilderBag } ); } + + /** + * {@inheritdoc} + */ + public function toArray() + { + $out = []; + foreach ($this->all() as $builder) { + $out = array_merge($out, $builder->toArray()); + } + + return $out; + } } diff --git a/ParametersTrait.php b/ParametersTrait.php index e865e9aae8b1a535835cb1e529e5f361e600969c..aa101ddef25f7670f5c65ea421e3f33232f7fa92 100644 --- a/ParametersTrait.php +++ b/ParametersTrait.php @@ -42,11 +42,7 @@ trait ParametersTrait */ public function getParameter($name) { - if ($this->hasParameter($name)) { - return $this->parameters[$name]; - } - - return false; + return $this->parameters[$name]; } /** @@ -65,9 +61,7 @@ trait ParametersTrait */ public function addParameter($name, $value) { - if (!$this->hasParameter($name)) { - $this->parameters[$name] = $value; - } + $this->parameters[$name] = $value; } /** diff --git a/Query/FilteredQuery.php b/Query/FilteredQuery.php index e216d266560052276c5d3e3113d73bd999eb13ef..bc4d8f700fb94baf2c2851608dd62149ac3326cd 100644 --- a/Query/FilteredQuery.php +++ b/Query/FilteredQuery.php @@ -69,8 +69,8 @@ class FilteredQuery extends AbstractFilter implements BuilderInterface $output = []; $output['filter'] = parent::toArray(); - if ($this->query !== null) { - $output['query'] = $this->query->toArray(); + if ($this->query) { + $output['query'][$this->getQuery()->getType()] = $this->getQuery()->toArray(); } return $output; diff --git a/Query/QueryAwareTrait.php b/Query/QueryAwareTrait.php deleted file mode 100644 index 262fdd694193f93565cd366245c043cf1ec07c72..0000000000000000000000000000000000000000 --- a/Query/QueryAwareTrait.php +++ /dev/null @@ -1,138 +0,0 @@ -<?php - -/* - * This file is part of the ONGR package. - * - * (c) NFQ Technologies UAB <info@nfq.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace ONGR\ElasticsearchBundle\DSL\Query; - -use ONGR\ElasticsearchBundle\DSL\Bool\Bool; -use ONGR\ElasticsearchBundle\DSL\BuilderInterface; - -/** - * Provides query container functionality to any class. - */ -trait QueryAwareTrait -{ - /** - * @var BuilderInterface[] - */ - private $queries = []; - - /** - * @var \ONGR\ElasticsearchBundle\DSL\Bool\Bool - */ - private $boolQuery; - - /** - * @param BuilderInterface $query - * @param string $boolType - * - * @return $this - */ - public function addQuery(BuilderInterface $query, $boolType = Bool::MUST) - { - if ($boolType !== Bool::MUST || $this->boolQuery !== null) { - $this->getBoolQuery()->addToBool($query, $boolType); - } else { - $this->queries[$query->getType()] = $query; - } - - return $this; - } - - /** - * Returns Bool query. Creates new instance if there is not initiated. - * - * @return \ONGR\ElasticsearchBundle\DSL\Bool\Bool - */ - public function getBoolQuery() - { - if (!$this->boolQuery) { - $this->boolQuery = new Bool(); - } - - return $this->boolQuery; - } - - /** - * @param array $params Example values: - * - minimum_should_match => 1 - * - boost => 1. - */ - public function setBoolQueryParameters(array $params) - { - $this->getBoolQuery()->setParameters($params); - } - - /** - * Checks if there is added specific query. - * - * @param string $type Query type. - * - * @return bool - */ - public function hasQuery($type) - { - return array_key_exists($type, $this->queries); - } - - /** - * Removes specific query. - * - * @param string $key - */ - public function removeQuery($key) - { - if ($this->hasQuery($key)) { - unset($this->queries[$key]); - } - } - - /** - * Completely resets query. - */ - public function destroyQuery() - { - $this->queries = []; - $this->boolQuery = null; - } - - /** - * Return all queries. - * - * @return array - */ - public function getQueries() - { - return $this->queries; - } - - /** - * Aggregates all queries to array. - * - * @return array - */ - public function processQueries() - { - if ($this->boolQuery || count($this->getQueries()) > 1) { - $bool = $this->getBoolQuery(); - foreach ($this->getQueries() as $query) { - $bool->addToBool($query); - } - - return ['query' => [$bool->getType() => $bool->toArray()]]; - } elseif (count($this->getQueries()) == 1) { - $query = array_values($this->getQueries())[0]; - - return ['query' => [$query->getType() => $query->toArray()]]; - } - - return []; - } -} diff --git a/Search.php b/Search.php index 5e9bd482cd94b80d724d0cbe93b0ee23b071c3e6..a7945ad77d13321977c89293c6e1d0998184eeec 100644 --- a/Search.php +++ b/Search.php @@ -12,45 +12,23 @@ namespace ONGR\ElasticsearchBundle\DSL; use ONGR\ElasticsearchBundle\DSL\Aggregation\AbstractAggregation; -use ONGR\ElasticsearchBundle\DSL\Bool\Bool; -use ONGR\ElasticsearchBundle\DSL\Filter\PostFilter; use ONGR\ElasticsearchBundle\DSL\Highlight\Highlight; -use ONGR\ElasticsearchBundle\DSL\Query\FilteredQuery; -use ONGR\ElasticsearchBundle\DSL\Query\Query; -use ONGR\ElasticsearchBundle\DSL\Query\QueryAwareTrait; +use ONGR\ElasticsearchBundle\DSL\SearchEndpoint\SearchEndpointFactory; +use ONGR\ElasticsearchBundle\DSL\SearchEndpoint\SearchEndpointInterface; use ONGR\ElasticsearchBundle\DSL\Sort\AbstractSort; use ONGR\ElasticsearchBundle\DSL\Sort\Sorts; use ONGR\ElasticsearchBundle\DSL\Suggester\AbstractSuggester; +use ONGR\ElasticsearchBundle\Serializer\Normalizer\CustomReferencedNormalizer; +use ONGR\ElasticsearchBundle\Serializer\OrderedSerializer; +use Symfony\Component\Serializer\Normalizer\CustomNormalizer; /** * Search object that can be executed by a manager. */ class Search { - use QueryAwareTrait; - const SCROLL_DURATION = '5m'; - /** - * @var array - */ - private $boolQueryParams; - - /** - * @var BuilderInterface - */ - private $filters; - - /** - * @var BuilderInterface Filters collection. - */ - private $postFilters; - - /** - * @var array - */ - private $boolFilterParams; - /** * @var int */ @@ -61,11 +39,6 @@ class Search */ private $from; - /** - * @var Sorts - */ - private $sorts; - /** * @var string|null */ @@ -86,16 +59,6 @@ class Search */ private $scriptFields; - /** - * @var NamedBuilderBag - */ - private $suggesters; - - /** - * @var Highlight - */ - private $highlight; - /** * @var string */ @@ -111,11 +74,6 @@ class Search */ private $stats; - /** - * @var NamedBuilderBag - */ - private $aggregations; - /** * @var string[] */ @@ -127,364 +85,414 @@ class Search private $minScore; /** - * @return float + * @var OrderedSerializer */ - public function getMinScore() + private $serializer; + + /** + * @var SearchEndpointInterface[] + */ + private $endpoints = []; + + /** + * Initializes serializer. + */ + public function __construct() { - return $this->minScore; + $this->serializer = new OrderedSerializer( + [ + new CustomReferencedNormalizer(), + new CustomNormalizer(), + ] + ); } /** - * Set min score. + * Adds query to search. * - * @param float $minScore + * @param BuilderInterface $query + * @param string $boolType * - * @return $this + * @return Search */ - public function setMinScore($minScore) + public function addQuery(BuilderInterface $query, $boolType = '') { - $this->minScore = $minScore; + $this + ->getEndpoint('query') + ->addBuilder($query, ['bool_type' => $boolType]); return $this; } /** - * Set offset. + * Sets parameters for bool query. * - * @param int $from + * @param array $params Example values: + * - minimum_should_match => 1 + * - boost => 1. * - * @return $this + * @return Search */ - public function setFrom($from) + public function setBoolQueryParameters(array $params) { - $this->from = $from; + $this + ->getEndpoint('query') + ->setParameters($params); return $this; } /** - * Set maximum number of results. + * Returns contained query. * - * @param int $size + * @return BuilderInterface + */ + public function getQuery() + { + return $this + ->getEndpoint('query') + ->getBuilder(); + } + + /** + * Destroys query part. * - * @return $this + * @return Search */ - public function setSize($size) + public function destroyQuery() { - $this->size = $size; + $this->destroyEndpoint('query'); return $this; } /** - * Add sort. + * Adds a filter to search. * - * @param AbstractSort $sort + * @param BuilderInterface $filter Filter. + * @param string $boolType Possible boolType values: + * - must + * - must_not + * - should. * - * @return $this + * @return Search */ - public function addSort($sort) + public function addFilter(BuilderInterface $filter, $boolType = '') { - if ($this->sorts === null) { - $this->sorts = new Sorts(); - } + $this->getEndpoint('query'); - $this->sorts->addSort($sort); + $this + ->getEndpoint('filter') + ->addBuilder($filter, ['bool_type' => $boolType]); return $this; } /** - * Set source. + * Returns currently contained filters. * - * @param array|bool|string $source - * - * @return $this + * @return BuilderInterface */ - public function setSource($source) + public function getFilters() { - $this->source = $source; - - return $this; + return $this + ->getEndpoint('filter') + ->getBuilder(); } /** - * Set fields. + * Sets bool filter parameters. * - * @param array $fields + * @param array $params Possible values: + * _cache => true + * false. * - * @return $this + * @return Search */ - public function setFields(array $fields) + public function setBoolFilterParameters($params) { - $this->fields = $fields; + $this + ->getEndpoint('filter') + ->setParameters($params); return $this; } /** - * Set script fields. - * - * @param array $scriptFields - * - * @return $this + * Destroys filter part. */ - public function setScriptFields($scriptFields) + public function destroyFilters() { - $this->scriptFields = $scriptFields; - - return $this; + $this->destroyEndpoint('filter'); } /** + * Adds a post filter to search. + * * @param BuilderInterface $postFilter Post filter. * @param string $boolType Possible boolType values: * - must * - must_not * - should. * - * @return $this + * @return Search */ - public function addPostFilter(BuilderInterface $postFilter, $boolType = 'must') + public function addPostFilter(BuilderInterface $postFilter, $boolType = '') { - if ($this->postFilters === null) { - $this->postFilters = new Bool(); - } - - $this->postFilters->addToBool($postFilter, $boolType); + $this + ->getEndpoint('post_filter') + ->addBuilder($postFilter, ['bool_type' => $boolType]); return $this; } /** - * Sets highlight. - * - * @param Highlight $highlight + * Returns all contained post filters. * - * @return $this + * @return BuilderInterface */ - public function setHighlight($highlight) + public function getPostFilters() { - $this->highlight = $highlight; - - return $this; + return $this + ->getEndpoint('post_filter') + ->getBuilder(); } /** - * Set search type. + * Sets bool post filter parameters. * - * @param string $searchType + * @param array $params Possible values: + * _cache => true + * false. * - * @return $this + * @return Search */ - public function setSearchType($searchType) + public function setBoolPostFilterParameters($params) { - $this->searchType = $searchType; + $this + ->getEndpoint('post_filter') + ->setParameters($params); return $this; } /** - * Set explain. + * Returns min score value. * - * @param bool $explain - * - * @return $this + * @return float */ - public function setExplain($explain) + public function getMinScore() { - $this->explain = $explain; - - return $this; + return $this->minScore; } /** - * Set stats. + * Exclude documents which have a _score less than the minimum specified. * - * @param array $stats + * @param float $minScore * - * @return $this + * @return Search */ - public function setStats($stats) + public function setMinScore($minScore) { - $this->stats = $stats; + $this->minScore = $minScore; return $this; } /** - * Setter for scroll duration, effectively setting if search is scrolled or not. + * Paginate results from. * - * @param string|null $duration + * @param int $from * * @return Search */ - public function setScroll($duration = self::SCROLL_DURATION) + public function setFrom($from) { - $this->scrollDuration = $duration; + $this->from = $from; return $this; } /** - * Setter for preference. + * Returns results offset value. * - * Controls which shard replicas to execute the search request on. + * @return int + */ + public function getFrom() + { + return $this->from; + } + + /** + * Set maximum number of results. * - * @param mixed $preferenceParams Possible values: - * _primary - * _primary_first - * _local - * _only_node:xyz (xyz - node id) - * _prefer_node:xyz (xyz - node id) - * _shards:2,3 (2 and 3 specified shards) - * custom value - * string[] combination of params. + * @param int $size * - * @return Search $this + * @return Search */ - public function setPreference($preferenceParams) + public function setSize($size) { - if (is_string($preferenceParams)) { - $this->preference[] = $preferenceParams; - } - - if (is_array($preferenceParams) && !empty($preferenceParams)) { - $this->preference = $preferenceParams; - } + $this->size = $size; return $this; } /** - * Returns preference params as string. + * Returns maximum number of results query can request. * - * @return string + * @return int */ - protected function getPreference() + public function getSize() { - return implode(';', $this->preference); + return $this->size; } /** - * Returns scroll duration. + * Adds sort to search. + * + * @param AbstractSort $sort * - * @return null|string + * @return Search */ - public function getScroll() + public function addSort(AbstractSort $sort) { - return $this->scrollDuration; + $this + ->getEndpoint('sort') + ->addBuilder($sort); + + return $this; } /** - * @param AbstractAggregation $agg + * Returns currectly contained sorts object. * - * @return $this + * @return Sorts */ - public function addAggregation($agg) + public function getSorts() { - if ($this->aggregations === null) { - $this->aggregations = new NamedBuilderBag(); - } - $this->aggregations->add($agg); - - return $this; + return $this + ->getEndpoint('sort') + ->getBuilder(); } /** - * @param BuilderInterface $filter Filter. - * @param string $boolType Possible boolType values: - * - must - * - must_not - * - should. + * Allows to control how the _source field is returned with every hit. * - * @return $this + * @param array|bool|string $source + * + * @return Search */ - public function addFilter(BuilderInterface $filter, $boolType = 'must') + public function setSource($source) { - if ($this->filters === null) { - $this->filters = new Bool(); - } - - $this->filters->addToBool($filter, $boolType); + $this->source = $source; return $this; } /** - * @param array $params Possible values: - * _cache => true - * false. + * Returns source value. + * + * @return array|bool|string */ - public function setBoolFilterParameters($params) + public function getSource() { - $this->boolFilterParams = $params; + return $this->source; } /** - * @param AbstractSuggester $suggester + * Allows to selectively load specific stored fields for each document represented by a search hit. + * + * @param array $fields * * @return Search */ - public function addSuggester(AbstractSuggester $suggester) + public function setFields(array $fields) { - if ($this->suggesters === null) { - $this->suggesters = new NamedBuilderBag(); - } - $this->suggesters->add($suggester); + $this->fields = $fields; return $this; } /** - * Returns query url parameters. + * Returns field value. * * @return array */ - public function getQueryParams() + public function getFields() { - $array = []; - - if ($this->scrollDuration !== null) { - $array['scroll'] = $this->scrollDuration; - } + return $this->fields; + } - if ($this->searchType !== null) { - $array['search_type'] = $this->searchType; - } + /** + * Allows to return a script evaluation (based on different fields) for each hit. + * + * @param array $scriptFields + * + * @return Search + */ + public function setScriptFields($scriptFields) + { + $this->scriptFields = $scriptFields; - if ($this->preference !== null) { - $array['preference'] = $this->getPreference(); - } + return $this; + } - return $array; + /** + * Returns containing script fields. + * + * @return array + */ + public function getScriptFields() + { + return $this->scriptFields; } /** - * @return NamedBuilderBag + * Allows to highlight search results on one or more fields. + * + * @param Highlight $highlight + * + * @return Search */ - public function getAggregations() + public function setHighlight($highlight) { - return $this->aggregations; + $this + ->getEndpoint('highlight') + ->addBuilder($highlight); + + return $this; } /** - * @return array + * Returns containing highlight object. + * + * @return Highlight */ - public function getBoolFilterParameters() + public function getHighlight() { - return $this->boolFilterParams; + return $this + ->getEndpoint('highlight') + ->getBuilder(); } /** - * @return array + * Sets explain property in request body search. + * + * @param bool $explain + * + * @return Search */ - public function getBoolQueryParameters() + public function setExplain($explain) { - return $this->boolQueryParams; + $this->explain = $explain; + + return $this; } /** + * Returns if explain property is set in request body search. + * * @return bool */ public function isExplain() @@ -493,107 +501,187 @@ class Search } /** - * @return array + * Sets a stats group. + * + * @param array $stats + * + * @return Search */ - public function getFields() + public function setStats($stats) { - return $this->fields; + $this->stats = $stats; + + return $this; } /** - * @return BuilderInterface[] + * Returns a stats group. + * + * @return array */ - public function getFilters() + public function getStats() { - return $this->filters; + return $this->stats; } /** - * @return int + * Adds aggregation into search. + * + * @param AbstractAggregation $aggregation + * + * @return Search */ - public function getFrom() + public function addAggregation(AbstractAggregation $aggregation) { - return $this->from; + $this + ->getEndpoint('aggregations') + ->addBuilder($aggregation); + + return $this; } /** - * @return Highlight + * Returns contained aggregations. + * + * @return AbstractAggregation[] */ - public function getHighlight() + public function getAggregations() { - return $this->highlight; + return $this + ->getEndpoint('aggregations') + ->getBuilder(); } /** - * @return BuilderInterface + * Adds suggester to search. + * + * @param AbstractSuggester $suggester + * + * @return Search */ - public function getPostFilters() + public function addSuggester(AbstractSuggester $suggester) { - return $this->postFilters; + $this + ->getEndpoint('suggest') + ->addBuilder($suggester); + + return $this; } /** - * @return array + * Returns all contained suggester's. + * + * @return AbstractSuggester[] */ - public function getScriptFields() + public function getSuggesters() { - return $this->scriptFields; + return $this + ->getEndpoint('suggest') + ->getBuilder(); } /** - * @return null|string + * Setter for scroll duration, effectively setting if search is scrolled or not. + * + * @param string|null $duration + * + * @return Search */ - public function getScrollDuration() + public function setScroll($duration = self::SCROLL_DURATION) { - return $this->scrollDuration; + $this->scrollDuration = $duration; + + return $this; } /** - * @return string + * Returns scroll duration. + * + * @return string|null */ - public function getSearchType() + public function getScroll() { - return $this->searchType; + return $this->scrollDuration; } /** - * @return int + * Set search type. + * + * @param string $searchType + * + * @return Search */ - public function getSize() + public function setSearchType($searchType) { - return $this->size; + $this->searchType = $searchType; + + return $this; } /** - * @return Sorts + * Returns search type used. + * + * @return string */ - public function getSorts() + public function getSearchType() { - return $this->sorts; + return $this->searchType; } /** - * @return array|bool|string + * Setter for preference. + * + * Controls which shard replicas to execute the search request on. + * + * @param mixed $preferenceParams Possible values: + * _primary + * _primary_first + * _local + * _only_node:xyz (xyz - node id) + * _prefer_node:xyz (xyz - node id) + * _shards:2,3 (2 and 3 specified shards) + * custom value + * string[] combination of params. + * + * @return Search */ - public function getSource() + public function setPreference($preferenceParams) { - return $this->source; + if (is_string($preferenceParams)) { + $this->preference[] = $preferenceParams; + } + + if (is_array($preferenceParams) && !empty($preferenceParams)) { + $this->preference = $preferenceParams; + } + + return $this; } /** - * @return array + * Returns preference params as string. + * + * @return string */ - public function getStats() + public function getPreference() { - return $this->stats; + return $this->preference ? implode(';', $this->preference) : null; } /** - * @return NamedBuilderBag + * Returns query url parameters. + * + * @return array */ - public function getSuggesters() + public function getQueryParams() { - return $this->suggesters; + return array_filter( + [ + 'scroll' => $this->getScroll(), + 'search_type' => $this->getSearchType(), + 'preference' => $this->getPreference(), + ] + ); } /** @@ -601,29 +689,7 @@ class Search */ public function toArray() { - /* - * First we check if there are some filter to add to filtered query. - * For now we use match all, but its gonna be changed. - */ - - if ($this->filters !== null) { - $filteredQuery = new FilteredQuery(); - $filteredQuery->setFilter($this->filters); - $this->addQuery($filteredQuery); - } - - $output = $this->processQueries(); - - if ($this->postFilters !== null) { - $postFilter = new PostFilter(); - $postFilter->setFilter($this->postFilters); - - $output[$postFilter->getType()] = $postFilter->toArray(); - } - - if ($this->highlight !== null) { - $output['highlight'] = $this->highlight->toArray(); - } + $output = array_filter($this->serializer->normalize($this->endpoints)); $params = [ 'from' => 'from', @@ -633,6 +699,7 @@ class Search 'explain' => 'explain', 'stats' => 'stats', 'minScore' => 'min_score', + 'source' => '_source', ]; foreach ($params as $field => $param) { @@ -641,36 +708,32 @@ class Search } } - if ($this->sorts && $this->sorts->isRelevant()) { - $output[$this->sorts->getType()] = $this->sorts->toArray(); - } - - if ($this->source !== null) { - $output['_source'] = $this->source; - } - - if ($this->aggregations !== null) { - $aggregationsOutput = []; - foreach ($this->aggregations->all() as $aggregation) { - $aggregationsOutput = array_merge($aggregationsOutput, $aggregation->toArray()); - } + return $output; + } - if (!empty($aggregationsOutput)) { - $output['aggregations'] = $aggregationsOutput; - } + /** + * Returns endpoint instance. + * + * @param string $type Endpoint type. + * + * @return SearchEndpointInterface + */ + private function getEndpoint($type) + { + if (!array_key_exists($type, $this->endpoints)) { + $this->endpoints[$type] = SearchEndpointFactory::get($type); } - if ($this->suggesters !== null) { - $suggestersOutput = []; - foreach ($this->suggesters->all() as $suggester) { - $suggestersOutput = array_merge($suggestersOutput, $suggester->toArray()); - } - - if (!empty($suggestersOutput)) { - $output['suggest'] = $suggestersOutput; - } - } + return $this->endpoints[$type]; + } - return $output; + /** + * Destroys search endpoint. + * + * @param string $type Endpoint type. + */ + private function destroyEndpoint($type) + { + unset($this->endpoints[$type]); } } diff --git a/SearchEndpoint/AbstractSearchEndpoint.php b/SearchEndpoint/AbstractSearchEndpoint.php new file mode 100644 index 0000000000000000000000000000000000000000..4effbf8ce55b4440ee2782279e3803b9eff5bd37 --- /dev/null +++ b/SearchEndpoint/AbstractSearchEndpoint.php @@ -0,0 +1,21 @@ +<?php + +/* + * This file is part of the ONGR package. + * + * (c) NFQ Technologies UAB <info@nfq.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ElasticsearchBundle\DSL\SearchEndpoint; + +use ONGR\ElasticsearchBundle\Serializer\Normalizer\AbstractNormalizable; + +/** + * Abstract class used to define search endpoint with references. + */ +abstract class AbstractSearchEndpoint extends AbstractNormalizable implements SearchEndpointInterface +{ +} diff --git a/SearchEndpoint/AggregationsEndpoint.php b/SearchEndpoint/AggregationsEndpoint.php new file mode 100644 index 0000000000000000000000000000000000000000..c1c077fbe052b1f926aee766a94ac4016afdf69a --- /dev/null +++ b/SearchEndpoint/AggregationsEndpoint.php @@ -0,0 +1,65 @@ +<?php + +/* + * This file is part of the ONGR package. + * + * (c) NFQ Technologies UAB <info@nfq.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ElasticsearchBundle\DSL\SearchEndpoint; + +use ONGR\ElasticsearchBundle\DSL\BuilderInterface; +use ONGR\ElasticsearchBundle\DSL\NamedBuilderBag; +use ONGR\ElasticsearchBundle\DSL\NamedBuilderInterface; +use ONGR\ElasticsearchBundle\DSL\ParametersTrait; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +/** + * Search aggregations dsl endpoint. + */ +class AggregationsEndpoint implements SearchEndpointInterface +{ + /** + * @var NamedBuilderBag + */ + private $bag; + + /** + * Initialized aggregations bag. + */ + public function __construct() + { + $this->bag = new NamedBuilderBag(); + } + + /** + * {@inheritdoc} + */ + public function normalize(NormalizerInterface $normalizer, $format = null, array $context = []) + { + if (count($this->bag->all()) > 0) { + return $this->bag->toArray(); + } + } + + /** + * {@inheritdoc} + */ + public function addBuilder(BuilderInterface $builder, $parameters = []) + { + if ($builder instanceof NamedBuilderInterface) { + $this->bag->add($builder); + } + } + + /** + * {@inheritdoc} + */ + public function getBuilder() + { + return $this->bag->all(); + } +} diff --git a/SearchEndpoint/FilterEndpoint.php b/SearchEndpoint/FilterEndpoint.php new file mode 100644 index 0000000000000000000000000000000000000000..45e572b9efe92cdd7b74d4d0d916264d9cf66fd8 --- /dev/null +++ b/SearchEndpoint/FilterEndpoint.php @@ -0,0 +1,42 @@ +<?php + +/* + * This file is part of the ONGR package. + * + * (c) NFQ Technologies UAB <info@nfq.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ElasticsearchBundle\DSL\SearchEndpoint; + +use ONGR\ElasticsearchBundle\DSL\Query\FilteredQuery; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +/** + * Search filter dsl endpoint. + */ +class FilterEndpoint extends QueryEndpoint +{ + /** + * {@inheritdoc} + */ + public function normalize(NormalizerInterface $normalizer, $format = null, array $context = []) + { + if ($this->getBuilder()) { + $query = new FilteredQuery(); + $query->setBoolParameters($this->getParameters()); + $query->setFilter($this->getBuilder()); + $this->addReference('filtered_query', $query); + } + } + + /** + * {@inheritdoc} + */ + public function getOrder() + { + return 1; + } +} diff --git a/SearchEndpoint/HighlightEndpoint.php b/SearchEndpoint/HighlightEndpoint.php new file mode 100644 index 0000000000000000000000000000000000000000..5a2afbd0164cbf410aee611c5acecc8a37e397f9 --- /dev/null +++ b/SearchEndpoint/HighlightEndpoint.php @@ -0,0 +1,52 @@ +<?php + +/* + * This file is part of the ONGR package. + * + * (c) NFQ Technologies UAB <info@nfq.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ElasticsearchBundle\DSL\SearchEndpoint; + +use ONGR\ElasticsearchBundle\DSL\BuilderInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +/** + * Search highlight dsl endpoint. + */ +class HighlightEndpoint implements SearchEndpointInterface +{ + /** + * @var BuilderInterface + */ + private $highlight; + + /** + * {@inheritdoc} + */ + public function normalize(NormalizerInterface $normalizer, $format = null, array $context = []) + { + if ($this->getBuilder()) { + return $this->getBuilder()->toArray(); + } + } + + /** + * {@inheritdoc} + */ + public function addBuilder(BuilderInterface $builder, $parameters = []) + { + $this->highlight = $builder; + } + + /** + * {@inheritdoc} + */ + public function getBuilder() + { + return $this->highlight; + } +} diff --git a/SearchEndpoint/PostFilterEndpoint.php b/SearchEndpoint/PostFilterEndpoint.php new file mode 100644 index 0000000000000000000000000000000000000000..acd0967a68e32fdfd636e84e5b00ad40a73aa9af --- /dev/null +++ b/SearchEndpoint/PostFilterEndpoint.php @@ -0,0 +1,37 @@ +<?php + +/* + * This file is part of the ONGR package. + * + * (c) NFQ Technologies UAB <info@nfq.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ElasticsearchBundle\DSL\SearchEndpoint; + +use ONGR\ElasticsearchBundle\DSL\Filter\PostFilter; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +/** + * Search post filter dsl endpoint. + */ +class PostFilterEndpoint extends QueryEndpoint +{ + /** + * {@inheritdoc} + */ + public function normalize(NormalizerInterface $normalizer, $format = null, array $context = []) + { + if ($this->getBuilder()) { + $postFilter = new PostFilter(); + $postFilter->setBoolParameters($this->getParameters()); + $postFilter->setFilter($this->getBuilder()); + + return $postFilter->toArray(); + } + + return null; + } +} diff --git a/SearchEndpoint/QueryEndpoint.php b/SearchEndpoint/QueryEndpoint.php new file mode 100644 index 0000000000000000000000000000000000000000..ddf4ec414e4d8e50cad1c5a6e922fa3b704a23a8 --- /dev/null +++ b/SearchEndpoint/QueryEndpoint.php @@ -0,0 +1,143 @@ +<?php + +/* + * This file is part of the ONGR package. + * + * (c) NFQ Technologies UAB <info@nfq.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ElasticsearchBundle\DSL\SearchEndpoint; + +use ONGR\ElasticsearchBundle\DSL\Bool\Bool; +use ONGR\ElasticsearchBundle\DSL\BuilderInterface; +use ONGR\ElasticsearchBundle\DSL\ParametersTrait; +use ONGR\ElasticsearchBundle\DSL\Query\FilteredQuery; +use ONGR\ElasticsearchBundle\Serializer\Normalizer\OrderedNormalizerInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +/** + * Search query dsl endpoint. + */ +class QueryEndpoint extends AbstractSearchEndpoint implements OrderedNormalizerInterface +{ + use ParametersTrait; + + /** + * @var BuilderInterface|Bool + */ + private $query; + + /** + * @var OptionsResolver + */ + private $resolver; + + /** + * Instantiates resolver. + */ + public function __construct() + { + $this->resolver = new OptionsResolver(); + $this->configureResolver($this->resolver); + } + + /** + * {@inheritdoc} + */ + public function addBuilder(BuilderInterface $builder, $parameters = []) + { + if (!$this->query && !(array_key_exists('bool_type', $parameters) && !empty($parameters['bool_type']))) { + $this->setBuilder($builder); + } else { + $parameters = $this->resolver->resolve(array_filter($parameters)); + $this->query instanceof Bool ? : $this->convertToBool(); + $this->query->addToBool($builder, $parameters['bool_type']); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function normalize(NormalizerInterface $normalizer, $format = null, array $context = []) + { + $isRelevant = false; + + if ($this->hasReference('filtered_query')) { + /** @var FilteredQuery $query */ + $query = $this->getReference('filtered_query'); + $this->addBuilder($query); + $isRelevant = true; + } + + if ($this->getBuilder()) { + if (method_exists($this->getBuilder(), 'setParameters') && count($this->getParameters()) > 0) { + $this + ->getBuilder() + ->setParameters($this->processArray($this->getBuilder()->getParameters())); + } + + $isRelevant = true; + } + + return $isRelevant ? [$this->getBuilder()->getType() => $this->getBuilder()->toArray()] : null; + } + + /** + * {@inheritdoc} + */ + public function getOrder() + { + return 2; + } + + /** + * {@inheritdoc} + */ + public function getBuilder() + { + return $this->query; + } + + /** + * Sets builder. + * + * @param BuilderInterface $builder + */ + protected function setBuilder(BuilderInterface $builder) + { + $this->query = $builder; + } + + /** + * Default properties for query. + * + * @param OptionsResolver $resolver + */ + protected function configureResolver(OptionsResolver $resolver) + { + $resolver + ->setDefaults( + ['bool_type' => Bool::MUST] + ); + } + + /** + * Converts query to bool. + */ + private function convertToBool() + { + $bool = new Bool(); + + if ($this->query !== null) { + $bool->addToBool($this->query); + } + + $this->query = $bool; + } +} diff --git a/SearchEndpoint/SearchEndpointFactory.php b/SearchEndpoint/SearchEndpointFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..1340ab67d3424f91913c23ac914edee17f048dcc --- /dev/null +++ b/SearchEndpoint/SearchEndpointFactory.php @@ -0,0 +1,55 @@ +<?php + +/* + * This file is part of the ONGR package. + * + * (c) NFQ Technologies UAB <info@nfq.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ElasticsearchBundle\DSL\SearchEndpoint; + +/** + * Factory for search endpoints. + */ +class SearchEndpointFactory +{ + /** + * @var array Holds namespaces for endpoints. + */ + private static $endpoints = [ + 'query' => 'ONGR\ElasticsearchBundle\DSL\SearchEndpoint\QueryEndpoint', + 'filter' => 'ONGR\ElasticsearchBundle\DSL\SearchEndpoint\FilterEndpoint', + 'post_filter' => 'ONGR\ElasticsearchBundle\DSL\SearchEndpoint\PostFilterEndpoint', + 'sort' => 'ONGR\ElasticsearchBundle\DSL\SearchEndpoint\SortEndpoint', + 'highlight' => 'ONGR\ElasticsearchBundle\DSL\SearchEndpoint\HighlightEndpoint', + 'aggregations' => 'ONGR\ElasticsearchBundle\DSL\SearchEndpoint\AggregationsEndpoint', + 'suggest' => 'ONGR\ElasticsearchBundle\DSL\SearchEndpoint\SuggestEndpoint', + ]; + + /** + * Returns a search endpoint instance. + * + * @param string $type Type of endpoint. + * + * @return SearchEndpointInterface + * + * @throws \RuntimeException Endpoint does not exist. + * @throws \DomainException Endpoint is not implementing SearchEndpointInterface + */ + public static function get($type) + { + if (!array_key_exists($type, self::$endpoints)) { + throw new \RuntimeException(); + } + $endpoint = new self::$endpoints[$type](); + + if ($endpoint instanceof SearchEndpointInterface) { + return $endpoint; + } + + throw new \DomainException(); + } +} diff --git a/SearchEndpoint/SearchEndpointInterface.php b/SearchEndpoint/SearchEndpointInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..83139eb9dda78819dbfdfff81f2d1c03c36d422c --- /dev/null +++ b/SearchEndpoint/SearchEndpointInterface.php @@ -0,0 +1,39 @@ +<?php + +/* + * This file is part of the ONGR package. + * + * (c) NFQ Technologies UAB <info@nfq.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ElasticsearchBundle\DSL\SearchEndpoint; + +use ONGR\ElasticsearchBundle\DSL\BuilderInterface; +use ONGR\ElasticsearchBundle\DSL\ParametersTrait; +use Symfony\Component\Serializer\Normalizer\NormalizableInterface; + +/** + * Interface used to define search endpoint. + */ +interface SearchEndpointInterface extends NormalizableInterface +{ + /** + * Adds builder to search endpoint. + * + * @param BuilderInterface $builder Builder to add. + * @param array $parameters Additional parameters relevant to builder. + * + * @return SearchEndpointInterface + */ + public function addBuilder(BuilderInterface $builder, $parameters = []); + + /** + * Returns currectly contained builder. + * + * @return BuilderInterface|BuilderInterface[] + */ + public function getBuilder(); +} diff --git a/SearchEndpoint/SortEndpoint.php b/SearchEndpoint/SortEndpoint.php new file mode 100644 index 0000000000000000000000000000000000000000..a05f89778341040675c1b63a366dffced318d174 --- /dev/null +++ b/SearchEndpoint/SortEndpoint.php @@ -0,0 +1,61 @@ +<?php + +/* + * This file is part of the ONGR package. + * + * (c) NFQ Technologies UAB <info@nfq.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ElasticsearchBundle\DSL\SearchEndpoint; + +use ONGR\ElasticsearchBundle\DSL\BuilderInterface; +use ONGR\ElasticsearchBundle\DSL\Sort\Sorts; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +/** + * Search sort dsl endpoint. + */ +class SortEndpoint implements SearchEndpointInterface +{ + /** + * @var Sorts + */ + protected $sorts; + + /** + * Initializes Sorts object. + */ + public function __construct() + { + $this->sorts = new Sorts(); + } + + /** + * {@inheritdoc} + */ + public function normalize(NormalizerInterface $normalizer, $format = null, array $context = []) + { + if ($this->sorts->isRelevant()) { + return $this->sorts->toArray(); + } + } + + /** + * {@inheritdoc} + */ + public function addBuilder(BuilderInterface $builder, $parameters = []) + { + $this->sorts->addSort($builder); + } + + /** + * {@inheritdoc} + */ + public function getBuilder() + { + return $this->sorts; + } +} diff --git a/SearchEndpoint/SuggestEndpoint.php b/SearchEndpoint/SuggestEndpoint.php new file mode 100644 index 0000000000000000000000000000000000000000..c40b5f070b94376d49c07c9c841926afb215a905 --- /dev/null +++ b/SearchEndpoint/SuggestEndpoint.php @@ -0,0 +1,22 @@ +<?php + +/* + * This file is part of the ONGR package. + * + * (c) NFQ Technologies UAB <info@nfq.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ONGR\ElasticsearchBundle\DSL\SearchEndpoint; + +use ONGR\ElasticsearchBundle\DSL\BuilderInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +/** + * Search suggesters dsl endpoint. + */ +class SuggestEndpoint extends AggregationsEndpoint +{ +}