<?php namespace IQDEV\ElasticSearch\Converter; use IQDEV\ElasticSearch\Order\OrderAscType; use IQDEV\ElasticSearch\Order\OrderDescType; use IQDEV\ElasticSearch\Order\OrderField; use IQDEV\ElasticSearch\Order\OrderKeywordProperty; use IQDEV\ElasticSearch\Order\OrderNumberProperty; use IQDEV\ElasticSearch\Search\Aggs\Aggs; use IQDEV\ElasticSearch\Search\Aggs\AggsFacetStats; use IQDEV\ElasticSearch\Search\Aggs\AggsFacetTerms; 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 IQDEV\Search\Criteria; use IQDEV\Search\Document\Property\AttrType; use IQDEV\Search\Document\Property\PropertyType; use IQDEV\Search\FIlter\Filter; use IQDEV\Search\Filter\FilterOperator; use IQDEV\Search\Order\Order; use IQDEV\Search\Order\OrderAscType as SOrderAscType; use IQDEV\Search\Order\OrderDescType as SOrderDescType; final class CriteriaToEsRequest { public function fromCriteria(Criteria $criteria): Request { $request = new Request(); $request = $this->pagination($request, $criteria); $request = $this->order($request, $criteria); $request = $this->filter($request, $criteria); $request = $this->aggs($request, $criteria); return $request; } private function pagination(Request $request, Criteria $criteria): Request { $request = clone $request; $request->setPagination(new Pagination($criteria->pagination()->limit, $criteria->pagination()->offset)); return $request; } private function order(Request $request, Criteria $criteria): Request { $request = clone $request; if (true === $criteria->sorting()->isEmpty()) { return $request; } foreach ($criteria->sorting() as $order) { /** @var Order $order */ $direction = null; if ($order->orderType() instanceof SOrderAscType) { $direction = new OrderAscType(); } if ($order->orderType() instanceof SOrderDescType) { $direction = new OrderDescType(); } if ($order->orderBy() instanceof AttrType) { $request->getSort()->add(new OrderField($order->orderBy()->key(), $direction)); } elseif ($order->orderBy() instanceof PropertyType) { if ($order->orderBy()->type() === PropertyType::TYPE_KEYWORD) { $request->getSort()->add(new OrderKeywordProperty($order->orderBy()->key(), $direction)); } elseif ($order->orderBy()->type() === PropertyType::TYPE_NUMBER) { $request->getSort()->add(new OrderNumberProperty($order->orderBy()->key(), $direction)); } } } return $request; } private function filter(Request $request, Criteria $criteria): Request { $request = clone $request; if ($criteria->filters()->isEmpty()) { return $request; } foreach ($criteria->filters() as $filter) { /** @var Filter $filter */ $value = $filter->value()->value(); $field = $filter->field()->value(); if ('search' === $field) { if ($filter->operator()->value() === FilterOperator::CONTAINS) { $request->addMatch( 'suggest_search_content', [ 'query' => $value, ], ); } else { $request->addMatch( 'full_search_content', [ 'query' => $value, ], ); } continue; } if ('category_id' === $field) { $request->getQuery()->must( new Terms('category_id', $filter->value()->value()) ); continue; } } $keywordFilter = $this->getKeywordFilter($criteria); if (false === $keywordFilter->isEmpty()) { $keywordNestedFilter = new Nested(); $keywordNestedFilter->setPath('search_data') ->setQuery($keywordFilter); $request->getPostFilter()->filter($keywordNestedFilter); } $numberFilter = $this->getNumberFilter($criteria); if (false === $numberFilter->isEmpty()) { $numberNestedFilter = new Nested(); $numberNestedFilter->setPath('search_data') ->setQuery($numberFilter); $request->getPostFilter()->filter($numberNestedFilter); } return $request; } private function getNumberFilter(Criteria $criteria, array $excludeFilter = []): Query { $numberFilter = new Query(); $ranges = []; foreach ($criteria->filters() as $filter) { /** @var Filter $filter */ $value = $filter->value()->value(); $field = $filter->field()->value(); if (in_array($field, $excludeFilter, true)) { continue; } if (in_array($filter->operator()->value(), [FilterOperator::LT, FilterOperator::LTE], true)) { $ranges[$field][$filter->operator()->value()] = $value; continue; } if (in_array($filter->operator()->value(), [FilterOperator::GT, FilterOperator::GTE], true)) { $ranges[$field][$filter->operator()->value()] = $value; } } if (false === empty($ranges)) { foreach ($ranges as $field => $range) { $numberFilter->filter( new FilterNumberFacet( $field, $range ) ); } } return $numberFilter; } private function getKeywordFilter(Criteria $criteria, array $excludeFilter = []): Query { $keywordFilter = new Query(); foreach ($criteria->filters() as $filter) { /** @var Filter $filter */ $value = $filter->value()->value(); $field = $filter->field()->value(); if (in_array($field, $excludeFilter, true)) { continue; } if (in_array($filter->operator()->value(), [FilterOperator::LT, FilterOperator::LTE], true)) { continue; } if (in_array($filter->operator()->value(), [FilterOperator::GT, FilterOperator::GTE], true)) { continue; } if ('search' === $field) { continue; } if ('category_id' === $field) { continue; } if (is_array($value)) { $value = array_map(static fn($v) => (string)$v, $value); } else { $value = (string)$value; } $keywordFilter->filter(new FilterKeywordFacet($field, $value)); } return $keywordFilter; } private function aggs(Request $request, Criteria $criteria): Request { $request = clone $request; if ($criteria->filters()->isEmpty()) { return $request; } $request->getAggs()->add( AggsFacetTerms::create( 'keyword_facet', 'keyword_facet' ) ); $request->getAggs()->add( AggsFacetStats::create( 'number_facet', 'number_facet' ) ); $getKey = static fn(string $type, string $name): string => sprintf('%s_facet_%s', $type, $name); foreach ($criteria->filters() as $filter) { /** @var Filter $filter */ $field = $filter->field()->value(); if (in_array( $filter->operator()->value(), [ FilterOperator::LT, FilterOperator::LTE, FilterOperator::GT, FilterOperator::GTE, ], true ) ) { $aggsFiltered = new Aggs($getKey('number', $field)); $aggsFiltered->addAggs( AggsFacetStats::create( 'agg_special', 'number_facet' ) ); $queryNumberFiltered = new Query(); $keywordFilter = $this->getKeywordFilter($criteria); $numberFilter = $this->getNumberFilter($criteria, [$field]); if (false === $keywordFilter->isEmpty()) { $nestedFilterKeyword = new Nested(); $nestedFilterKeyword->setPath('search_data') ->setQuery($keywordFilter); $queryNumberFiltered->filter($nestedFilterKeyword); } if (false === $numberFilter->isEmpty()) { $nestedFilterNumber = new Nested(); $nestedFilterNumber->setPath('search_data') ->setQuery($numberFilter); $queryNumberFiltered->filter($nestedFilterNumber); } if ($queryNumberFiltered->isEmpty() === false) { $aggsFiltered->setQuery($queryNumberFiltered); } else { $aggsFiltered->setNested((new Nested())->setPath('search_data')); } $request->getAggs()->add($aggsFiltered); continue; } if (in_array($filter->operator()->value(), [], true)) { continue; } if ('search' === $field) { continue; } if ('category_id' === $field) { continue; } $aggsFiltered = new Aggs($getKey('keyword', $field)); $aggsFiltered->addAggs( AggsFacetTerms::create( 'agg_special', 'keyword_facet' ) ); $queryKeywordFiltered = new Query(); $keywordFilter = $this->getKeywordFilter($criteria, [$field]); $numberFilter = $this->getNumberFilter($criteria); if (false === $keywordFilter->isEmpty()) { $nestedFilterKeyword = new Nested(); $nestedFilterKeyword->setPath('search_data') ->setQuery($keywordFilter); $queryKeywordFiltered->filter($nestedFilterKeyword); } if (false === $numberFilter->isEmpty()) { $nestedFilterNumber = new Nested(); $nestedFilterNumber->setPath('search_data') ->setQuery($numberFilter); $queryKeywordFiltered->filter($nestedFilterNumber); } if ($queryKeywordFiltered->isEmpty() === false) { $aggsFiltered->setQuery($queryKeywordFiltered); } else { $aggsFiltered->setNested((new Nested())->setPath('search_data')); } $request->getAggs()->add($aggsFiltered); } $keywordFilter = $this->getKeywordFilter($criteria); $numberFilter = $this->getNumberFilter($criteria); $aggsKeywordFiltered = new Aggs('keyword_facet_filtered'); $aggsKeywordFiltered->addAggs( AggsFacetTerms::create( 'all_keyword_facet_filtered', 'keyword_facet' ) ); $queryKeywordFiltered = new Query(); $aggsNumberFiltered = new Aggs('number_facet_filtered'); $aggsNumberFiltered->addAggs( AggsFacetStats::create( 'all_number_facet_filtered', 'number_facet' ) ); $queryNumberFiltered = new Query(); if (false === $keywordFilter->isEmpty()) { $nestedFilterKeyword = new Nested(); $nestedFilterKeyword->setPath('search_data') ->setQuery($keywordFilter); $queryKeywordFiltered->filter($nestedFilterKeyword); $queryNumberFiltered->filter($nestedFilterKeyword); } if (false === $numberFilter->isEmpty()) { $nestedFilterNumber = new Nested(); $nestedFilterNumber->setPath('search_data') ->setQuery($numberFilter); $queryKeywordFiltered->filter($nestedFilterNumber); $queryNumberFiltered->filter($nestedFilterNumber); } if (false === $queryKeywordFiltered->isEmpty()) { $aggsKeywordFiltered->setQuery($queryKeywordFiltered); } else { $aggsKeywordFiltered->setNested((new Nested())->setPath('search_data')); } if (false === $queryNumberFiltered->isEmpty()) { $aggsNumberFiltered->setQuery($queryNumberFiltered); } else { $aggsNumberFiltered->setNested((new Nested())->setPath('search_data')); } $request->getAggs() ->add($aggsKeywordFiltered) ->add($aggsNumberFiltered); return $request; } }