Skip to content

Commit 26fb7a8

Browse files
author
Eric Bohanon
committed
MAGETWO-82674: GraphQL Full Text Search
- Performance improvements
1 parent 77ef04e commit 26fb7a8

File tree

10 files changed

+407
-56
lines changed

10 files changed

+407
-56
lines changed

app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
*/
88
namespace Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute;
99

10+
use Magento\Catalog\Model\Product;
1011
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
12+
use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute as Model;
1113
use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute;
1214
use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable as ConfigurableResource;
1315
use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
@@ -39,6 +41,8 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
3941
* Product instance
4042
*
4143
* @var \Magento\Catalog\Model\Product
44+
* @deprecated Now collection supports fetching options for multiple products. This field will be set to first
45+
* element of products array.
4246
*/
4347
protected $_product;
4448

@@ -68,6 +72,11 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
6872
*/
6973
private $metadataPool;
7074

75+
/**
76+
* @var \Magento\Catalog\Model\Product[]
77+
*/
78+
private $products;
79+
7180
/**
7281
* @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory
7382
* @param \Psr\Log\LoggerInterface $logger
@@ -122,9 +131,10 @@ protected function _construct()
122131
*/
123132
public function setProductFilter($product)
124133
{
125-
$metadata = $this->getMetadataPool()->getMetadata(ProductInterface::class);
126-
$this->_product = $product;
127-
return $this->addFieldToFilter('product_id', $product->getData($metadata->getLinkField()));
134+
$this->products[] = $product;
135+
$this->_product = reset($this->products);
136+
137+
return $this;
128138
}
129139

130140
/**
@@ -156,7 +166,24 @@ public function orderByPosition($dir = self::SORT_ORDER_ASC)
156166
*/
157167
public function getStoreId()
158168
{
159-
return (int)$this->_product->getStoreId();
169+
return reset($this->products)->getStoreId();
170+
}
171+
172+
/**
173+
* Add product ids to `in` filter before load
174+
*
175+
* @return $this
176+
* @throws \Exception
177+
*/
178+
protected function _beforeLoad()
179+
{
180+
parent::_beforeLoad();
181+
$metadata = $this->getMetadataPool()->getMetadata(ProductInterface::class);
182+
$productIds = [];
183+
foreach ($this->products as $product) {
184+
$productIds[] = $product->getData($metadata->getLinkField());
185+
}
186+
return $this->addFieldToFilter('product_id', ['in (?)' => $productIds]);
160187
}
161188

162189
/**
@@ -186,16 +213,34 @@ protected function _afterLoad()
186213
*/
187214
protected function _addProductAttributes()
188215
{
216+
/** @var Model $item */
189217
foreach ($this->_items as $item) {
190218
$productAttribute = $this->getProductType()->getAttributeById(
191219
$item->getAttributeId(),
192-
$this->getProduct()
220+
$this->getAttributeParentProduct($item)
193221
);
194222
$item->setProductAttribute($productAttribute);
195223
}
196224
return $this;
197225
}
198226

227+
/**
228+
* Get product that has given attribute
229+
*
230+
* @param Model $attribute
231+
* @return Product
232+
*/
233+
private function getAttributeParentProduct($attribute)
234+
{
235+
$targetProduct = null;
236+
foreach ($this->products as $product) {
237+
if ($product->getId() === $attribute->getProductId()) {
238+
$targetProduct = $product;
239+
}
240+
}
241+
return $targetProduct ?: reset($this->products);
242+
}
243+
199244
/**
200245
* Add Associated Product Filters (From Product Type Instance)
201246
*
@@ -204,10 +249,12 @@ protected function _addProductAttributes()
204249
*/
205250
public function _addAssociatedProductFilters()
206251
{
207-
$this->getProductType()->getUsedProducts(
208-
$this->getProduct(),
209-
$this->getColumnValues('attribute_id') // Filter associated products
210-
);
252+
foreach ($this->products as $product) {
253+
$this->getProductType()->getUsedProducts(
254+
$product,
255+
$this->getColumnValues('attribute_id') // Filter associated products
256+
);
257+
}
211258
return $this;
212259
}
213260

@@ -261,6 +308,7 @@ protected function loadOptions()
261308
{
262309
/** @var ConfigurableResource $configurableResource */
263310
$configurableResource = $this->getConfigurableResource();
311+
/** @var Model $item */
264312
foreach ($this->_items as $item) {
265313
$values = [];
266314

@@ -269,9 +317,7 @@ protected function loadOptions()
269317
$itemId = $item->getId();
270318
$options = $configurableResource->getAttributeOptions(
271319
$productAttribute,
272-
$this->getProduct()->getData(
273-
$this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField()
274-
)
320+
$item->getProductId()
275321
);
276322
foreach ($options as $option) {
277323
$values[$itemId . ':' . $option['value_index']] = [
@@ -305,16 +351,6 @@ protected function getIncludedOptions(array $usedProducts, AbstractAttribute $pr
305351
return $options;
306352
}
307353

308-
/**
309-
* Retrieve product instance
310-
*
311-
* @return \Magento\Catalog\Model\Product
312-
*/
313-
private function getProduct()
314-
{
315-
return $this->_product;
316-
}
317-
318354
/**
319355
* @inheritdoc
320356
* @since 100.0.6

app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Product/Collection.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
2323
*/
2424
protected $_linkTable;
2525

26+
/**
27+
* @var \Magento\Catalog\Model\Product[]
28+
*/
29+
private $products;
30+
2631
/**
2732
* Assign link table name
2833
*
@@ -59,9 +64,26 @@ protected function _initSelect()
5964
*/
6065
public function setProductFilter($product)
6166
{
67+
$this->products[] = $product;
68+
return $this;
69+
}
70+
71+
/**
72+
* Add parent ids to `in` filter before load.
73+
*
74+
* @return $this
75+
*/
76+
protected function _beforeLoad()
77+
{
78+
parent::_beforeLoad();
6279
$metadata = $this->getProductEntityMetadata();
80+
$parentIds = [];
81+
foreach ($this->products as $product) {
82+
$parentIds[] = $product->getData($metadata->getLinkField());
83+
}
84+
85+
$this->getSelect()->where('link_table.parent_id in (?)', $parentIds);
6386

64-
$this->getSelect()->where('link_table.parent_id = ?', $product->getData($metadata->getLinkField()));
6587
return $this;
6688
}
6789

app/code/Magento/GraphQl/Controller/GraphQl.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
namespace Magento\GraphQl\Controller;
88

99
use Magento\Framework\App\FrontControllerInterface;
10+
use Magento\Framework\App\Request\Http;
1011
use Magento\Framework\App\RequestInterface;
1112
use Magento\Framework\App\ResponseInterface;
1213
use Magento\Framework\Exception\LocalizedException;
@@ -81,6 +82,7 @@ public function __construct(
8182
public function dispatch(RequestInterface $request)
8283
{
8384
try {
85+
/** @var $request Http */
8486
if ($request->getHeader('Content-Type')
8587
&& strpos($request->getHeader('Content-Type'), 'application/json') !== false
8688
) {

app/code/Magento/GraphQlCatalog/Model/Resolver/Products/DataProvider/Product.php

Lines changed: 90 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@
77
namespace Magento\GraphQlCatalog\Model\Resolver\Products\DataProvider;
88

99
use Magento\Catalog\Api\ProductRepositoryInterface;
10+
use Magento\Framework\Api\SearchCriteriaInterface;
11+
use Magento\Framework\Data\SearchResultInterface;
1012
use Magento\Framework\Exception\NoSuchEntityException;
1113
use Magento\Framework\Serialize\SerializerInterface;
1214
use Magento\Framework\Webapi\ServiceOutputProcessor;
13-
use Magento\GraphQlCatalog\Model\Resolver\Products\DataProvider\MediaGalleryEntries;
15+
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
16+
use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface;
17+
use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
18+
use Magento\Catalog\Api\Data\ProductSearchResultsInterfaceFactory;
19+
use Magento\GraphQl\Model\EntityAttributeList;
1420

1521
/**
1622
* Product field data provider, used for GraphQL resolver processing.
@@ -37,22 +43,70 @@ class Product
3743
*/
3844
private $jsonSerializer;
3945

46+
/**
47+
* @var CollectionFactory
48+
*/
49+
private $collectionFactory;
50+
51+
/**
52+
* @var JoinProcessorInterface
53+
*/
54+
private $joinProcessor;
55+
56+
/**
57+
* @var CollectionProcessorInterface
58+
*/
59+
private $collectionProcessor;
60+
61+
/**
62+
* @var ProductSearchResultsInterfaceFactory
63+
*/
64+
private $searchResultsFactory;
65+
66+
/**
67+
* @var EntityAttributeList
68+
*/
69+
private $entityAttributeList;
70+
71+
/**
72+
* @var \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection
73+
*/
74+
private $configurable;
75+
4076
/**
4177
* @param ProductRepositoryInterface $productRepository
4278
* @param ServiceOutputProcessor $serviceOutputProcessor
4379
* @param MediaGalleryEntries $mediaGalleryResolver
4480
* @param SerializerInterface $jsonSerializer
81+
* @param CollectionFactory $collectionFactory
82+
* @param JoinProcessorInterface $joinProcessor
83+
* @param CollectionProcessorInterface $collectionProcessor
84+
* @param ProductSearchResultsInterfaceFactory $searchResultsFactory
85+
* @param EntityAttributeList $entityAttributeList
86+
* @param \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection $collection
4587
*/
4688
public function __construct(
4789
ProductRepositoryInterface $productRepository,
4890
ServiceOutputProcessor $serviceOutputProcessor,
4991
MediaGalleryEntries $mediaGalleryResolver,
50-
SerializerInterface $jsonSerializer
92+
SerializerInterface $jsonSerializer,
93+
CollectionFactory $collectionFactory,
94+
JoinProcessorInterface $joinProcessor,
95+
CollectionProcessorInterface $collectionProcessor,
96+
ProductSearchResultsInterfaceFactory $searchResultsFactory,
97+
EntityAttributeList $entityAttributeList,
98+
\Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection $collection
5199
) {
52100
$this->productRepository = $productRepository;
53101
$this->serviceOutputProcessor = $serviceOutputProcessor;
54102
$this->mediaGalleryResolver = $mediaGalleryResolver;
55103
$this->jsonSerializer = $jsonSerializer;
104+
$this->collectionFactory = $collectionFactory;
105+
$this->joinProcessor = $joinProcessor;
106+
$this->collectionProcessor = $collectionProcessor;
107+
$this->searchResultsFactory = $searchResultsFactory;
108+
$this->entityAttributeList = $entityAttributeList;
109+
$this->configurable = $collection;
56110
}
57111

58112
/**
@@ -64,7 +118,7 @@ public function __construct(
64118
public function getProduct(string $sku)
65119
{
66120
try {
67-
$productObject = $this->productRepository->get($sku, false, null, true);
121+
$productObject = $this->productRepository->get($sku);
68122
} catch (NoSuchEntityException $e) {
69123
// No error should be thrown, null result should be returned
70124
return null;
@@ -95,8 +149,9 @@ public function getProductById(int $productId)
95149
* @param \Magento\Catalog\Api\Data\ProductInterface $productObject
96150
* @return array|null
97151
*/
98-
private function processProduct(\Magento\Catalog\Api\Data\ProductInterface $productObject)
152+
public function processProduct(\Magento\Catalog\Api\Data\ProductInterface $productObject)
99153
{
154+
// $productObject = $this->productRepository->get($productObject->getSku());
100155
$product = $this->serviceOutputProcessor->process(
101156
$productObject,
102157
ProductRepositoryInterface::class,
@@ -133,29 +188,43 @@ private function processProduct(\Magento\Catalog\Api\Data\ProductInterface $prod
133188
$product['media_gallery_entries']
134189
= $this->mediaGalleryResolver->getMediaGalleryEntries($productObject->getSku());
135190

136-
if (isset($product['configurable_product_links'])) {
137-
$product['configurable_product_links'] = $this
138-
->resolveConfigurableProductLinks($product['configurable_product_links']);
139-
}
140-
141191
return $product;
142192
}
143193

144194
/**
145-
* Resolve links for configurable product into simple products
195+
* Gets list of product data with full data set
146196
*
147-
* @param int[]
148-
* @return array
197+
* @param SearchCriteriaInterface $searchCriteria
198+
* @return SearchResultInterface
149199
*/
150-
private function resolveConfigurableProductLinks($configurableProductLinks)
200+
public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria)
151201
{
152-
if (empty($configurableProductLinks)) {
153-
return [];
154-
}
155-
$result = [];
156-
foreach ($configurableProductLinks as $key => $id) {
157-
$result[$key] = $this->getProductById($id);
158-
}
159-
return $result;
202+
/** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */
203+
$collection = $this->collectionFactory->create();
204+
$this->joinProcessor->process($collection);
205+
206+
$collection->addAttributeToSelect('*');
207+
$collection->joinAttribute('status', 'catalog_product/status', 'entity_id', null, 'inner');
208+
$collection->joinAttribute('visibility', 'catalog_product/visibility', 'entity_id', null, 'inner');
209+
210+
$this->collectionProcessor->process($searchCriteria, $collection);
211+
212+
$collection->load();
213+
214+
$collection->addCategoryIds();
215+
$collection->addFinalPrice();
216+
$collection->addMediaGalleryData();
217+
$collection->addMinimalPrice();
218+
$collection->addPriceData();
219+
$collection->addWebsiteNamesToResult();
220+
$collection->addOptionsToResult();
221+
$collection->addTaxPercents();
222+
$collection->addWebsiteNamesToResult();
223+
$searchResult = $this->searchResultsFactory->create();
224+
$searchResult->setSearchCriteria($searchCriteria);
225+
$searchResult->setItems($collection->getItems());
226+
$searchResult->setTotalCount($collection->getSize());
227+
228+
return $searchResult;
160229
}
161230
}

0 commit comments

Comments
 (0)