161 lines
5.6 KiB
PHP
161 lines
5.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace KupShop\GraphQLBundle\ApiPublic\Controller;
|
|
|
|
use Elastica\Query\Ids;
|
|
use Elastica\Result;
|
|
use KupShop\AdminBundle\Util\SystemImageUtils;
|
|
use KupShop\CatalogBundle\Search\Exception\FulltextException;
|
|
use KupShop\CatalogBundle\Search\FulltextElastic;
|
|
use KupShop\CatalogElasticBundle\Util\ElasticaFactory;
|
|
use KupShop\CatalogElasticBundle\Util\ElasticQueryClient;
|
|
use KupShop\GraphQLBundle\ApiAdmin\Annotation\Module;
|
|
use KupShop\GraphQLBundle\ApiPublic\Types\Search\AutocompleteProductResultItem;
|
|
use KupShop\GraphQLBundle\ApiPublic\Types\Search\AutocompleteResult;
|
|
use KupShop\GraphQLBundle\ApiPublic\Types\Search\AutocompleteResultItem;
|
|
use KupShop\I18nBundle\Util\PriceConverter;
|
|
use KupShop\KupShopBundle\Context\CurrencyContext;
|
|
use KupShop\KupShopBundle\Context\LanguageContext;
|
|
use KupShop\KupShopBundle\Util\Contexts;
|
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
|
use TheCodingMachine\GraphQLite\Annotations\Query;
|
|
|
|
class SearchController
|
|
{
|
|
private const RESULTS_COUNT = 6;
|
|
|
|
private const ALLOWED_PRESEARCH_TYPES = [
|
|
FulltextElastic::INDEX_PRODUCTS,
|
|
FulltextElastic::INDEX_SECTIONS,
|
|
FulltextElastic::INDEX_PRODUCERS,
|
|
];
|
|
|
|
public function __construct(
|
|
private readonly FulltextElastic $fulltext,
|
|
private readonly SystemImageUtils $systemImageUtils,
|
|
private readonly ?ElasticaFactory $searchClientFactory,
|
|
private readonly ?ElasticQueryClient $elasticQueryClient,
|
|
private readonly CurrencyContext $currencyContext,
|
|
private readonly PriceConverter $priceConverter,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* @param string[] $types
|
|
*
|
|
* @return \Generator<AutocompleteResult>
|
|
*
|
|
* @throws FulltextException
|
|
*/
|
|
#[Query]
|
|
#[Module(\Modules::PRODUCTS_SECTIONS, \Modules::SUB_ELASTICSEARCH)]
|
|
public function autocomplete(string $term, array $types): \Generator
|
|
{
|
|
$invalidTypes = array_diff($types, $this->fulltext->getIndexTypes());
|
|
if ($invalidTypes) {
|
|
throw new \Exception('Invalid types:'.implode(', ', $invalidTypes));
|
|
}
|
|
|
|
$multiSearch = $this->searchClientFactory->createMultiSearch();
|
|
foreach ($types as $searchTopic) {
|
|
$pager = new \Pager();
|
|
$pager->setOnPage(self::RESULTS_COUNT);
|
|
$pager->setPageNumber(1);
|
|
|
|
$search = $this->elasticQueryClient->createGenericSearch(
|
|
searchTerm: $term,
|
|
index: $searchTopic,
|
|
pager: $pager,
|
|
);
|
|
|
|
$multiSearch->addSearch($search, $searchTopic);
|
|
}
|
|
|
|
$response = $multiSearch->search();
|
|
foreach ($response->getResultSets() as $type => $result) {
|
|
$items = array_map(
|
|
static fn (Result $r) => array_merge($r->getSource(), ['id' => $r->getId()]),
|
|
$result->getResults(),
|
|
);
|
|
|
|
$items = $this->processFulltextItems($type, $items);
|
|
yield new AutocompleteResult($type, $items, $result->getTotalHits());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string[] $types
|
|
*
|
|
* @return \Generator<AutocompleteResult>
|
|
*
|
|
* @throws BadRequestHttpException
|
|
*/
|
|
#[Query]
|
|
#[Module(\Modules::PRODUCTS_SECTIONS, \Modules::SUB_ELASTICSEARCH)]
|
|
public function presearch(array $types): \Generator
|
|
{
|
|
$dbcfg = \Settings::getDefault();
|
|
$config = $dbcfg->fulltext['preload'] ?? [];
|
|
|
|
if ($invalidTypes = array_diff($types, static::ALLOWED_PRESEARCH_TYPES)) {
|
|
throw new BadRequestHttpException('Invalid types: '.implode(', ', $invalidTypes));
|
|
}
|
|
|
|
$multiSearch = $this->searchClientFactory->createMultiSearch();
|
|
foreach ($types as $type) {
|
|
if (empty($config[$type])) {
|
|
continue;
|
|
}
|
|
|
|
$query = new \KupShop\CatalogElasticBundle\Elastica\Query();
|
|
$query->setQuery(new Ids($config[$type]));
|
|
|
|
$search = $this->searchClientFactory->createSearch($type)->setQuery($query);
|
|
$multiSearch->addSearch($search, $type);
|
|
}
|
|
|
|
if (empty($multiSearch->getSearches())) {
|
|
return yield from [];
|
|
}
|
|
|
|
$resultSet = $multiSearch->search();
|
|
|
|
foreach ($resultSet->getResultSets() as $type => $result) {
|
|
$items = $this->processFulltextItems($type, array_map(fn (Result $x) => ['id' => $x->getId(), ...$x->getSource()], $result->getResults()));
|
|
yield new AutocompleteResult($type, $items, 0);
|
|
}
|
|
}
|
|
|
|
protected function processFulltextItems(string $type, array $items)
|
|
{
|
|
$cfg = \Settings::getDefault();
|
|
$lang = Contexts::get(LanguageContext::class)->getActiveId();
|
|
$defaultImagePath = $this->systemImageUtils->getSystemImagePlaceholderPath();
|
|
if (!str_starts_with($defaultImagePath, '/')) {
|
|
$defaultImagePath = '/'.$defaultImagePath;
|
|
}
|
|
|
|
if (!is_array($cfg['Photo']['placeholder'] ?? null)) {
|
|
$placeholder = getVal('placeholder', $cfg['Photo'], $defaultImagePath);
|
|
}
|
|
|
|
if (empty($placeholder)) {
|
|
$placeholder = $cfg['Photo']['placeholder']['lang'][$lang] ?? $cfg['Photo']['placeholder']['default'] ?? $defaultImagePath;
|
|
}
|
|
|
|
if ($type == FulltextElastic::INDEX_PRODUCTS) {
|
|
foreach ($items as $item) {
|
|
yield new AutocompleteProductResultItem($type, $item, $placeholder, $this->currencyContext, $this->priceConverter);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
foreach ($items as $item) {
|
|
yield new AutocompleteResultItem($type, $item, $placeholder);
|
|
}
|
|
}
|
|
}
|