diff --git a/src/module-elasticsuite-catalog-graph-ql/Model/Resolver/Products.php b/src/module-elasticsuite-catalog-graph-ql/Model/Resolver/Products.php index 029f3c89b..4f0aab03e 100644 --- a/src/module-elasticsuite-catalog-graph-ql/Model/Resolver/Products.php +++ b/src/module-elasticsuite-catalog-graph-ql/Model/Resolver/Products.php @@ -78,6 +78,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value 'current_page' => $searchResult->getCurrentPage(), 'total_pages' => $searchResult->getTotalPages(), 'is_spellchecked' => $searchResult->isSpellchecked(), + 'query_id' => $searchResult->getQueryId(), ], 'search_result' => $searchResult, 'layer_type' => $layerType, diff --git a/src/module-elasticsuite-catalog-graph-ql/Model/Resolver/Products/Query/Search.php b/src/module-elasticsuite-catalog-graph-ql/Model/Resolver/Products/Query/Search.php index 659e72918..34ac12b9d 100644 --- a/src/module-elasticsuite-catalog-graph-ql/Model/Resolver/Products/Query/Search.php +++ b/src/module-elasticsuite-catalog-graph-ql/Model/Resolver/Products/Query/Search.php @@ -138,6 +138,7 @@ public function getResult(array $args, ResolveInfo $info, ContextInterface $cont 'currentPage' => $searchCriteria->getCurrentPage(), 'totalPages' => $maxPages, 'isSpellchecked' => $searchResults->__toArray()['is_spellchecked'] ?? false, + 'queryId' => $searchResults->__toArray()['query_id'] ?? null, 'suggestions' => $suggestions, ]); } diff --git a/src/module-elasticsuite-catalog-graph-ql/Model/Resolver/Products/SearchResult.php b/src/module-elasticsuite-catalog-graph-ql/Model/Resolver/Products/SearchResult.php index 607919936..9e6417856 100644 --- a/src/module-elasticsuite-catalog-graph-ql/Model/Resolver/Products/SearchResult.php +++ b/src/module-elasticsuite-catalog-graph-ql/Model/Resolver/Products/SearchResult.php @@ -44,4 +44,12 @@ public function isSpellchecked() { return (bool) $this->data['isSpellchecked'] ?? false; } + + /** + * @return ?int + */ + public function getQueryId() + { + return $this->data['queryId'] ?? null; + } } diff --git a/src/module-elasticsuite-catalog-graph-ql/etc/schema.graphqls b/src/module-elasticsuite-catalog-graph-ql/etc/schema.graphqls index 94cf9c3cf..ec0eea18c 100644 --- a/src/module-elasticsuite-catalog-graph-ql/etc/schema.graphqls +++ b/src/module-elasticsuite-catalog-graph-ql/etc/schema.graphqls @@ -20,4 +20,5 @@ type ViewMoreResult @doc(description: "The Products object is the top-level obje type SearchResultPageInfo { is_spellchecked: Boolean + query_id: Int } diff --git a/src/module-elasticsuite-catalog-optimizer/Ui/Component/Listing/Column/BoostWeight.php b/src/module-elasticsuite-catalog-optimizer/Ui/Component/Listing/Column/BoostWeight.php index a24fc5b9a..9c5c08483 100644 --- a/src/module-elasticsuite-catalog-optimizer/Ui/Component/Listing/Column/BoostWeight.php +++ b/src/module-elasticsuite-catalog-optimizer/Ui/Component/Listing/Column/BoostWeight.php @@ -62,8 +62,18 @@ public function prepareDataSource(array $dataSource) { if (isset($dataSource['data']['items'])) { foreach ($dataSource['data']['items'] as &$item) { + $value = ''; $config = $this->serializer->unserialize($item['config']); - $value = $config['constant_score_value'] ? ($config['constant_score_value'] . '%') : ''; + $type = $item['model'] ?? 'constant_score'; + if ($type === 'constant_score') { + $value = $config['constant_score_value'] ? ($config['constant_score_value'] . '%') : ''; + } elseif ($type === 'attribute_value') { + $factor = $config['scale_factor'] ?? ''; + $modifier = $config['scale_function'] ?? ''; + $field = $config['attribute_code'] ?? ''; + $value = sprintf("%s(%s * %s)", $modifier, $factor, $field); + } + $item[$this->getData('name')] = $value; } } diff --git a/src/module-elasticsuite-catalog/Plugin/Category/Toolbar/SortDirectionPerCategoryPlugin.php b/src/module-elasticsuite-catalog/Plugin/Category/Toolbar/SortDirectionPerCategoryPlugin.php index 28fd83d0c..b0924c12c 100644 --- a/src/module-elasticsuite-catalog/Plugin/Category/Toolbar/SortDirectionPerCategoryPlugin.php +++ b/src/module-elasticsuite-catalog/Plugin/Category/Toolbar/SortDirectionPerCategoryPlugin.php @@ -15,7 +15,11 @@ use Magento\Catalog\Block\Product\ProductList\Toolbar as ProductListToolbar; use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Request\Http; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; /** * Plugin which is modified the behavior of sorting arrows based on the custom sort direction attribute. @@ -26,6 +30,8 @@ */ class SortDirectionPerCategoryPlugin { + const XML_PATH_LIST_DEFAULT_SORT_DIRECTION_BY = 'catalog/frontend/default_sort_direction_by'; + /** * @var CategoryRepositoryInterface */ @@ -36,18 +42,38 @@ class SortDirectionPerCategoryPlugin */ private $request; + /** + * Scope configuration. + * + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * Store manager. + * + * @var StoreManagerInterface + */ + protected $storeManager; + /** * Toolbar constructor. * * @param CategoryRepositoryInterface $categoryRepository Category Repository. * @param Http $request Http request. + * @param ScopeConfigInterface $scopeConfig Scope configuration. + * @param StoreManagerInterface $storeManager Store manager. */ public function __construct( CategoryRepositoryInterface $categoryRepository, - Http $request + Http $request, + ScopeConfigInterface $scopeConfig, + StoreManagerInterface $storeManager ) { $this->categoryRepository = $categoryRepository; $this->request = $request; + $this->scopeConfig = $scopeConfig; + $this->storeManager = $storeManager; } /** @@ -57,6 +83,7 @@ public function __construct( * @param mixed $collection Collection. * * @return array + * @throws NoSuchEntityException */ public function beforeSetCollection(ProductListToolbar $subject, $collection) { @@ -69,30 +96,55 @@ public function beforeSetCollection(ProductListToolbar $subject, $collection) return [$collection]; } + /** + * Retrieve Product List Default Sort Direction By + * + * @return string|null + * @throws NoSuchEntityException + */ + private function getProductListDefaultSortDirectionBy() + { + // Get the current store ID. + $storeId = $this->storeManager->getStore()->getId(); + + // Fetch system configuration value for 'default_sort_direction_by' at the store level. + return $this->scopeConfig->getValue( + self::XML_PATH_LIST_DEFAULT_SORT_DIRECTION_BY, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } + /** * Get the custom sort direction from the current category. * * @return string|null + * @throws NoSuchEntityException */ private function getCustomSortDirection() { $categoryId = $this->request->getParam('id'); if (!$categoryId) { - return null; // Return null if category ID is not available. + return $this->getProductListDefaultSortDirectionBy(); // Fallback to system config value if no category ID. } try { $category = $this->categoryRepository->get($categoryId); + + // Check if the category has a custom sort direction set. $customDirection = $category->getSortDirection(); + // If a custom sort direction exists for the category and is valid, return it. if ($customDirection && in_array($customDirection, ['asc', 'desc'])) { return $customDirection; } } catch (\Exception $e) { - return null; // Handle category not found or other exceptions. + // Handle exceptions (e.g., category not found) by falling back to the system config. + return $this->getProductListDefaultSortDirectionBy(); } - return null; + // If no custom sort direction for the category, return the default system config. + return $this->getProductListDefaultSortDirectionBy(); } } diff --git a/src/module-elasticsuite-catalog/i18n/nl_NL.csv b/src/module-elasticsuite-catalog/i18n/nl_NL.csv index c80780e18..2611f1dc3 100644 --- a/src/module-elasticsuite-catalog/i18n/nl_NL.csv +++ b/src/module-elasticsuite-catalog/i18n/nl_NL.csv @@ -4,7 +4,7 @@ "Save and Continue Edit","Opslaan en Doorgaan" "Save","Opslaan" "1 product","1 artikel" -"<%- count %> products","<%- aantal %> producten" +"<%- count %> products","<%- count %> producten" "No products in the selected range.","Geen producten in het geselecteerde bereik." "Search Configuration","Zoekconfiguratie" "Can be used only with catalog input type Text field, Dropdown, Multiple Select and Price.","Kan alleen gebruikt worden met invoertype catalogus, tekstveld, Dropdown, Multiple select en Prijs." diff --git a/src/module-elasticsuite-core/Model/Search.php b/src/module-elasticsuite-core/Model/Search.php index cee7a93de..ddb81fde2 100644 --- a/src/module-elasticsuite-core/Model/Search.php +++ b/src/module-elasticsuite-core/Model/Search.php @@ -14,6 +14,11 @@ namespace Smile\ElasticsuiteCore\Model; +use Magento\Framework\Search\SearchEngineInterface; +use Magento\Framework\Search\SearchResponseBuilder; +use Smile\ElasticsuiteCore\Api\Search\ContextInterface; +use Smile\ElasticsuiteCore\Model\Search\RequestBuilder; + /** * SearchInterface implementation using elasticsuite. * @@ -24,35 +29,43 @@ class Search implements \Magento\Search\Api\SearchInterface { /** - * @var \Smile\ElasticsuiteCore\Model\Search\RequestBuilder + * @var RequestBuilder */ private $searchRequestBuilder; /** - * @var \Magento\Framework\Search\SearchEngineInterface + * @var SearchEngineInterface */ private $searchEngine; /** - * @var \Magento\Framework\Search\SearchResponseBuilder + * @var SearchResponseBuilder */ private $searchResponseBuilder; + /** + * @var ContextInterface + */ + private $searchContext; + /** * Constructor. * - * @param \Magento\Framework\Search\SearchEngineInterface $searchEngine Search engine. - * @param \Smile\ElasticsuiteCore\Model\Search\RequestBuilder $searchRequestBuilder Search request builder. - * @param \Magento\Framework\Search\SearchResponseBuilder $searchResponseBuilder Search response builder. + * @param SearchEngineInterface $searchEngine Search engine. + * @param RequestBuilder $searchRequestBuilder Search request builder. + * @param SearchResponseBuilder $searchResponseBuilder Search response builder. + * @param ContextInterface $searchContext Search context. */ public function __construct( - \Magento\Framework\Search\SearchEngineInterface $searchEngine, - \Smile\ElasticsuiteCore\Model\Search\RequestBuilder $searchRequestBuilder, - \Magento\Framework\Search\SearchResponseBuilder $searchResponseBuilder + SearchEngineInterface $searchEngine, + RequestBuilder $searchRequestBuilder, + SearchResponseBuilder $searchResponseBuilder, + ContextInterface $searchContext ) { - $this->searchRequestBuilder = $searchRequestBuilder; - $this->searchEngine = $searchEngine; - $this->searchResponseBuilder = $searchResponseBuilder; + $this->searchRequestBuilder = $searchRequestBuilder; + $this->searchEngine = $searchEngine; + $this->searchResponseBuilder = $searchResponseBuilder; + $this->searchContext = $searchContext; } /** @@ -68,10 +81,13 @@ public function search(\Magento\Framework\Api\Search\SearchCriteriaInterface $se $searchResponse = $this->searchEngine->search($searchRequest); $searchResult = $this->searchResponseBuilder->build($searchResponse); + $query = $this->searchContext->getCurrentSearchQuery(); + $totalCount = $searchResponse->count(); $searchResult->setTotalCount($totalCount); $searchResult->setSearchCriteria($searchCriteria); $searchResult->setData('is_spellchecked', (bool) $searchRequest->isSpellchecked()); + $searchResult->setData('query_id', ($query && $query->getId()) ? (int) $query->getId() : null); return $searchResult; } diff --git a/src/module-elasticsuite-core/Search/Adapter/Elasticsuite/Request/Query/Builder/Regexp.php b/src/module-elasticsuite-core/Search/Adapter/Elasticsuite/Request/Query/Builder/Regexp.php new file mode 100644 index 000000000..82e4bebec --- /dev/null +++ b/src/module-elasticsuite-core/Search/Adapter/Elasticsuite/Request/Query/Builder/Regexp.php @@ -0,0 +1,56 @@ + + * @copyright 2024 Smile + * @license Open Software License ("OSL") v. 3.0 + */ + +namespace Smile\ElasticsuiteCore\Search\Adapter\Elasticsuite\Request\Query\Builder; + +use Smile\ElasticsuiteCore\Search\Adapter\Elasticsuite\Request\Query\BuilderInterface; +use Smile\ElasticsuiteCore\Search\Request\QueryInterface; + +/** + * Build an ES regexp query. + * + * @category Smile + * @package Smile\ElasticsuiteCore + * @author Richard BAYET + */ +class Regexp implements BuilderInterface +{ + /** + * @var string + */ + const DEFAULT_FLAGS = 'NONE'; + + /** + * {@inheritDoc} + */ + public function buildQuery(QueryInterface $query) + { + if ($query->getType() !== QueryInterface::TYPE_REGEXP) { + throw new \InvalidArgumentException("Query builder : invalid query type {$query->getType()}"); + } + + $searchQueryParams = [ + 'value' => $query->getValue(), + 'boost' => $query->getBoost(), + 'flags' => self::DEFAULT_FLAGS, + ]; + + $searchQuery = ['regexp' => [$query->getField() => $searchQueryParams]]; + + if ($query->getName()) { + $searchQuery['regexp']['_name'] = $query->getName(); + } + + return $searchQuery; + } +} diff --git a/src/module-elasticsuite-core/Search/Adapter/Elasticsuite/Spellchecker.php b/src/module-elasticsuite-core/Search/Adapter/Elasticsuite/Spellchecker.php index 0f6c4f86a..3834a7f03 100644 --- a/src/module-elasticsuite-core/Search/Adapter/Elasticsuite/Spellchecker.php +++ b/src/module-elasticsuite-core/Search/Adapter/Elasticsuite/Spellchecker.php @@ -163,6 +163,7 @@ private function getTermVectors(RequestInterface $request) MappingInterface::DEFAULT_SPELLING_FIELD => $request->getQueryText(), ], ]; + $perFieldAnalyzer = []; if ($request->isUsingReference()) { $doc['fields'][] = MappingInterface::DEFAULT_REFERENCE_FIELD . "." . FieldInterface::ANALYZER_REFERENCE; @@ -171,9 +172,15 @@ private function getTermVectors(RequestInterface $request) if ($request->isUsingEdgeNgram()) { $doc['fields'][] = MappingInterface::DEFAULT_EDGE_NGRAM_FIELD . "." . FieldInterface::ANALYZER_EDGE_NGRAM; + $perFieldAnalyzer[MappingInterface::DEFAULT_EDGE_NGRAM_FIELD . "." . FieldInterface::ANALYZER_EDGE_NGRAM] + = FieldInterface::ANALYZER_STANDARD; $doc['doc'][MappingInterface::DEFAULT_EDGE_NGRAM_FIELD] = $request->getQueryText(); } + if (!empty($perFieldAnalyzer)) { + $doc['per_field_analyzer'] = $perFieldAnalyzer; + } + $docs = []; // Compute the mtermvector query on all indices. diff --git a/src/module-elasticsuite-core/Search/Request/Query/Regexp.php b/src/module-elasticsuite-core/Search/Request/Query/Regexp.php new file mode 100644 index 000000000..99c5974b2 --- /dev/null +++ b/src/module-elasticsuite-core/Search/Request/Query/Regexp.php @@ -0,0 +1,116 @@ + + * @copyright 2024 Smile + * @license Open Software License ("OSL") v. 3.0 + */ + +namespace Smile\ElasticsuiteCore\Search\Request\Query; + +use Smile\ElasticsuiteCore\Search\Request\QueryInterface; + +/** + * ElasticSuite minimal regexp query implementation. + * + * @category Smile + * @package Smile\ElasticsuiteCore + * @author Richard BAYET + */ +class Regexp implements QueryInterface +{ + /** + * @var string + */ + private $name; + + /** + * @var integer + */ + private $boost; + + /** + * @var string + */ + private $value; + + /** + * @var string + */ + private $field; + + /** + * Constructor. + * + * @param string $value Query value ie the regular expression. + * @param string $field Query field. + * @param string $name Name of the query. + * @param integer $boost Query boost. + */ + public function __construct($value, $field, $name = null, $boost = QueryInterface::DEFAULT_BOOST_VALUE) + { + $this->name = $name; + $this->value = $value; + $this->field = $field; + $this->boost = $boost; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritDoc} + */ + public function setName($name): self + { + $this->name = $name; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function getBoost() + { + return $this->boost; + } + + /** + * {@inheritDoc} + */ + public function getType() + { + return QueryInterface::TYPE_REGEXP; + } + + /** + * Search value. + * + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * Search field. + * + * @return string + */ + public function getField() + { + return $this->field; + } +} diff --git a/src/module-elasticsuite-core/Search/Request/QueryInterface.php b/src/module-elasticsuite-core/Search/Request/QueryInterface.php index df176bcf9..92c2ccd8a 100644 --- a/src/module-elasticsuite-core/Search/Request/QueryInterface.php +++ b/src/module-elasticsuite-core/Search/Request/QueryInterface.php @@ -38,6 +38,7 @@ interface QueryInterface extends \Magento\Framework\Search\Request\QueryInterfac const TYPE_MORELIKETHIS = 'moreLikeThisQuery'; const TYPE_MATCHPHRASEPREFIX = 'matchPhrasePrefixQuery'; const TYPE_PREFIX = 'prefixQuery'; + const TYPE_REGEXP = 'regexpQuery'; /** * Set the query name diff --git a/src/module-elasticsuite-core/Test/Unit/Model/SearchTest.php b/src/module-elasticsuite-core/Test/Unit/Model/SearchTest.php index c97ba9e62..caf54d75b 100644 --- a/src/module-elasticsuite-core/Test/Unit/Model/SearchTest.php +++ b/src/module-elasticsuite-core/Test/Unit/Model/SearchTest.php @@ -13,10 +13,11 @@ */ namespace Smile\ElasticsuiteCore\Test\Unit\Model; -use Smile\ElasticsuiteCore\Model\Search; +use Smile\ElasticsuiteCore\Api\Search\ContextInterface; /** * Search API unit testing. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * * @category Smile * @package Smile\ElasticsuiteCore @@ -39,8 +40,9 @@ public function testSearch($documents, $docCount) $searchEngine = $this->getSearchEngine($documents, $docCount); $searchRequestBuilder = $this->getSearchRequestBuilder(); $searchResponseBuilder = $this->getSearchResponseBuilder(); + $searchContext = $this->createMock(ContextInterface::class); - $searchApi = new \Smile\ElasticsuiteCore\Model\Search($searchEngine, $searchRequestBuilder, $searchResponseBuilder); + $searchApi = new \Smile\ElasticsuiteCore\Model\Search($searchEngine, $searchRequestBuilder, $searchResponseBuilder, $searchContext); $searchCriteria = $this->createMock(\Smile\ElasticsuiteCore\Api\Search\SearchCriteriaInterface::class); $searchResponse = $searchApi->search($searchCriteria); diff --git a/src/module-elasticsuite-core/Test/Unit/Search/Adapter/Elasticsuite/Request/Query/Builder/RegexpTest.php b/src/module-elasticsuite-core/Test/Unit/Search/Adapter/Elasticsuite/Request/Query/Builder/RegexpTest.php new file mode 100644 index 000000000..7b877f187 --- /dev/null +++ b/src/module-elasticsuite-core/Test/Unit/Search/Adapter/Elasticsuite/Request/Query/Builder/RegexpTest.php @@ -0,0 +1,78 @@ + + * @copyright 2024 Smile + * @license Open Software License ("OSL") v. 3.0 + */ + +namespace Smile\ElasticsuiteCore\Test\Unit\Search\Adapter\Elasticsuite\Request\Query\Builder; + +use Smile\ElasticsuiteCore\Search\Request\Query\Regexp as RegexpQuery; +use Smile\ElasticsuiteCore\Search\Adapter\Elasticsuite\Request\Query\Builder\Regexp as RegexpQueryBuilder; + +/** + * Regexp search query test case. + * + * @category Smile + * @package Smile\ElasticsuiteCore + * @author Richard BAYET + */ +class RegexpTest extends AbstractSimpleQueryBuilderTest +{ + /** + * Test the builder with mandatory params only. + * + * @return void + */ + public function testAnonymousRegexpQueryBuilder() + { + $builder = $this->getQueryBuilder(); + + $regexpQuery = new RegexpQuery('value', 'field'); + $query = $builder->buildQuery($regexpQuery); + + $this->assertArrayHasKey('regexp', $query); + $this->assertArrayHasKey('field', $query['regexp']); + $this->assertArrayHasKey('value', $query['regexp']['field']); + $this->assertEquals('value', $query['regexp']['field']['value']); + + $this->assertArrayHasKey('boost', $query['regexp']['field']); + $this->assertEquals(RegexpQuery::DEFAULT_BOOST_VALUE, $query['regexp']['field']['boost']); + + $this->assertArrayHasKey('flags', $query['regexp']['field']); + $this->assertEquals($builder::DEFAULT_FLAGS, $query['regexp']['field']['flags']); + + $this->assertArrayNotHasKey('_name', $query['regexp']); + } + + /** + * Test the builder with mandatory + name params. + * + * @return void + */ + public function testNamedRegexpQueryBuilder() + { + $builder = $this->getQueryBuilder(); + + $regexpQuery = new RegexpQuery('value', 'field', 'queryName'); + $query = $builder->buildQuery($regexpQuery); + + $this->assertArrayHasKey('regexp', $query); + $this->assertArrayHasKey('_name', $query['regexp']); + $this->assertEquals('queryName', $query['regexp']['_name']); + } + + /** + * {@inheritDoc} + */ + protected function getQueryBuilder() + { + return new RegexpQueryBuilder(); + } +} diff --git a/src/module-elasticsuite-core/Test/Unit/Search/Adapter/Elasticsuite/SpellcheckerTest.php b/src/module-elasticsuite-core/Test/Unit/Search/Adapter/Elasticsuite/SpellcheckerTest.php index 7b22c8baa..a835dfd4f 100644 --- a/src/module-elasticsuite-core/Test/Unit/Search/Adapter/Elasticsuite/SpellcheckerTest.php +++ b/src/module-elasticsuite-core/Test/Unit/Search/Adapter/Elasticsuite/SpellcheckerTest.php @@ -181,6 +181,10 @@ public function testEdgeNgramTermVectorsParams() MappingInterface::DEFAULT_SEARCH_FIELD . "." . FieldInterface::ANALYZER_WHITESPACE, MappingInterface::DEFAULT_EDGE_NGRAM_FIELD . "." . FieldInterface::ANALYZER_EDGE_NGRAM, ], + 'per_field_analyzer' => [ + MappingInterface::DEFAULT_EDGE_NGRAM_FIELD . "." . FieldInterface::ANALYZER_EDGE_NGRAM + => FieldInterface::ANALYZER_STANDARD, + ], 'doc' => [ MappingInterface::DEFAULT_SEARCH_FIELD => $queryText, MappingInterface::DEFAULT_SPELLING_FIELD => $queryText, @@ -239,6 +243,10 @@ public function testReferenceAndEdgeNgramTermVectorsParams() MappingInterface::DEFAULT_REFERENCE_FIELD . "." . FieldInterface::ANALYZER_REFERENCE, MappingInterface::DEFAULT_EDGE_NGRAM_FIELD . "." . FieldInterface::ANALYZER_EDGE_NGRAM, ], + 'per_field_analyzer' => [ + MappingInterface::DEFAULT_EDGE_NGRAM_FIELD . "." . FieldInterface::ANALYZER_EDGE_NGRAM + => FieldInterface::ANALYZER_STANDARD, + ], 'doc' => [ MappingInterface::DEFAULT_SEARCH_FIELD => $queryText, MappingInterface::DEFAULT_SPELLING_FIELD => $queryText, diff --git a/src/module-elasticsuite-core/etc/di.xml b/src/module-elasticsuite-core/etc/di.xml index 8b42032ef..c4bc2146c 100644 --- a/src/module-elasticsuite-core/etc/di.xml +++ b/src/module-elasticsuite-core/etc/di.xml @@ -139,6 +139,7 @@ spanWithinFactory prefixQueryFactory neuralQueryFactory + regexpQueryFactory @@ -169,6 +170,7 @@ + @@ -199,6 +201,7 @@ Smile\ElasticsuiteCore\Search\Adapter\Elasticsuite\Request\Query\Builder\Span\SpanWithin\Proxy Smile\ElasticsuiteCore\Search\Adapter\Elasticsuite\Request\Query\Builder\Prefix\Proxy Smile\ElasticsuiteCore\Search\Adapter\Elasticsuite\Request\Query\Builder\Opensearch\Neural\Proxy + Smile\ElasticsuiteCore\Search\Adapter\Elasticsuite\Request\Query\Builder\Regexp\Proxy diff --git a/src/module-elasticsuite-core/etc/elasticsuite_analysis.xml b/src/module-elasticsuite-core/etc/elasticsuite_analysis.xml index 235b34eb6..8e059509b 100644 --- a/src/module-elasticsuite-core/etc/elasticsuite_analysis.xml +++ b/src/module-elasticsuite-core/etc/elasticsuite_analysis.xml @@ -402,7 +402,7 @@ - + @@ -452,7 +452,7 @@ - + diff --git a/src/module-elasticsuite-tracker/Cron/IndexLogEvent.php b/src/module-elasticsuite-tracker/Cron/IndexLogEvent.php index c22689d77..1020153d0 100644 --- a/src/module-elasticsuite-tracker/Cron/IndexLogEvent.php +++ b/src/module-elasticsuite-tracker/Cron/IndexLogEvent.php @@ -47,8 +47,8 @@ class IndexLogEvent * Constructor. * * @param \Smile\ElasticsuiteTracker\Api\EventQueueInterface $eventQueue Pending events queue. - * @param \Smile\ElasticsuiteTracker\Api\SessionIndexInterface $eventIndex Event index. - * @param \Smile\ElasticsuiteTracker\Api\EventIndexInterface $sessionIndex Session index. + * @param \Smile\ElasticsuiteTracker\Api\EventIndexInterface $eventIndex Event index. + * @param \Smile\ElasticsuiteTracker\Api\SessionIndexInterface $sessionIndex Session index. * @param integer $chunkSize Size of the chunk of events to index. */ public function __construct( diff --git a/src/module-elasticsuite-tracker/Model/ResourceModel/EventQueue.php b/src/module-elasticsuite-tracker/Model/ResourceModel/EventQueue.php index a531e8136..e097315d2 100644 --- a/src/module-elasticsuite-tracker/Model/ResourceModel/EventQueue.php +++ b/src/module-elasticsuite-tracker/Model/ResourceModel/EventQueue.php @@ -249,8 +249,8 @@ protected function isEventInvalid($data) if (array_key_exists('session', $data)) { if (array_key_exists('uid', $data['session']) && array_key_exists('vid', $data['session'])) { $isEventInvalid = false; - $sessionUid = trim($data['session']['uid']); - $sessionVid = trim($data['session']['vid']); + $sessionUid = trim($data['session']['uid'] ?? ''); + $sessionVid = trim($data['session']['vid'] ?? ''); if (empty($sessionUid) || ("null" === $sessionUid)) { $isEventInvalid = true; } diff --git a/src/module-elasticsuite-tracker/view/frontend/templates/config.phtml b/src/module-elasticsuite-tracker/view/frontend/templates/config.phtml index c13adcb4c..9867de89a 100644 --- a/src/module-elasticsuite-tracker/view/frontend/templates/config.phtml +++ b/src/module-elasticsuite-tracker/view/frontend/templates/config.phtml @@ -38,7 +38,7 @@ if ($block->isEnabled()) { $userConsentScript = $this->escapeJsQuote($block->getUserConsentScript()); $userConsentConfig = $jsonHelper->jsonEncode($block->getUserConsentConfig()); - $scriptString = "//isEnabled()) { $scriptString .= "}"; $scriptString .= "});"; $scriptString .= "}catch(err){;}\n"; - $scriptString .= "//]]>\n"; echo /* @noEscape */ $secureRenderer->renderTag('script', [], $scriptString, false); } diff --git a/src/module-elasticsuite-virtual-category/Plugin/Catalog/Category/SaveProductsPositions.php b/src/module-elasticsuite-virtual-category/Plugin/Catalog/Category/SaveProductsPositions.php index 331ac5220..f7b3d9df2 100644 --- a/src/module-elasticsuite-virtual-category/Plugin/Catalog/Category/SaveProductsPositions.php +++ b/src/module-elasticsuite-virtual-category/Plugin/Catalog/Category/SaveProductsPositions.php @@ -26,6 +26,11 @@ */ class SaveProductsPositions extends AbstractIndexerPlugin { + /** + * @var \Magento\Framework\Message\ManagerInterface + */ + protected $messageManager; + /** * @var \Magento\Framework\Json\Helper\Data */ @@ -44,17 +49,20 @@ class SaveProductsPositions extends AbstractIndexerPlugin * @param \Smile\ElasticsuiteVirtualCategory\Model\ResourceModel\Category\Product\Position $saveHandler Product position * save handler. * @param \Magento\Framework\Json\Helper\Data $jsonHelper JSON Helper. + * @param \Magento\Framework\Message\ManagerInterface $messageManager Message Manager. */ public function __construct( \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry, FullIndexer $fullIndexer, \Smile\ElasticsuiteVirtualCategory\Model\ResourceModel\Category\Product\Position $saveHandler, - \Magento\Framework\Json\Helper\Data $jsonHelper + \Magento\Framework\Json\Helper\Data $jsonHelper, + \Magento\Framework\Message\ManagerInterface $messageManager ) { parent::__construct($indexerRegistry, $fullIndexer); $this->jsonHelper = $jsonHelper; $this->saveHandler = $saveHandler; + $this->messageManager = $messageManager; } /** @@ -145,17 +153,23 @@ private function getAffectedProductIds($category) * * @param \Magento\Catalog\Model\Category $category Category * - * @return array + * @return $this */ private function unserializeProductPositions(\Magento\Catalog\Model\Category $category) { + // Get product positions from the category. $productPositions = $category->getSortedProducts() ? $category->getSortedProducts() : []; if (is_string($productPositions)) { try { $productPositions = $this->jsonHelper->jsonDecode($productPositions); } catch (\Exception $e) { - $productPositions = []; + $this->messageManager->addWarningMessage( + __('Something went wrong while saving your product positions, they have been switched back to their last known state.') + ); + + // Fallback to the last known valid product positions. + $productPositions = $this->saveHandler->getProductPositionsByCategory($category); } }