Skip to content
Snippets Groups Projects
Search.php 17.7 KiB
Newer Older
ONGR Team's avatar
ONGR Team committed
<?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\ElasticsearchDSL;
ONGR Team's avatar
ONGR Team committed

use ONGR\ElasticsearchDSL\Aggregation\AbstractAggregation;
use ONGR\ElasticsearchDSL\Highlight\Highlight;
use ONGR\ElasticsearchDSL\InnerHit\NestedInnerHit;
use ONGR\ElasticsearchDSL\Query\Compound\BoolQuery;
use ONGR\ElasticsearchDSL\SearchEndpoint\AbstractSearchEndpoint;
use ONGR\ElasticsearchDSL\SearchEndpoint\AggregationsEndpoint;
use ONGR\ElasticsearchDSL\SearchEndpoint\HighlightEndpoint;
use ONGR\ElasticsearchDSL\SearchEndpoint\InnerHitsEndpoint;
use ONGR\ElasticsearchDSL\SearchEndpoint\PostFilterEndpoint;
use ONGR\ElasticsearchDSL\SearchEndpoint\QueryEndpoint;
use ONGR\ElasticsearchDSL\SearchEndpoint\SearchEndpointFactory;
use ONGR\ElasticsearchDSL\SearchEndpoint\SearchEndpointInterface;
use ONGR\ElasticsearchDSL\SearchEndpoint\SortEndpoint;
Denis Urban's avatar
Denis Urban committed
use ONGR\ElasticsearchDSL\SearchEndpoint\SuggestEndpoint;
use ONGR\ElasticsearchDSL\Serializer\Normalizer\CustomReferencedNormalizer;
use ONGR\ElasticsearchDSL\Serializer\OrderedSerializer;
use Symfony\Component\Serializer\Normalizer\CustomNormalizer;
ONGR Team's avatar
ONGR Team committed
/**
 * Search object that can be executed by a manager.
 */
class Search
{
Denis Urban's avatar
Denis Urban committed
    private static ?OrderedSerializer $serializer = null;
    /**
     * If you don’t need to track the total number of hits at all you can improve
     * query times by setting this option to false. Defaults to true.
     */
Denis Urban's avatar
Denis Urban committed
    private ?bool $trackTotalHits = null;
ONGR Team's avatar
ONGR Team committed
    /**
     * To retrieve hits from a certain offset. Defaults to 0.
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    private ?int $from = null;
     * The number of hits to return. Defaults to 10. If you do not care about getting some
     * hits back but only about the number of matches and/or aggregations, setting the value
     * to 0 will help performance.
Denis Urban's avatar
Denis Urban committed
    private ?int $size = null;
ONGR Team's avatar
ONGR Team committed
    /**
     * Allows to control how the _source field is returned with every hit. By default
     * operations return the contents of the _source field unless you have used the
     * stored_fields parameter or if the _source field is disabled.
     *
     * @var bool
ONGR Team's avatar
ONGR Team committed
     */
    private $source;
    /**
     * Allows to selectively load specific stored fields for each document represented by a search hit.
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    private ?array $storedFields = null;
ONGR Team's avatar
ONGR Team committed
    /**
     * Allows to return a script evaluation (based on different fields) for each hit.
     * Script fields can work on fields that are not stored, and allow to return custom
     * values to be returned (the evaluated value of the script). Script fields can
     * also access the actual _source document indexed and extract specific elements
     * to be returned from it (can be an "object" type).
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    private ?array $scriptFields = null;
ONGR Team's avatar
ONGR Team committed
    /**
     * Allows to return the doc value representation of a field for each hit. Doc value
     * fields can work on fields that are not stored. Note that if the fields parameter
     * specifies fields without docvalues it will try to load the value from the fielddata
     * cache causing the terms for that field to be loaded to memory (cached), which will
     * result in more memory consumption.
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    private ?array $docValueFields = null;
     * Enables explanation for each hit on how its score was computed.
     *
     * @var bool
Denis Urban's avatar
Denis Urban committed
    private ?bool $explain = null;
ONGR Team's avatar
ONGR Team committed
    /**
     * Returns a version for each search hit.
     *
ONGR Team's avatar
ONGR Team committed
     * @var bool
     */
Denis Urban's avatar
Denis Urban committed
    private ?bool $version = null;
    /**
     * Allows to configure different boost level per index when searching across more
     * than one indices. This is very handy when hits coming from one index matter more
     * than hits coming from another index (think social graph where each user has an index).
     */
Denis Urban's avatar
Denis Urban committed
    private ?array $indicesBoost = null;
ONGR Team's avatar
ONGR Team committed
    /**
     * Exclude documents which have a _score less than the minimum specified in min_score.
     *
     * @var int
     */
Denis Urban's avatar
Denis Urban committed
    private ?int $minScore = null;
    /**
     * Pagination of results can be done by using the from and size but the cost becomes
     * prohibitive when the deep pagination is reached. The index.max_result_window which
     * defaults to 10,000 is a safeguard, search requests take heap memory and time
     * proportional to from + size. The Scroll api is recommended for efficient deep
     * scrolling but scroll contexts are costly and it is not recommended to use it for
     * real time user requests. The search_after parameter circumvents this problem by
     * providing a live cursor. The idea is to use the results from the previous page to
     * help the retrieval of the next page.
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    private ?array $searchAfter = null;
ONGR Team's avatar
ONGR Team committed
    /**
     * URI parameters alongside Request body search.
     *
     * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/search-uri-request.html
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    private ?array $uriParams = null;
Mantas Jonušas's avatar
Mantas Jonušas committed
    /**
     * While a search request returns a single “page” of results, the scroll API can be used to retrieve
     * large numbers of results (or even all results) from a single search request, in much the same way
     * as you would use a cursor on a traditional database. Scrolling is not intended for real time user
     * requests, but rather for processing large amounts of data, e.g. in order to reindex the contents
     * of one index into a new index with a different configuration.
Mantas Jonušas's avatar
Mantas Jonušas committed
     */
Denis Urban's avatar
Denis Urban committed
    private ?string $scroll = null;
    /**
     * @var SearchEndpointInterface[]
     */
Denis Urban's avatar
Denis Urban committed
    private ?array $endpoints = null;
     * Constructor to initialize static properties
     */
    public function __construct()
    {
        $this->initializeSerializer();
    }

    /**
     * Initializes the serializer
     */
Denis Urban's avatar
Denis Urban committed
    private function initializeSerializer(): void
Mantas Jonušas's avatar
Mantas Jonušas committed
    {
Denis Urban's avatar
Denis Urban committed
        if (!static::$serializer instanceof OrderedSerializer) {
            static::$serializer = new OrderedSerializer(
                [
                    new CustomReferencedNormalizer(),
                    new CustomNormalizer(),
                ]
            );
        }
Denis Urban's avatar
Denis Urban committed
    /**
     * Wakeup method to initialize static properties
     */
    public function __wakeup()
    {
        $this->initializeSerializer();
    }

ONGR Team's avatar
ONGR Team committed
    /**
     * Destroys search endpoint.
ONGR Team's avatar
ONGR Team committed
     *
     * @param string $type Endpoint type.
Denis Urban's avatar
Denis Urban committed
    public function destroyEndpoint($type): void
        unset($this->endpoints[$type]);
     * Adds query to the search.
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    public function addQuery(BuilderInterface $query, string $boolType = BoolQuery::MUST, mixed $key = null): static
ONGR Team's avatar
ONGR Team committed
    {
        $endpoint = $this->getEndpoint(QueryEndpoint::NAME);
        $endpoint->addToBool($query, $boolType, $key);
ONGR Team's avatar
ONGR Team committed

        return $this;
    }

    /**
     * Returns endpoint instance.
     *
     * @param string $type Endpoint type.
     *
     * @return SearchEndpointInterface
     */
    private function getEndpoint($type)
    {
Denis Urban's avatar
Denis Urban committed
        if (!array_key_exists($type, $this->endpoints ?? [])) {
            $this->endpoints[$type] = SearchEndpointFactory::get($type);
        }

        return $this->endpoints[$type];
    }

    /**
     * Returns queries inside BoolQuery instance.
     *
     */
    public function getQueries()
    {
        $endpoint = $this->getEndpoint(QueryEndpoint::NAME);

        return $endpoint->getBool();
    }

    /**
     * Sets query endpoint parameters.
     *
     *
     * @return $this
     */
Denis Urban's avatar
Denis Urban committed
    public function setQueryParameters(array $parameters): static
    {
        $this->setEndpointParameters(QueryEndpoint::NAME, $parameters);

        return $this;
    }

ONGR Team's avatar
ONGR Team committed
    /**
     * Sets parameters to the endpoint.
     * @param string $endpointName
Denis Urban's avatar
Denis Urban committed
    public function setEndpointParameters($endpointName, array $parameters): static
        /** @var AbstractSearchEndpoint $endpoint */
        $endpoint = $this->getEndpoint($endpointName);
        $endpoint->setParameters($parameters);
ONGR Team's avatar
ONGR Team committed
    /**
     * Adds a post filter to search.
     *
Denis Urban's avatar
Denis Urban committed
     * @param BuilderInterface $filter Filter.
     * @param string $boolType Example boolType values:
     *                                   - must
     *                                   - must_not
     *                                   - should.
Denis Urban's avatar
Denis Urban committed
     * @param string $key
ONGR Team's avatar
ONGR Team committed
     *
     * @return $this.
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    public function addPostFilter(BuilderInterface $filter, $boolType = BoolQuery::MUST, $key = null): static
ONGR Team's avatar
ONGR Team committed
    {
        $this
            ->getEndpoint(PostFilterEndpoint::NAME)
            ->addToBool($filter, $boolType, $key);
ONGR Team's avatar
ONGR Team committed

ONGR Team's avatar
ONGR Team committed
    }

    /**
     * Returns queries inside BoolFilter instance.
     *
     */
    public function getPostFilters()
    {
        $endpoint = $this->getEndpoint(PostFilterEndpoint::NAME);

        return $endpoint->getBool();
    }

    /**
     * Sets post filter endpoint parameters.
     *
     *
     * @return $this
     */
Denis Urban's avatar
Denis Urban committed
    public function setPostFilterParameters(array $parameters): static
    {
        $this->setEndpointParameters(PostFilterEndpoint::NAME, $parameters);

        return $this;
    }

    /**
     * Adds aggregation into search.
     *
     *
Mantas Varatiejus's avatar
Mantas Varatiejus committed
     * @return $this
Denis Urban's avatar
Denis Urban committed
    public function addAggregation(AbstractAggregation $aggregation): static
    {
        $this->getEndpoint(AggregationsEndpoint::NAME)->add($aggregation, $aggregation->getName());

        return $this;
    }

    /**
     * Returns all aggregations.
     *
     * @return BuilderInterface[]
     */
    public function getAggregations()
    {
        return $this->getEndpoint(AggregationsEndpoint::NAME)->getAll();
    }

    /**
     * Adds inner hit into search.
     *
     *
     * @return $this
     */
Denis Urban's avatar
Denis Urban committed
    public function addInnerHit(NestedInnerHit $innerHit): static
    {
        $this->getEndpoint(InnerHitsEndpoint::NAME)->add($innerHit, $innerHit->getName());

        return $this;
    }

    /**
     * Returns all inner hits.
     *
     * @return BuilderInterface[]
     */
    public function getInnerHits()
    {
        return $this->getEndpoint(InnerHitsEndpoint::NAME)->getAll();
    }

ONGR Team's avatar
ONGR Team committed
    /**
     * Adds sort to search.
ONGR Team's avatar
ONGR Team committed
     *
     *
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    public function addSort(BuilderInterface $sort): static
ONGR Team's avatar
ONGR Team committed
    {
        $this->getEndpoint(SortEndpoint::NAME)->add($sort);
ONGR Team's avatar
ONGR Team committed

        return $this;
    }

    /**
     * Returns all set sorts.
     *
     * @return BuilderInterface[]
     */
    public function getSorts()
    {
        return $this->getEndpoint(SortEndpoint::NAME)->getAll();
    }

ONGR Team's avatar
ONGR Team committed
    /**
     * Allows to highlight search results on one or more fields.
ONGR Team's avatar
ONGR Team committed
     *
     * @param Highlight $highlight
     *
     * @return $this.
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    public function addHighlight(BuilderInterface $highlight): static
ONGR Team's avatar
ONGR Team committed
    {
        $this->getEndpoint(HighlightEndpoint::NAME)->add($highlight);

        return $this;
ONGR Team's avatar
ONGR Team committed
    }

    /**
     * Returns highlight builder.
     *
     * @return BuilderInterface
     */
    public function getHighlights()
    {
        /** @var HighlightEndpoint $highlightEndpoint */
        $highlightEndpoint = $this->getEndpoint(HighlightEndpoint::NAME);

        return $highlightEndpoint->getHighlight();
    }

Denis Urban's avatar
Denis Urban committed
     * Adds suggest into search.
     *
     * @param BuilderInterface $suggest
     *
     * @return $this
     */
Denis Urban's avatar
Denis Urban committed
    public function addSuggest(NamedBuilderInterface $suggest): static
    {
        $this->getEndpoint(SuggestEndpoint::NAME)->add($suggest, $suggest->getName());

        return $this;
    }

    /**
Denis Urban's avatar
Denis Urban committed
     * Returns all suggests.
     *
     * @return BuilderInterface[]
     */
    public function getSuggests()
    {
        return $this->getEndpoint(SuggestEndpoint::NAME)->getAll();
    }

ONGR Team's avatar
ONGR Team committed
    /**
     * @return null|int
Denis Urban's avatar
Denis Urban committed
    public function getFrom(): ?int
        return $this->from;
     * @param null|int $from
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    public function setFrom($from): static
ONGR Team's avatar
ONGR Team committed
    {
ONGR Team's avatar
ONGR Team committed
        return $this;
    }

Denis Urban's avatar
Denis Urban committed
    public function isTrackTotalHits(): ?bool
    {
        return $this->trackTotalHits;
    }

    /**
     * @return $this
     */
    public function setTrackTotalHits(bool $trackTotalHits)
    {
        $this->trackTotalHits = $trackTotalHits;
Denis Urban's avatar
Denis Urban committed
    public function getSize(): ?int
        return $this->size;
Denis Urban's avatar
Denis Urban committed
    public function setSize(?int $size): static
ONGR Team's avatar
ONGR Team committed
    {
ONGR Team's avatar
ONGR Team committed
        return $this;
    }

    /**
     * @return bool
ONGR Team's avatar
ONGR Team committed
     */
    public function isSource()
ONGR Team's avatar
ONGR Team committed
    {
        return $this->source;
ONGR Team's avatar
ONGR Team committed
    }

    /**
     * @param bool $source
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    public function setSource($source): static
ONGR Team's avatar
ONGR Team committed
    {
ONGR Team's avatar
ONGR Team committed
        return $this;
    }

    /**
     * @return array
ONGR Team's avatar
ONGR Team committed
     */
    public function getStoredFields()
ONGR Team's avatar
ONGR Team committed
    {
        return $this->storedFields;
ONGR Team's avatar
ONGR Team committed
    }

    /**
     * @param array $storedFields
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    public function setStoredFields($storedFields): static
ONGR Team's avatar
ONGR Team committed
    {
        $this->storedFields = $storedFields;
ONGR Team's avatar
ONGR Team committed
        return $this;
    }

    /**
     * @return array
     */
    public function getScriptFields()
ONGR Team's avatar
ONGR Team committed
    {
        return $this->scriptFields;
ONGR Team's avatar
ONGR Team committed

    /**
     * @param array $scriptFields
Denis Urban's avatar
Denis Urban committed
    public function setScriptFields($scriptFields): static
    {
        $this->scriptFields = $scriptFields;
ONGR Team's avatar
ONGR Team committed

    public function getDocValueFields()
        return $this->docValueFields;
ONGR Team's avatar
ONGR Team committed
    }

    /**
     * @param array $docValueFields
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    public function setDocValueFields($docValueFields): static
ONGR Team's avatar
ONGR Team committed
    {
        $this->docValueFields = $docValueFields;
ONGR Team's avatar
ONGR Team committed
    }

    /**
     * @return bool
     */
    public function isExplain()
    {
        return $this->explain;
    }

    /**
     * @param bool $explain
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    public function setExplain($explain): static
ONGR Team's avatar
ONGR Team committed
    {
        $this->explain = $explain;
    /**
     * @return bool
     */
    public function isVersion()
    {
        return $this->version;
    }

    /**
     * @param bool $version
Denis Urban's avatar
Denis Urban committed
    public function setVersion($version): static
    {
        $this->version = $version;
ONGR Team's avatar
ONGR Team committed
    }

    /**
ONGR Team's avatar
ONGR Team committed
     */
    public function getIndicesBoost()
ONGR Team's avatar
ONGR Team committed
    {
        return $this->indicesBoost;
ONGR Team's avatar
ONGR Team committed
    }

    /**
     * @param array $indicesBoost
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    public function setIndicesBoost($indicesBoost): static
ONGR Team's avatar
ONGR Team committed
    {
        $this->indicesBoost = $indicesBoost;
ONGR Team's avatar
ONGR Team committed
    }

    /**
ONGR Team's avatar
ONGR Team committed
     */
    public function getMinScore()
ONGR Team's avatar
ONGR Team committed
    {
        return $this->minScore;
ONGR Team's avatar
ONGR Team committed
    }

    /**
     * @param int $minScore
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    public function setMinScore($minScore): static
ONGR Team's avatar
ONGR Team committed
    {
        $this->minScore = $minScore;
ONGR Team's avatar
ONGR Team committed
    }

    /**
     * @return array
ONGR Team's avatar
ONGR Team committed
     */
    public function getSearchAfter()
ONGR Team's avatar
ONGR Team committed
    {
        return $this->searchAfter;
ONGR Team's avatar
ONGR Team committed
    }

     * @param array $searchAfter
Denis Urban's avatar
Denis Urban committed
    public function setSearchAfter($searchAfter): static
        $this->searchAfter = $searchAfter;
    public function getScroll()
        return $this->scroll;
ONGR Team's avatar
ONGR Team committed
    /**
     * @param string $scroll
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    public function setScroll(string $scroll = '5m'): static
ONGR Team's avatar
ONGR Team committed
    {
        $this->scroll = $scroll;

        $this->addUriParam('scroll', $this->scroll);

ONGR Team's avatar
ONGR Team committed
    }

    /**
     * @param string $name
     * @param string|array|bool $value
     * @return $this
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    public function addUriParam($name, $value): static
    {
        if (in_array($name, [
            'q',
            'df',
            'analyzer',
            'analyze_wildcard',
            'default_operator',
            'lenient',
            'explain',
            '_source',
            '_source_exclude',
            '_source_include',
            'stored_fields',
            'sort',
            'track_scores',
            'timeout',
            'terminate_after',
            'from',
            'size',
            'search_type',
            'allow_no_indices',
            'ignore_unavailable',
            'typed_keys',
            'pre_filter_shard_size',
            'ignore_unavailable',
        ])) {
            $this->uriParams[$name] = $value;
        } else {
            throw new \InvalidArgumentException(sprintf('Parameter %s is not supported.', $value));
        }

        return $this;
ONGR Team's avatar
ONGR Team committed
    }

    /**
     * Returns query url parameters.
ONGR Team's avatar
ONGR Team committed
     */
Denis Urban's avatar
Denis Urban committed
    public function getUriParams(): array
ONGR Team's avatar
ONGR Team committed
    {
        return $this->uriParams;
ONGR Team's avatar
ONGR Team committed
    }

    /**
     * {@inheritdoc}
     */
    public function toArray(): array|\stdClass
ONGR Team's avatar
ONGR Team committed
    {
        $output = array_filter(static::$serializer->normalize($this->endpoints));
ONGR Team's avatar
ONGR Team committed

        $params = [
            'from' => 'from',
            'size' => 'size',
            'source' => '_source',
            'storedFields' => 'stored_fields',
ONGR Team's avatar
ONGR Team committed
            'scriptFields' => 'script_fields',
            'docValueFields' => 'docvalue_fields',
ONGR Team's avatar
ONGR Team committed
            'explain' => 'explain',
            'version' => 'version',
            'indicesBoost' => 'indices_boost',
Mantas Jonušas's avatar
Mantas Jonušas committed
            'minScore' => 'min_score',
            'searchAfter' => 'search_after',
            'trackTotalHits' => 'track_total_hits',
ONGR Team's avatar
ONGR Team committed
        ];

        foreach ($params as $field => $param) {
            if ($this->$field !== null) {
                $output[$param] = $this->$field;
            }
        }

ONGR Team's avatar
ONGR Team committed
}