Skip to content
Snippets Groups Projects
SearchService.php 6.74 KiB
Newer Older
Pavel's avatar
Pavel committed
<?php

namespace IQDEV\ElasticSearch\Domain;

use IQDEV\ElasticSearch\Search\Aggs\Aggs;
use IQDEV\ElasticSearch\Search\Aggs\AggsKeyWordFacet;
use IQDEV\ElasticSearch\Search\Aggs\AggsNumberFacet;
use IQDEV\ElasticSearch\Search\BoolQuery\FilterKeywordFacet;
use IQDEV\ElasticSearch\Search\BoolQuery\FilterNumberFacet;
use IQDEV\ElasticSearch\Search\BoolQuery\Query;
use IQDEV\ElasticSearch\Search\BoolQuery\Terms;
use IQDEV\ElasticSearch\Search\Nested;
use IQDEV\ElasticSearch\Search\Pagination;
use IQDEV\ElasticSearch\Search\Request;
use Elastic\Elasticsearch\Client;
use IQDEV\Search\{Filter\Filter,
Pavel's avatar
Pavel committed
    Filter\FilterKeyword,
    Filter\FilterCategory,
    Filter\FilterNumber,
    Query as DQuery,
    Result,
    SearchService as DomainSearchService,
    Sorting\SortingFieldPair as DSortingFieldPair,
    Sorting\SortingPropertyKeywordPair as DSortingPropertyKeywordPair,
    Sorting\SortingPropertyNumberPair as DSortingPropertyNumberPair
};
use IQDEV\ElasticSearch\Search\Sorting\SortingCollection;
use IQDEV\ElasticSearch\Search\Sorting\SortingFieldsPair;
use IQDEV\ElasticSearch\Search\Sorting\SortingPropertyKeywordPair;
use IQDEV\ElasticSearch\Search\Sorting\SortingPropertyNumberPair;

final class SearchService implements DomainSearchService
{
    private Client $esClient;
    private string $sIndex;

    public function __construct(Client $esClient, string $sIndex = 'product-test')
    {
        $this->esClient = $esClient;
        $this->sIndex = $sIndex;
    }

    public function search(DQuery $q): Result
    {
        $request       = new Request();
        $commonQuery   = new Query();

        if ($q->filters) {
            foreach ($q->filters as $filter) {
                /** @var Filter $filter */
                if ($filter instanceof FilterCategory) {
                    $request->getQuery()->must(
                        (new Terms('category_id', $filter->value))
                    );
                    continue;
                }
            }
            $commonQuery = $this->getQuery($q->filters);
Pavel's avatar
Pavel committed
        }
        $commonFilter = clone $commonQuery;
        $commonFilter->setType(Query::TYPE_FILTER);

        if ($q->pagination) {
            $request->setPagination(
                new Pagination($q->pagination->limit, $q->pagination->page)
            );
        }
Pavel's avatar
Pavel committed
        if ($q->sorting && !$q->sorting->isEmpty()) {
            $oSortingCollection = new SortingCollection();
            foreach ($q->sorting as $sorting) {
                if ($sorting instanceof DSortingFieldPair) {
                    $oSortingCollection->add(new SortingFieldsPair($sorting->by, $sorting->direction));
                }

                if ($sorting instanceof DSortingPropertyKeywordPair) {
                    $oSortingCollection->add(new SortingPropertyKeywordPair($sorting->by, $sorting->direction));
                }

                if ($sorting instanceof DSortingPropertyNumberPair) {
                    $oSortingCollection->add(new SortingPropertyNumberPair($sorting->by, $sorting->direction));
                }
            }
            $request->setSorting($oSortingCollection);
        }

        if ($q->query) {
            $request->addMatch('full_search_content', ['query' => $q->query]);
        }

        if ($commonQuery->isEmpty() === false) {
            $nestedFilter = new Nested();
            $nestedFilter->setPath('search_data')
                ->setQuery($commonQuery);
Pavel's avatar
Pavel committed
            $request->getPostFilter()->filter($nestedFilter);

            $aggsKeywordFiltered = new Aggs('keyword_facet_filtered');
            $aggsKeywordFiltered->addAggs(AggsKeyWordFacet::create('all_keyword_facet_filtered', 'keyword_facet'))
                ->setQuery($commonFilter);
            $request->getAggs()->add($aggsKeywordFiltered);
Pavel's avatar
Pavel committed
            $aggsNumberFiltered = new Aggs('number_facet_filtered');
            $aggsNumberFiltered->addAggs(AggsNumberFacet::create('all_number_facet_filtered', 'number_facet'))
                ->setQuery($commonFilter);
            $request->getAggs()->add($aggsNumberFiltered);

            foreach ($q->filters as $filter) {
                if ($filter instanceof FilterKeyword) {
                    $oFilters = new FilterCollection();
                    foreach ($q->filters as $filter2) {
                        if (!($filter2 instanceof Filter) || $filter2 instanceof FilterCategory) {
                            continue;
                        }

                        if ($filter->key() === $filter2->key()) {
                            continue;
                        }
                        $oFilters->add($filter2);
                    }
                    if ($oFilters->isEmpty()) {
                        $aggsKeywordFiltered = AggsKeyWordFacet::create('all_keyword_facet_filtered_' . $filter->key(
                                                                        ),
                                                                        'keyword_facet'
                        );
                    } else {
                        $aggsKeywordFiltered = new Aggs('keyword_facet_filtered_' . $filter->key());
                        $aggsKeywordFiltered->addAggs(AggsKeyWordFacet::create('all_keyword_facet_filtered_' . $filter->key(
                                                                               ),
                                                                               'keyword_facet'
                        )
                        );
                        $aggsKeywordFiltered->setQuery($this->getQuery($oFilters));
                    }
                    $request->getAggs()->add($aggsKeywordFiltered);
                }
            }
Pavel's avatar
Pavel committed
        }

        $aggsKeyword = AggsKeyWordFacet::create('keyword_facet', 'keyword_facet');
        $request->getAggs()->add($aggsKeyword);
        $aggsNumber = AggsNumberFacet::create('number_facet', 'number_facet');
        $request->getAggs()->add($aggsNumber);

        $response = $this->esClient->search(
            [
                'index' => $this->sIndex,
                'body' => $request->es(),
            ]
        );


        return SearchResultFactory::createFromResponse($response, $request);
    }

    private function getQuery(FilterCollection $filters): Query
    {
        $commonQuery = new Query();
        foreach ($filters as $filter) {
            if ($filter instanceof FilterNumber) {
                $oFacet = new FilterNumberFacet($filter->key, $filter->min, $filter->max);
                $commonQuery->filter($oFacet);
                continue;
            }

            if ($filter instanceof FilterKeyword) {
                $oFacet = new FilterKeywordFacet($filter->key, $filter->value);
                $commonQuery->filter($oFacet);
                continue;
            }
        }

        return $commonQuery;
    }
Pavel's avatar
Pavel committed
}