From 5f2e0f9bc14b47aa820168a65844d0e0219664b5 Mon Sep 17 00:00:00 2001 From: Pavel Piligrimov <p.piligrimov@iqdev.digital> Date: Sun, 18 Dec 2022 23:17:21 +0500 Subject: [PATCH] =?UTF-8?q?=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D0=BD=D0=BE=D0=B9=20=D1=84=D0=B0=D1=81=D0=B5=D1=82?= =?UTF-8?q?=D0=BD=D0=BE=D0=B9=20=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B8,=20=D0=B1=D0=B0=D0=B7=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D0=B9=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=B8=D0=BD=D0=B4=D0=B5=D0=BA=D1=81=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Document/ProductDocument.php | 5 +- .../Domain/SearchResultFactory.php | 26 +++-- src/ElasticSearch/Domain/SearchService.php | 98 ++++++++++++------- .../Indexer/BaseIndexProvider.php | 44 +++++++++ .../Search/Aggs/AggsNumberFacet.php | 10 +- .../Search/Aggs/ExtremumTerms.php | 15 +-- src/ElasticSearch/Search/Request.php | 2 +- 7 files changed, 139 insertions(+), 61 deletions(-) create mode 100644 src/ElasticSearch/Indexer/BaseIndexProvider.php diff --git a/src/ElasticSearch/Document/ProductDocument.php b/src/ElasticSearch/Document/ProductDocument.php index 6ae448a..d6071e8 100644 --- a/src/ElasticSearch/Document/ProductDocument.php +++ b/src/ElasticSearch/Document/ProductDocument.php @@ -10,14 +10,16 @@ class ProductDocument implements Document private FacetCollection $keywordFacets; private FacetCollection $numberFacets; private ?string $fullSearchContent = null; + private array $info; private FacetCategory $categoryFacet; - public function __construct(FacetCategory $categoryFacet) + public function __construct(FacetCategory $categoryFacet, array $info = []) { $this->keywordFacets = new FacetCollection(); $this->numberFacets = new FacetCollection(); $this->categoryFacet = $categoryFacet; + $this->info = $info; } /** @@ -60,6 +62,7 @@ class ProductDocument implements Document 'keyword_facet' => $this->getKeywordFacets()->es(), 'number_facet' => $this->getNumberFacets()->es() ], + 'data' => $this->info ]; if ($this->fullSearchContent) { diff --git a/src/ElasticSearch/Domain/SearchResultFactory.php b/src/ElasticSearch/Domain/SearchResultFactory.php index a9a132a..363abe4 100644 --- a/src/ElasticSearch/Domain/SearchResultFactory.php +++ b/src/ElasticSearch/Domain/SearchResultFactory.php @@ -49,6 +49,20 @@ final class SearchResultFactory } } + foreach ($buckets as $bucket) { + $code = $bucket['key']; + if (isset($data['aggregations']["keyword_facet_$code"]['agg_special']['agg_keyword_facet_code']['buckets'])) { + $bucketsFiltered[$code] = []; + foreach ($data['aggregations']["keyword_facet_$code"]['agg_special']['agg_keyword_facet_code']['buckets'] as $filtredBucket) { + foreach ($filtredBucket['agg_keyword_facet_value']['buckets'] as $values) { + if ($filtredBucket['key'] === $code) { + $bucketsFiltered[$code][$values['key']] = $values; + } + } + } + } + } + foreach ($buckets as $bucket) { $code = $bucket['key']; $valueBucket = $bucket['agg_keyword_facet_value']['buckets']; @@ -74,8 +88,8 @@ final class SearchResultFactory foreach ($data['aggregations']['number_facet_filtered']['all_number_facet_filtered']['agg_number_facet_code']['buckets'] as $bucket) { $bucketsFiltered[$bucket['key']] = [ 'data' => $bucket, - 'min' => $bucket['agg_number_facet_min_value'], - 'max' => $bucket['agg_number_facet_max_value'] + 'min' => $bucket['agg_number_facet_value']['min'], + 'max' => $bucket['agg_number_facet_value']['max'] ]; } } @@ -88,13 +102,13 @@ final class SearchResultFactory $selectedMin = $selectedMax = null; if (isset($bucketsFiltered[$code])) { $count = $bucketsFiltered[$code]['data']['doc_count']; - $selectedMin = $bucketsFiltered[$code]['min']['value']; - $selectedMax = $bucketsFiltered[$code]['max']['value']; + $selectedMin = $bucketsFiltered[$code]['min']; + $selectedMax = $bucketsFiltered[$code]['max']; } $facet->items->add(FacetItemRange::createFromRange( - $bucket['agg_number_facet_min_value']['value'], - $bucket['agg_number_facet_max_value']['value'], + $bucket['agg_number_facet_value']['min'], + $bucket['agg_number_facet_value']['max'], $count, $selectedMin, $selectedMax diff --git a/src/ElasticSearch/Domain/SearchService.php b/src/ElasticSearch/Domain/SearchService.php index 277c171..ffa88f7 100644 --- a/src/ElasticSearch/Domain/SearchService.php +++ b/src/ElasticSearch/Domain/SearchService.php @@ -14,6 +14,7 @@ use IQDEV\ElasticSearch\Search\Pagination; use IQDEV\ElasticSearch\Search\Request; use Elastic\Elasticsearch\Client; use IQDEV\Search\{Filter\Filter, + Filter\FilterCollection, Filter\FilterKeyword, Filter\FilterCategory, Filter\FilterNumber, @@ -22,8 +23,7 @@ use IQDEV\Search\{Filter\Filter, SearchService as DomainSearchService, Sorting\SortingFieldPair as DSortingFieldPair, Sorting\SortingPropertyKeywordPair as DSortingPropertyKeywordPair, - Sorting\SortingPropertyNumberPair as DSortingPropertyNumberPair -}; + Sorting\SortingPropertyNumberPair as DSortingPropertyNumberPair}; use IQDEV\ElasticSearch\Search\Sorting\SortingCollection; use IQDEV\ElasticSearch\Search\Sorting\SortingFieldsPair; use IQDEV\ElasticSearch\Search\Sorting\SortingPropertyKeywordPair; @@ -44,8 +44,6 @@ final class SearchService implements DomainSearchService { $request = new Request(); $commonQuery = new Query(); - $filterKeyword = new Query(); - $filterNumber = new Query(); if ($q->filters) { foreach ($q->filters as $filter) { @@ -54,23 +52,10 @@ final class SearchService implements DomainSearchService $request->getQuery()->must( (new Terms('category_id', $filter->value)) ); - continue; - } - - if ($filter instanceof FilterNumber) { - $oFacet = new FilterNumberFacet($filter->key, $filter->min, $filter->max); - $filterNumber->filter($oFacet); - $commonQuery->filter($oFacet); - continue; - } - - if ($filter instanceof FilterKeyword) { - $oFacet = new FilterKeywordFacet($filter->key, $filter->value); - $filterKeyword->filter($oFacet); - $commonQuery->filter($oFacet); - continue; + break; } } + $commonQuery = $this->getQuery($q->filters); } $commonFilter = clone $commonQuery; $commonFilter->setType(Query::TYPE_FILTER); @@ -80,7 +65,7 @@ final class SearchService implements DomainSearchService new Pagination($q->pagination->limit, $q->pagination->page) ); } - + if ($q->sorting && !$q->sorting->isEmpty()) { $oSortingCollection = new SortingCollection(); foreach ($q->sorting as $sorting) { @@ -103,30 +88,57 @@ final class SearchService implements DomainSearchService $request->addMatch('full_search_content', ['query' => $q->query]); } - if ($filterKeyword->isEmpty() === false) { - $nestedFilterKeyword = new Nested(); - $nestedFilterKeyword->setPath('search_data') - ->setQuery($filterKeyword); - } + $getKey = static fn (string $type, string $name): string => sprintf('%s_facet_%s', $type, $name); + if ($commonQuery->isEmpty() === false) { + foreach ($q->filters as $filter) { + /** @var Filter $filter */ + if ($filter instanceof FilterCategory || $filter instanceof FilterNumber) { + continue; + } - if ($filterNumber->isEmpty() === false) { - $nestedFilterNumber = new Nested(); - $nestedFilterNumber->setPath('search_data') - ->setQuery($filterNumber); - } + $oFilters = new FilterCollection(); + + if ($filter instanceof FilterKeyword) { + foreach ($q->filters as $filter2) { + if (!($filter2 instanceof Filter) || $filter2 instanceof FilterCategory) { + continue; + } + + if ($filter->key() === $filter2->key()) { + continue; + } + $oFilters->add($filter2); + } + } + + $aggsFiltered = new Aggs($getKey('keyword', $filter->key())); + $aggsFiltered->addAggs( + AggsKeyWordFacet::create( + 'agg_special', + 'keyword_facet' + ) + ); + + if (false === $oFilters->isEmpty()) { + $aggsFiltered->setQuery($this->getQuery($oFilters)); + } else { + $aggsFiltered->setNested((new Nested())->setPath('search_data')); + } + + $request->getAggs()->add($aggsFiltered); + } - if ($commonQuery->isEmpty() === false) { $nestedFilter = new Nested(); $nestedFilter->setPath('search_data') ->setQuery($commonQuery); - + $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); - + $aggsNumberFiltered = new Aggs('number_facet_filtered'); $aggsNumberFiltered->addAggs(AggsNumberFacet::create('all_number_facet_filtered', 'number_facet')) ->setQuery($commonFilter); @@ -148,4 +160,24 @@ final class SearchService implements DomainSearchService 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; + } } diff --git a/src/ElasticSearch/Indexer/BaseIndexProvider.php b/src/ElasticSearch/Indexer/BaseIndexProvider.php new file mode 100644 index 0000000..d8bafd1 --- /dev/null +++ b/src/ElasticSearch/Indexer/BaseIndexProvider.php @@ -0,0 +1,44 @@ +<?php + +namespace IQDEV\ElasticSearch\Indexer; + +use IQDEV\ElasticSearch\Configuration; +use IQDEV\ElasticSearch\Document\ProductDocument; +use IQDEV\ElasticSearch\Facet\FacetCategory; +use IQDEV\ElasticSearch\Facet\FacetKeyword; +use IQDEV\ElasticSearch\Facet\FacetNumber; + +class BaseIndexProvider implements IndexProvider +{ + private array $products; + private Configuration $configuration; + + public function __construct($products, $configuration) + { + $this->configuration = $configuration; + $this->products = $products; + } + + public function get(): \Generator + { + foreach ($this->products as $product) { + $document = new ProductDocument(new FacetCategory($product['category']), $product['data'] ?? []); + foreach ($product['properties'] as $type => $values) { + foreach ($values as $key => $prop) { + if ($type === 'number') { + $document->getNumberFacets()->add(new FacetNumber($key, $prop)); + } else { + $document->getKeywordFacets()->add(new FacetKeyword($key, $prop)); + } + } + } + $document->setFullSearchContent($product['name']); + + yield new Index( + $this->configuration->getIndexName(), + $document, + $product['id'] + ); + } + } +} \ No newline at end of file diff --git a/src/ElasticSearch/Search/Aggs/AggsNumberFacet.php b/src/ElasticSearch/Search/Aggs/AggsNumberFacet.php index 2c67ab8..16d0b01 100644 --- a/src/ElasticSearch/Search/Aggs/AggsNumberFacet.php +++ b/src/ElasticSearch/Search/Aggs/AggsNumberFacet.php @@ -18,17 +18,9 @@ final class AggsNumberFacet (new Terms("{$path}.{$facet}.facet_code")) ); - $aggFacetValue = new Aggs("agg_{$facet}_min_value"); + $aggFacetValue = new Aggs("agg_{$facet}_value"); $aggFacetValue->setExtremumTerms( (new ExtremumTerms("{$path}.{$facet}.facet_value")) - ->setOperation('min') - ); - $aggFacetCode->addAggs($aggFacetValue); - - $aggFacetValue = new Aggs("agg_{$facet}_max_value"); - $aggFacetValue->setExtremumTerms( - (new ExtremumTerms("{$path}.{$facet}.facet_value")) - ->setOperation('max') ); $aggFacetCode->addAggs($aggFacetValue); diff --git a/src/ElasticSearch/Search/Aggs/ExtremumTerms.php b/src/ElasticSearch/Search/Aggs/ExtremumTerms.php index 629ef18..9de2a5c 100644 --- a/src/ElasticSearch/Search/Aggs/ExtremumTerms.php +++ b/src/ElasticSearch/Search/Aggs/ExtremumTerms.php @@ -7,7 +7,6 @@ use IQDEV\ElasticSearch\Esable; final class ExtremumTerms implements Esable { - private string $sOperation; private string $field; /** @@ -18,18 +17,12 @@ final class ExtremumTerms implements Esable $this->field = $field; } - public function setOperation($sOperation): self - { - $this->sOperation = $sOperation; - return $this; - } - public function es(): array { - $data = [ - 'field' => $this->field + return [ + 'stats' => [ + 'field' => $this->field, + ], ]; - - return [$this->sOperation => $data]; } } \ No newline at end of file diff --git a/src/ElasticSearch/Search/Request.php b/src/ElasticSearch/Search/Request.php index 19d8e84..ab423a9 100644 --- a/src/ElasticSearch/Search/Request.php +++ b/src/ElasticSearch/Search/Request.php @@ -94,7 +94,7 @@ final class Request implements Esable public function es(): array { $request = [ - '_source' => ['id'] + '_source' => ['id', 'data.*'] ]; if (isset($this->postFilter) && $this->postFilter->isEmpty() === false) { -- GitLab