Skip to content

Commit

Permalink
[Virtual Category] Prevent using cached query for sub virtual category
Browse files Browse the repository at this point in the history
If we use this cached query we will build a category query with many useless clauses as the optimization of the method \Smile\ElasticsuiteVirtualCategory\Model\Rule::isVirtualCategoryRootInExcludeCategories will be avoided
  • Loading branch information
PierreGauthier committed Jan 14, 2025
1 parent dd36854 commit 5afcdc3
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 21 deletions.
21 changes: 19 additions & 2 deletions src/module-elasticsuite-virtual-category/Helper/Rule.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,24 +47,32 @@ class Rule
*/
private $categoryRepository;

/**
* @var Config
*/
private $config;

/**
* Provider constructor.
*
* @param \Magento\Framework\App\CacheInterface $cache Cache.
* @param \Magento\Customer\Model\Session $customerSession Customer session.
* @param ReadHandler $readHandler Rule read handler.
* @param \Magento\Catalog\Api\CategoryRepositoryInterfaceFactory $categoryRepository Category factory.
* @param Config $config Virtual category configuration.
*/
public function __construct(
\Magento\Framework\App\CacheInterface $cache,
\Magento\Customer\Model\Session $customerSession,
ReadHandler $readHandler,
CategoryRepositoryInterfaceFactory $categoryRepository
CategoryRepositoryInterfaceFactory $categoryRepository,
Config $config
) {
$this->cache = $cache;
$this->customerSession = $customerSession;
$this->readHandler = $readHandler;
$this->categoryRepository = $categoryRepository;
$this->config = $config;
}

/**
Expand All @@ -79,7 +87,16 @@ public function __construct(
public function loadUsingCache(CategoryInterface $category, $callback)
{
\Magento\Framework\Profiler::start('ES:Virtual Rule ' . $callback);
$cacheKey = implode('|', [$callback, $category->getStoreId(), $category->getId(), $this->customerSession->getCustomerGroupId()]);
$cacheKey = implode(
'|',
[
$callback,
$category->getStoreId(),
$category->getId(),
$this->customerSession->getCustomerGroupId(),
(int) $this->config->isForceZeroResultsForDisabledCategoriesEnabled($category->getStoreId()),
]
);

$data = $this->cache->load($cacheKey);

Expand Down
57 changes: 38 additions & 19 deletions src/module-elasticsuite-virtual-category/Model/Rule.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,25 +189,30 @@ public function getCategorySearchQuery($category, $excludedCategories = []): ?Qu
\Magento\Framework\Profiler::start('ES:Virtual Rule ' . __FUNCTION__);
$categoryId = (int) (!is_object($category) ? $category : $category->getId());
$storeId = !is_object($category) ? $this->getStoreId() : $category->getStoreId();
$storeId = $storeId ?: $this->storeManager->getStore()->getId();
$cacheKey = implode(
'|',
[
__FUNCTION__,
$storeId,
$categoryId,
$this->customerSession->getCustomerGroupId(),
$this->config->isForceZeroResultsForDisabledCategoriesEnabled($storeId),
(int) $this->config->isForceZeroResultsForDisabledCategoriesEnabled($storeId),
]
);

$query = $this->getFromLocalCache($categoryId);

// If the category is not an object, it can't be in a "draft" mode.
if ($query === false && (!is_object($category) || !$category->getHasDraftVirtualRule())) {
// Due to the fact we serialize/unserialize completely pre-built queries as object.
// We cannot use any implementation of SerializerInterface.
$query = $this->sharedCache->load($cacheKey);
$query = $query ? unserialize($query) : false;
// If the category virtual root is one of the excluded categories, we must recalculate the query.
// We can't use the cached one in order to avoid loop and very big queries.
if (!is_object($category) || !$this->isVirtualCategoryRootInExcludeCategories($category, $excludedCategories)) {
// Due to the fact we serialize/unserialize completely pre-built queries as object.
// We cannot use any implementation of SerializerInterface.
$query = $this->sharedCache->load($cacheKey);
$query = $query ? unserialize($query) : false;
}
}

if ($query === false) {
Expand Down Expand Up @@ -439,23 +444,11 @@ private function getVirtualCategoryQuery(
CategoryInterface $category,
$excludedCategories = []
): ?QueryInterface {
$rootCategory = $this->getVirtualRootCategory($category);
// If the root category of the current virtual category has already been computed (exist in $excludedCategories)
// or if a parent of the root category of the current category has already been computed we don't need
// to compute the rule. All the product will already been present.
// For example, if you have the following category tree:
// - Category A (static)
// - - Category B (static)
// - - Category C (virtual with category B as root)
// When you compute the rule of the category A you do not need to compute the rule of the category C
// as all the product will be there.
if ($rootCategory
&& $rootCategory->getPath()
&& array_intersect(explode('/', (string) $rootCategory->getPath()), $excludedCategories)
) {
if ($this->isVirtualCategoryRootInExcludeCategories($category, $excludedCategories)) {
return null;
}

$rootCategory = $this->getVirtualRootCategory($category);
$query = $category->getVirtualRule()->getConditions()->getSearchQuery($excludedCategories);
if ($query instanceof QueryInterface) {
$queryName = sprintf('(%s) virtual category [%s]:%d', $category->getPath(), $category->getName(), $category->getId());
Expand Down Expand Up @@ -630,4 +623,30 @@ private function getNoResultsQuery(): QueryInterface
['field' => 'entity_id', 'values' => [0]]
);
}

/**
* If the root category of the current virtual category has already been computed (exist in $excludedCategories)
* or if a parent of the root category of the current category has already been computed we don't need
* to compute the rule. All the product will already been present.
* For example, if you have the following category tree:
* - Category A (static)
* - - Category B (static)
* - - Category C (virtual with category B as root)
* When you compute the rule of the category A you do not need to compute the rule of the category C
* as all the product will be there.
*
* @param CategoryInterface $category category to check
* @param array $excludedCategories list of actual excluded categories
* @return bool
* @throws NoSuchEntityException
*/
private function isVirtualCategoryRootInExcludeCategories(CategoryInterface $category, array $excludedCategories): bool
{
$rootCategory = $this->getVirtualRootCategory($category);

return $category->getIsVirtualCategory()
&& $rootCategory
&& $rootCategory->getPath()
&& array_intersect(explode('/', (string) $rootCategory->getPath()), $excludedCategories);
}
}

0 comments on commit 5afcdc3

Please sign in to comment.