-
Notifications
You must be signed in to change notification settings - Fork 9.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Fulltext search in the Magento backend to use elasticsearch #38634
Open
digitalrisedorset
wants to merge
5
commits into
magento:2.4-develop
Choose a base branch
from
digitalrisedorset:feature/backend-product-fulltext-search
base: 2.4-develop
Could not load branches
Branch not found: {{ refName }}
Could not load tags
Nothing to show
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
43574ad
Added a new backend search model to query elasticsearch when fulltext…
85c4d12
Added changes to match code standards requirements
digitalrisedorset c2b3f81
Added a method so that the product collection is reordered using the …
digitalrisedorset 87c8702
Added the simple product ids from the result after the full text sear…
digitalrisedorset 727c0bd
Added docblock to comply with code standards
digitalrisedorset File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
204 changes: 204 additions & 0 deletions
204
app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/BackendCollection.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Magento\CatalogSearch\Model\ResourceModel\Fulltext; | ||
|
||
use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; | ||
use Magento\Catalog\Model\Indexer\Product\Flat\State; | ||
use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; | ||
use Magento\Catalog\Model\Product\Gallery\ReadHandler as GalleryReadHandler; | ||
use Magento\Catalog\Model\Product\OptionFactory; | ||
use Magento\Catalog\Model\ResourceModel\Category; | ||
use Magento\Catalog\Model\ResourceModel\Helper; | ||
use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; | ||
use Magento\Catalog\Model\ResourceModel\Product\Gallery; | ||
use Magento\Catalog\Model\ResourceModel\Url; | ||
use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplierFactory; | ||
use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplierInterface; | ||
use Magento\CatalogUrlRewrite\Model\Storage\DbStorage; | ||
use Magento\Customer\Api\GroupManagementInterface; | ||
use Magento\Customer\Model\Session; | ||
use Magento\Eav\Model\Config; | ||
use Magento\Framework\Api\FilterBuilder; | ||
use Magento\Framework\Api\Search\FilterGroupBuilder; | ||
use Magento\Framework\Api\Search\SearchCriteriaBuilder; | ||
use Magento\Framework\Api\Search\SearchResultInterface; | ||
use Magento\Framework\Api\SearchCriteriaInterface; | ||
use Magento\Framework\Api\SortOrderBuilder; | ||
use Magento\Framework\App\Config\ScopeConfigInterface; | ||
use Magento\Framework\App\ResourceConnection; | ||
use Magento\Framework\Data\Collection\Db\FetchStrategyInterface; | ||
use Magento\Framework\Data\Collection\EntityFactory; | ||
use Magento\Framework\DB\Adapter\AdapterInterface; | ||
use Magento\Framework\EntityManager\MetadataPool; | ||
use Magento\Framework\Event\ManagerInterface; | ||
use Magento\Framework\Indexer\DimensionFactory; | ||
use Magento\Framework\Module\Manager; | ||
use Magento\Framework\Search\Search; | ||
use Magento\Framework\Stdlib\DateTime; | ||
use Magento\Framework\Stdlib\DateTime\TimezoneInterface; | ||
use Magento\Framework\Validator\UniversalFactory; | ||
use Magento\Store\Model\StoreManagerInterface; | ||
use Psr\Log\LoggerInterface; | ||
|
||
class BackendCollection extends \Magento\Catalog\Model\ResourceModel\Product\Collection | ||
{ | ||
/** | ||
* @var FilterBuilder | ||
*/ | ||
private FilterBuilder $filterBuilder; | ||
/** | ||
* @var SortOrderBuilder | ||
*/ | ||
private SortOrderBuilder $sortOrderBuilder; | ||
/** | ||
* @var SearchCriteriaBuilder | ||
*/ | ||
private SearchCriteriaBuilder $searchCriteriaBuilder; | ||
/** | ||
* @var SearchResultApplierFactory | ||
*/ | ||
private SearchResultApplierFactory $searchResultApplierFactory; | ||
|
||
/** | ||
* @param EntityFactory $entityFactory | ||
* @param LoggerInterface $logger | ||
* @param FetchStrategyInterface $fetchStrategy | ||
* @param ManagerInterface $eventManager | ||
* @param Config $eavConfig | ||
* @param ResourceConnection $resource | ||
* @param \Magento\Eav\Model\EntityFactory $eavEntityFactory | ||
* @param Helper $resourceHelper | ||
* @param UniversalFactory $universalFactory | ||
* @param StoreManagerInterface $storeManager | ||
* @param Manager $moduleManager | ||
* @param State $catalogProductFlatState | ||
* @param ScopeConfigInterface $scopeConfig | ||
* @param OptionFactory $productOptionFactory | ||
* @param Url $catalogUrl | ||
* @param TimezoneInterface $localeDate | ||
* @param Session $customerSession | ||
* @param DateTime $dateTime | ||
* @param GroupManagementInterface $groupManagement | ||
* @param FilterBuilder $filterBuilder | ||
* @param SortOrderBuilder $sortOrderBuilder | ||
* @param FilterGroupBuilder $filterGroupBuilder | ||
* @param SearchCriteriaBuilder $searchCriteriaBuilder | ||
* @param SearchResultApplierFactory $searchResultApplierFactory | ||
* @param Search $search | ||
* @param AdapterInterface|null $connection | ||
* @param ProductLimitationFactory|null $productLimitationFactory | ||
* @param MetadataPool|null $metadataPool | ||
* @param TableMaintainer|null $tableMaintainer | ||
* @param PriceTableResolver|null $priceTableResolver | ||
* @param DimensionFactory|null $dimensionFactory | ||
* @param Category|null $categoryResourceModel | ||
* @param DbStorage|null $urlFinder | ||
* @param GalleryReadHandler|null $productGalleryReadHandler | ||
* @param Gallery|null $mediaGalleryResource | ||
*/ | ||
public function __construct( | ||
\Magento\Framework\Data\Collection\EntityFactory $entityFactory, | ||
\Psr\Log\LoggerInterface $logger, | ||
\Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, | ||
\Magento\Framework\Event\ManagerInterface $eventManager, | ||
\Magento\Eav\Model\Config $eavConfig, | ||
\Magento\Framework\App\ResourceConnection $resource, | ||
\Magento\Eav\Model\EntityFactory $eavEntityFactory, | ||
\Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, | ||
\Magento\Framework\Validator\UniversalFactory $universalFactory, | ||
\Magento\Store\Model\StoreManagerInterface $storeManager, | ||
\Magento\Framework\Module\Manager $moduleManager, | ||
\Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, | ||
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, | ||
\Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, | ||
\Magento\Catalog\Model\ResourceModel\Url $catalogUrl, | ||
\Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, | ||
\Magento\Customer\Model\Session $customerSession, | ||
\Magento\Framework\Stdlib\DateTime $dateTime, | ||
GroupManagementInterface $groupManagement, | ||
FilterBuilder $filterBuilder, | ||
SortOrderBuilder $sortOrderBuilder, | ||
FilterGroupBuilder $filterGroupBuilder, | ||
SearchCriteriaBuilder $searchCriteriaBuilder, | ||
SearchResultApplierFactory $searchResultApplierFactory, | ||
private readonly Search $search, | ||
\Magento\Framework\DB\Adapter\AdapterInterface $connection = null, | ||
ProductLimitationFactory $productLimitationFactory = null, | ||
MetadataPool $metadataPool = null, | ||
TableMaintainer $tableMaintainer = null, | ||
PriceTableResolver $priceTableResolver = null, | ||
DimensionFactory $dimensionFactory = null, | ||
Category $categoryResourceModel = null, | ||
DbStorage $urlFinder = null, | ||
GalleryReadHandler $productGalleryReadHandler = null, | ||
\Magento\Catalog\Model\ResourceModel\Product\Gallery $mediaGalleryResource = null | ||
) { | ||
parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $eavConfig, $resource, $eavEntityFactory, $resourceHelper, $universalFactory, $storeManager, $moduleManager, $catalogProductFlatState, $scopeConfig, $productOptionFactory, $catalogUrl, $localeDate, $customerSession, $dateTime, $groupManagement, $connection, $productLimitationFactory, $metadataPool, $tableMaintainer, $priceTableResolver, $dimensionFactory, $categoryResourceModel, $urlFinder, $productGalleryReadHandler, $mediaGalleryResource); | ||
$this->filterBuilder = $filterBuilder; | ||
$this->sortOrderBuilder = $sortOrderBuilder; | ||
$this->searchCriteriaBuilder = $searchCriteriaBuilder; | ||
$this->searchResultApplierFactory = $searchResultApplierFactory; | ||
} | ||
|
||
/** | ||
* Add a filter onto the collection | ||
* | ||
* @param string $attribute | ||
* @param mixed $condition | ||
* | ||
* @return void | ||
*/ | ||
public function addFieldToFilter($attribute, $condition = null): void | ||
{ | ||
$this->filterBuilder->setField($attribute); | ||
$this->filterBuilder->setValue($condition); | ||
$this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); | ||
} | ||
|
||
/** | ||
* Setup a search term filter and submit the search to ElasticSearch | ||
* | ||
* @param string $fulltext | ||
* | ||
* @return void | ||
*/ | ||
public function addSearchFilter(string $fulltext): void | ||
{ | ||
$this->addFieldToFilter('search_term', $fulltext); | ||
|
||
$searchCriteria = $this->searchCriteriaBuilder->create(); | ||
$searchCriteria->setRequestName('admin_search_container'); | ||
|
||
$sort = $this->sortOrderBuilder->setField('relevance')->setDescendingDirection()->create(); | ||
$searchCriteria->setSortOrders([$sort]); | ||
$result = $this->search->search($searchCriteria); | ||
$this->getSearchResultApplier($result, $searchCriteria)->apply(); | ||
} | ||
|
||
/** | ||
* Assign the result from ElasticSearch to the collection | ||
* | ||
* @param SearchResultInterface $searchResult | ||
* @param SearchCriteriaInterface $searchCriteria | ||
* | ||
* @return SearchResultApplierInterface | ||
*/ | ||
private function getSearchResultApplier( | ||
SearchResultInterface $searchResult, | ||
SearchCriteriaInterface $searchCriteria | ||
): SearchResultApplierInterface { | ||
$sort = current($searchCriteria->getSortOrders()); | ||
|
||
return $this->searchResultApplierFactory->create( | ||
[ | ||
'collection' => $this, | ||
'searchResult' => $searchResult, | ||
/** This variable sets by serOrder method, but doesn't have a getter method. */ | ||
'orders' => [$sort->getField(), $sort->getDirection()], | ||
'size' => $searchCriteria->getPageSize(), | ||
'currentPage' => $searchCriteria->getPageSize(), | ||
] | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,8 @@ | |
*/ | ||
namespace Magento\CatalogSearch\Ui\DataProvider\Product; | ||
|
||
use Magento\CatalogSearch\Model\ResourceModel\Search\Collection as SearchCollection; | ||
use Magento\CatalogSearch\Model\ResourceModel\Fulltext\BackendCollection; | ||
use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\CollectionFactory as ConfigurableCollectionFactory; | ||
use Magento\Framework\Data\Collection; | ||
use Magento\Ui\DataProvider\AddFilterToCollectionInterface; | ||
|
||
|
@@ -14,19 +15,18 @@ | |
*/ | ||
class AddFulltextFilterToCollection implements AddFilterToCollectionInterface | ||
{ | ||
/** | ||
* Search Collection | ||
* | ||
* @var SearchCollection | ||
*/ | ||
private $searchCollection; | ||
private BackendCollection $backendCollection; | ||
private ConfigurableCollectionFactory $linkCollectionFactory; | ||
|
||
/** | ||
* @param SearchCollection $searchCollection | ||
* @param BackendCollection $backendCollection | ||
*/ | ||
public function __construct(SearchCollection $searchCollection) | ||
{ | ||
$this->searchCollection = $searchCollection; | ||
public function __construct( | ||
BackendCollection $backendCollection, | ||
ConfigurableCollectionFactory $linkCollectionFactory | ||
) { | ||
$this->backendCollection = $backendCollection; | ||
$this->linkCollectionFactory = $linkCollectionFactory; | ||
} | ||
|
||
/** | ||
|
@@ -38,13 +38,30 @@ public function addFilter(Collection $collection, $field, $condition = null) | |
{ | ||
/** @var $collection \Magento\Catalog\Model\ResourceModel\Product\Collection */ | ||
if (isset($condition['fulltext']) && (string)$condition['fulltext'] !== '') { | ||
$this->searchCollection->addBackendSearchFilter($condition['fulltext']); | ||
$productIds = $this->searchCollection->load()->getAllIds(); | ||
$this->backendCollection->addSearchFilter((string) $condition['fulltext']); | ||
$productIds = $this->backendCollection->load()->getAllIds(); | ||
$simpleProductIds = []; | ||
if (empty($productIds)) { | ||
//add dummy id to prevent returning full unfiltered collection | ||
$productIds = -1; | ||
} else { | ||
$simpleProductIds = $this->getSimpleProductIds($this->backendCollection->getItems()); | ||
} | ||
$collection->addIdFilter($productIds); | ||
$collection->addIdFilter(array_merge($simpleProductIds, $productIds)); | ||
} | ||
} | ||
|
||
/** | ||
* @param array $products | ||
* @return array | ||
*/ | ||
private function getSimpleProductIds(array $products): array | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The context with this additional query is that elasticsearch does not store the simple products data from the configurable. This solution is not perfect but it does mean the backend sees the simple products as well as the parents |
||
{ | ||
/** @var \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\Collection $collection */ | ||
$collection = $this->linkCollectionFactory->create(); | ||
$collection->setProductListFilter($products); | ||
$productIds = $collection->load()->getAllIds(); | ||
|
||
return $productIds; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The array_reverse is likely not needed but it is the only way at this time for the product result to be correctly sorted