Skip to content

Commit

Permalink
Merge pull request Smile-SA#3089 from rbayet/feat-catalogoptimizer-st…
Browse files Browse the repository at this point in the history
…ock-search-special-attributes

Ability to create rules based on stock qty and searchable content
  • Loading branch information
rbayet authored Oct 18, 2023
2 parents dbdfaa9 + ad5d3e5 commit e7d9503
Show file tree
Hide file tree
Showing 5 changed files with 344 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
<?php
/**
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Smile ElasticSuite to newer
* versions in the future.
*
* @category Smile
* @package Smile\ElasticsuiteCatalogRule
* @author Richard Bayet <richard.bayet@smile.fr>
* @copyright 2021 Smile
* @license Open Software License ("OSL") v. 3.0
*/

namespace Smile\ElasticsuiteCatalogRule\Model\Rule\Condition\Product\SpecialAttribute;

use Smile\ElasticsuiteCatalogRule\Api\Rule\Condition\Product\SpecialAttributeInterface;
use Smile\ElasticsuiteCatalogRule\Model\Rule\Condition\Product as ProductCondition;
use Smile\ElasticsuiteCore\Api\Search\ContextInterface;
use Smile\ElasticsuiteCore\Api\Search\Request\ContainerConfigurationInterface;
use Smile\ElasticsuiteCore\Api\Search\Spellchecker\RequestInterfaceFactory as SpellcheckRequestFactory;
use Smile\ElasticsuiteCore\Api\Search\SpellcheckerInterface;
use Smile\ElasticsuiteCore\Search\Request\ContainerConfigurationFactory;
use Smile\ElasticsuiteCore\Search\Request\Query\Builder as QueryBuilder;
use Smile\ElasticsuiteCore\Search\Request\Query\QueryFactory;
use Smile\ElasticsuiteCore\Search\Request\QueryInterface;

/**
* Class Search
*
* @category Elasticsuite
* @package Elasticsuite\CatalogRule
* @author Richard Bayet <richard.bayet@smile.fr>
*/
class Search implements SpecialAttributeInterface
{
/**
* @var ContainerConfigurationFactory
*/
private $containerConfigFactory;

/**
* @var ContextInterface
*/
private $searchContext;

/**
* @var QueryBuilder
*/
private $queryBuilder;

/**
* @var QueryFactory
*/
private $queryFactory;

/**
* @var SpellcheckRequestFactory
*/
private $spellcheckRequestFactory;

/**
* @var SpellcheckerInterface
*/
private $spellchecker;

/**
* Search constructor.
*
* @param ContainerConfigurationFactory $containerConfigFactory Container configuration factory.
* @param ContextInterface $searchContext Search context.
* @param QueryBuilder $queryBuilder Query builder.
* @param QueryFactory $queryFactory Query factory.
* @param SpellcheckRequestFactory $spellcheckRequestFactory Spellcheck request factory.
* @param SpellcheckerInterface $spellchecker Spellchecker.
*/
public function __construct(
ContainerConfigurationFactory $containerConfigFactory,
ContextInterface $searchContext,
QueryBuilder $queryBuilder,
QueryFactory $queryFactory,
SpellcheckRequestFactory $spellcheckRequestFactory,
SpellcheckerInterface $spellchecker
) {
$this->containerConfigFactory = $containerConfigFactory;
$this->searchContext = $searchContext;
$this->queryBuilder = $queryBuilder;
$this->queryFactory = $queryFactory;
$this->spellcheckRequestFactory = $spellcheckRequestFactory;
$this->spellchecker = $spellchecker;
}

/**
* {@inheritdoc}
*/
public function getAttributeCode()
{
return 'search';
}

/**
* {@inheritdoc}
*/
public function getSearchQuery(ProductCondition $condition)
{
$containerConfiguration = $this->getContainerConfiguration();
$queryText = $condition->getValue();
$searchQuery = $this->getFullTextQuery($containerConfiguration, $queryText);

if (substr($condition->getOperator(), 0, 1) === '!') {
$searchQuery = $this->queryFactory->create(QueryInterface::TYPE_NOT, ['query' => $searchQuery]);
}

return $searchQuery;
}

/**
* {@inheritdoc}
*/
public function getOperatorName()
{
return null;
}

/**
* {@inheritdoc}
*/
public function getInputType()
{
return 'string';
}

/**
* {@inheritdoc}
*/
public function getValueElementType()
{
return 'text';
}

/**
* {@inheritdoc}
*/
public function getValueName($value)
{
if ($value === null || '' === $value) {
return '...';
}

return $value;
}

/**
* {@inheritdoc}
*/
public function getValue($value)
{
return $value;
}

/**
* {@inheritdoc}
*/
public function getValueOptions()
{
return [];
}

/**
* {@inheritdoc}
*/
public function getLabel()
{
return __('Searchable content');
}

/**
* Get fulltext container configuration.
*
* @return ContainerConfigurationInterface
*/
private function getContainerConfiguration()
{
return $this->containerConfigFactory->create(
['containerName' => 'quick_search_container', 'storeId' => (int) $this->searchContext->getStoreId()]
);
}

/**
* Retrieve fulltext search query for a given query text
*
* @param ContainerConfigurationInterface $containerConfiguration Container configuration.
* @param string $queryText The query text.
*
* @return QueryInterface
*/
private function getFullTextQuery(ContainerConfigurationInterface $containerConfiguration, $queryText)
{
$spellingType = $this->getSpellingType($containerConfiguration, $queryText);

return $this->queryBuilder->createFulltextQuery($containerConfiguration, $queryText, $spellingType);
}

/**
* Retrieve the spelling type for a fulltext query.
*
* @param ContainerConfigurationInterface $containerConfiguration Container configuration.
* @param string $queryText Query text.
*
* @return int
*/
private function getSpellingType(ContainerConfigurationInterface $containerConfiguration, $queryText)
{
if (is_array($queryText)) {
$queryText = implode(" ", $queryText);
}

$spellcheckRequestParams = [
'index' => $containerConfiguration->getIndexName(),
'queryText' => $queryText,
'cutoffFrequency' => $containerConfiguration->getRelevanceConfig()->getCutOffFrequency(),
'isUsingAllTokens' => $containerConfiguration->getRelevanceConfig()->isUsingAllTokens(),
'isUsingReference' => $containerConfiguration->getRelevanceConfig()->isUsingReferenceAnalyzer(),
'isUsingEdgeNgram' => $containerConfiguration->getRelevanceConfig()->isUsingEdgeNgramAnalyzer(),
];

$spellcheckRequest = $this->spellcheckRequestFactory->create($spellcheckRequestParams);

return $this->spellchecker->getSpellingType($spellcheckRequest);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php
/**
* Elasticsuite stock qty catalog rule special attribute provider.
* Allows to use the stock qty (self or those of children for configurable products) to build catalog rules for
* search optimizers or virtual categories rules.
* Please note that indexed stock.qty for configurable or bundle products will vary depending on your usage
* of Multi Source Inventory modules: it will be left at 0 if you use the default source and stock.
*
* @category Smile
* @package Smile\ElasticsuiteCatalogRule
* @author Richard Bayet <richard.bayet@smile.fr>
* @copyright 2021 Smile
* @license Open Software License ("OSL") v. 3.0
*/

namespace Smile\ElasticsuiteCatalogRule\Model\Rule\Condition\Product\SpecialAttribute;

use Smile\ElasticsuiteCatalogRule\Api\Rule\Condition\Product\SpecialAttributeInterface;
use Smile\ElasticsuiteCatalogRule\Model\Rule\Condition\Product as ProductCondition;

/**
* Class StockQty
*
* @category Elasticsuite
* @package Elasticsuite\CatalogRule
* @author Richard Bayet <richard.bayet@smile.fr>
*/
class StockQty implements SpecialAttributeInterface
{
/**
* {@inheritdoc}
*/
public function getAttributeCode()
{
return 'stock.qty';
}

/**
* {@inheritdoc}
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function getSearchQuery(ProductCondition $condition)
{
// Query can be computed directly with the attribute code and value. (eg. stock.qty < 5).
return null;
}

/**
* {@inheritdoc}
*/
public function getOperatorName()
{
return null;
}

/**
* {@inheritdoc}
*/
public function getInputType()
{
return 'numeric';
}

/**
* {@inheritdoc}
*/
public function getValueElementType()
{
return 'text';
}

/**
* {@inheritdoc}
*/
public function getValueName($value)
{
if ($value === null || '' === $value) {
return '...';
}

return $value;
}

/**
* {@inheritdoc}
*/
public function getValue($rawValue)
{
return $rawValue;
}

/**
* {@inheritdoc}
*/
public function getValueOptions()
{
return [];
}

/**
* {@inheritdoc}
*/
public function getLabel()
{
return __('Stock qty');
}
}
2 changes: 2 additions & 0 deletions src/module-elasticsuite-catalog-rule/etc/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
<item name="is_giftcard" xsi:type="object">isGiftCardProduct</item>
<item name="is_grouped" xsi:type="object">isGroupedProduct</item>
<item name="is_simple" xsi:type="object">isSimpleProduct</item>
<item name="stock.qty" xsi:type="object">Smile\ElasticsuiteCatalogRule\Model\Rule\Condition\Product\SpecialAttribute\StockQty</item>
<item name="search" xsi:type="object">Smile\ElasticsuiteCatalogRule\Model\Rule\Condition\Product\SpecialAttribute\Search</item>
</argument>
</arguments>
</type>
Expand Down
2 changes: 2 additions & 0 deletions src/module-elasticsuite-catalog-rule/i18n/en_US.csv
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@
"Catalog Rules Configuration","Catalog Rules Configuration"
"Alphabetical sorting of attribute options in the rule engine","Alphabetical sorting of attribute options in the rule engine"
"If enabled, will forcefully sort select and multiselect attributes' option labels alphabetically when displaying them in the rule engine used by Optimizers and Virtual Categories. Useful if you have product attributes with very long list of options.","If enabled, will forcefully sort select and multiselect attributes' option labels alphabetically when displaying them in the rule engine used by Optimizers and Virtual Categories. Useful if you have product attributes with very long list of options."
"Stock qty","Stock qty"
"Searchable content","Searchable content"
2 changes: 2 additions & 0 deletions src/module-elasticsuite-catalog-rule/i18n/fr_FR.csv
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@
"Catalog Rules Configuration","Configuration des règles catalogue"
"Alphabetical sorting of attribute options in the rule engine","Tri alphabétique des options d'attributs dans le moteur de règles"
"If enabled, will forcefully sort select and multiselect attributes' option labels alphabetically when displaying them in the rule engine used by Optimizers and Virtual Categories. Useful if you have product attributes with very long list of options.","Si activé, forcera un tri alphabétique des labels des options d'attributs select et multiselect lors de leur affichage dans le moteur de règles utilisé par les Optimiseurs et les Catégories Virtuelles. Utile si vous avez des attributs produits avec un très grand nombre d'options."
"Stock qty","Quantité en stock"
"Searchable content","Contenu recherchable"

0 comments on commit e7d9503

Please sign in to comment.