Skip to content

Commit 7e31850

Browse files
authored
Merge pull request #4923 from magento-honey-badgers/MC-21694-bucket
[honey] MC-21694: Boolean attributes show "_bucket" in graphql search aggregations
2 parents 7312e56 + 3a3e573 commit 7e31850

File tree

9 files changed

+331
-7
lines changed

9 files changed

+331
-7
lines changed

app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/AttributeOptionProvider.php

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@ public function __construct(ResourceConnection $resourceConnection)
4141
* Get option data. Return list of attributes with option data
4242
*
4343
* @param array $optionIds
44+
* @param array $attributeCodes
4445
* @return array
4546
* @throws \Zend_Db_Statement_Exception
4647
*/
47-
public function getOptions(array $optionIds): array
48+
public function getOptions(array $optionIds, array $attributeCodes = []): array
4849
{
4950
if (!$optionIds) {
5051
return [];
@@ -60,20 +61,28 @@ public function getOptions(array $optionIds): array
6061
'attribute_label' => 'a.frontend_label',
6162
]
6263
)
63-
->joinInner(
64+
->joinLeft(
6465
['options' => $this->resourceConnection->getTableName('eav_attribute_option')],
6566
'a.attribute_id = options.attribute_id',
6667
[]
6768
)
68-
->joinInner(
69+
->joinLeft(
6970
['option_value' => $this->resourceConnection->getTableName('eav_attribute_option_value')],
7071
'options.option_id = option_value.option_id',
7172
[
7273
'option_label' => 'option_value.value',
7374
'option_id' => 'option_value.option_id',
7475
]
75-
)
76-
->where('option_value.option_id IN (?)', $optionIds);
76+
);
77+
78+
$select->where('option_value.option_id IN (?)', $optionIds);
79+
80+
if (!empty($attributeCodes)) {
81+
$select->orWhere(
82+
'a.attribute_code in (?) AND a.frontend_input = \'boolean\'',
83+
$attributeCodes
84+
);
85+
}
7786

7887
return $this->formatResult($select);
7988
}

app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,9 @@ private function isBucketEmpty(?BucketInterface $bucket): bool
139139
private function getAttributeOptions(AggregationInterface $aggregation): array
140140
{
141141
$attributeOptionIds = [];
142+
$attributes = [];
142143
foreach ($this->getAttributeBuckets($aggregation) as $bucket) {
144+
$attributes[] = \preg_replace('~_bucket$~', '', $bucket->getName());
143145
$attributeOptionIds[] = \array_map(
144146
function (AggregationValueInterface $value) {
145147
return $value->getValue();
@@ -152,6 +154,6 @@ function (AggregationValueInterface $value) {
152154
return [];
153155
}
154156

155-
return $this->attributeOptionProvider->getOptions(\array_merge(...$attributeOptionIds));
157+
return $this->attributeOptionProvider->getOptions(\array_merge(...$attributeOptionIds), $attributes);
156158
}
157159
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GraphQl\Catalog;
9+
10+
use Magento\TestFramework\Helper\Bootstrap;
11+
use Magento\TestFramework\TestCase\GraphQlAbstract;
12+
13+
class ProductSearchAggregationsTest extends GraphQlAbstract
14+
{
15+
/**
16+
* @magentoApiDataFixture Magento/Catalog/_files/products_with_boolean_attribute.php
17+
*/
18+
public function testAggregationBooleanAttribute()
19+
{
20+
$this->reindex();
21+
22+
$skus= '"search_product_1", "search_product_2", "search_product_3", "search_product_4" ,"search_product_5"';
23+
$query = <<<QUERY
24+
{
25+
products(filter: {sku: {in: [{$skus}]}}){
26+
items{
27+
id
28+
sku
29+
name
30+
}
31+
aggregations{
32+
label
33+
attribute_code
34+
count
35+
options{
36+
label
37+
value
38+
count
39+
}
40+
}
41+
}
42+
}
43+
QUERY;
44+
45+
$result = $this->graphQlQuery($query);
46+
47+
$this->assertArrayNotHasKey('errors', $result);
48+
$this->assertArrayHasKey('items', $result['products']);
49+
$this->assertCount(5, $result['products']['items']);
50+
$this->assertArrayHasKey('aggregations', $result['products']);
51+
52+
$booleanAggregation = array_filter(
53+
$result['products']['aggregations'],
54+
function ($a) {
55+
return $a['attribute_code'] == 'boolean_attribute';
56+
}
57+
);
58+
$this->assertNotEmpty($booleanAggregation);
59+
$booleanAggregation = reset($booleanAggregation);
60+
$this->assertEquals('Boolean Attribute', $booleanAggregation['label']);
61+
$this->assertEquals('boolean_attribute', $booleanAggregation['attribute_code']);
62+
$this->assertContains(['label' => '1', 'value'=> '1', 'count' => '3'], $booleanAggregation['options']);
63+
64+
$this->markTestIncomplete('MC-22184: Elasticsearch returns incorrect aggregation options for booleans');
65+
$this->assertEquals(2, $booleanAggregation['count']);
66+
$this->assertCount(2, $booleanAggregation['options']);
67+
$this->assertContains(['label' => '0', 'value'=> '0', 'count' => '2'], $booleanAggregation['options']);
68+
}
69+
70+
/**
71+
* Reindex
72+
*
73+
* @throws \Magento\Framework\Exception\LocalizedException
74+
*/
75+
private function reindex()
76+
{
77+
$appDir = dirname(Bootstrap::getInstance()->getAppTempDir());
78+
// phpcs:ignore Magento2.Security.InsecureFunction
79+
exec("php -f {$appDir}/bin/magento indexer:reindex");
80+
}
81+
}

dev/tests/api-functional/testsuite/Magento/GraphQl/IntrospectionQueryTest.php

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,124 @@ public function testIntrospectionQuery()
5656
$this->assertArrayHasKey('__schema', $this->graphQlQuery($query));
5757
}
5858

59+
/**
60+
* Tests that Introspection is allowed by default
61+
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
62+
*/
63+
public function testIntrospectionQueryWithOnlySchema()
64+
{
65+
$query
66+
= <<<QUERY
67+
{
68+
__schema {
69+
queryType { name }
70+
types{
71+
...FullType
72+
}
73+
}
74+
}
75+
fragment FullType on __Type{
76+
name
77+
kind
78+
fields(includeDeprecated:true){
79+
name
80+
args{
81+
...InputValue
82+
}
83+
}
84+
}
85+
86+
fragment TypeRef on __Type {
87+
kind
88+
name
89+
ofType{
90+
kind
91+
name
92+
}
93+
}
94+
fragment InputValue on __InputValue {
95+
name
96+
description
97+
type { ...TypeRef }
98+
defaultValue
99+
}
100+
QUERY;
101+
$this->assertArrayHasKey('__schema', $this->graphQlQuery($query));
102+
$response = $this->graphQlQuery($query);
103+
104+
$query
105+
= <<<QUERY
106+
query IntrospectionQuery {
107+
__schema {
108+
queryType { name }
109+
types{
110+
...FullType
111+
}
112+
}
113+
}
114+
fragment FullType on __Type{
115+
name
116+
kind
117+
fields(includeDeprecated:true){
118+
name
119+
args{
120+
...InputValue
121+
}
122+
}
123+
}
124+
125+
fragment TypeRef on __Type {
126+
kind
127+
name
128+
ofType{
129+
kind
130+
name
131+
}
132+
}
133+
fragment InputValue on __InputValue {
134+
name
135+
description
136+
type { ...TypeRef }
137+
defaultValue
138+
}
139+
QUERY;
140+
$this->assertArrayHasKey('__schema', $this->graphQlQuery($query));
141+
$responseFields = $this->graphQlQuery($query);
142+
$this->assertResponseFields($response, $responseFields);
143+
$this->assertEquals($responseFields, $response);
144+
}
145+
146+
/**
147+
* Tests that Introspection is allowed by default
148+
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
149+
*/
150+
public function testIntrospectionQueryWithOnlyType()
151+
{
152+
$query
153+
= <<<QUERY
154+
{
155+
__type(name:"Query")
156+
{
157+
name
158+
kind
159+
fields(includeDeprecated:true){
160+
name
161+
type{
162+
kind
163+
name
164+
}
165+
description
166+
isDeprecated
167+
deprecationReason
168+
}
169+
}
170+
}
171+
QUERY;
172+
$this->assertArrayHasKey('__type', $this->graphQlQuery($query));
173+
$response = $this->graphQlQuery($query);
174+
$this->assertNotEmpty($response['__type']['fields']);
175+
}
176+
59177
/**
60178
* Tests that Introspection Query with deprecated annotations on enum values, fields are read.
61179
*/
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
use Magento\Catalog\Setup\CategorySetup;
8+
use Magento\Eav\Api\AttributeRepositoryInterface;
9+
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
10+
use Magento\TestFramework\Helper\Bootstrap;
11+
12+
$objectManager = Bootstrap::getObjectManager();
13+
/** @var AttributeRepositoryInterface $attributeRepository */
14+
$attributeRepository = $objectManager->get(AttributeRepositoryInterface::class);
15+
/** @var Attribute $attribute */
16+
$attribute = $objectManager->create(Attribute::class);
17+
/** @var $installer \Magento\Catalog\Setup\CategorySetup */
18+
$installer = $objectManager->create(CategorySetup::class);
19+
20+
$attribute->setData(
21+
[
22+
'attribute_code' => 'boolean_attribute',
23+
'entity_type_id' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID,
24+
'is_global' => 0,
25+
'is_user_defined' => 1,
26+
'frontend_input' => 'boolean',
27+
'is_unique' => 0,
28+
'is_required' => 0,
29+
'is_searchable' => 1,
30+
'is_visible_in_advanced_search' => 1,
31+
'is_comparable' => 0,
32+
'is_filterable' => 1,
33+
'is_filterable_in_search' => 1,
34+
'is_used_for_promo_rules' => 0,
35+
'is_html_allowed_on_front' => 1,
36+
'is_visible_on_front' => 1,
37+
'used_in_product_listing' => 1,
38+
'used_for_sort_by' => 0,
39+
'frontend_label' => ['Boolean Attribute'],
40+
'backend_type' => 'int'
41+
]
42+
);
43+
44+
$attributeRepository->save($attribute);
45+
46+
/* Assign attribute to attribute set */
47+
$installer->addAttributeToGroup('catalog_product', 'Default', 'Attributes', $attribute->getId());
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
use Magento\Framework\Registry;
8+
use Magento\TestFramework\Helper\Bootstrap;
9+
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
10+
11+
$objectManager = Bootstrap::getObjectManager();
12+
13+
$registry = $objectManager->get(Registry::class);
14+
$registry->unregister('isSecureArea');
15+
$registry->register('isSecureArea', true);
16+
/** @var Attribute $attribute */
17+
$attribute = $objectManager->create(Attribute::class);
18+
$attribute->load('boolean_attribute', 'attribute_code');
19+
$attribute->delete();
20+
$registry->unregister('isSecureArea');
21+
$registry->register('isSecureArea', false);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
use Magento\Catalog\Api\ProductRepositoryInterface;
8+
use Magento\TestFramework\Helper\CacheCleaner;
9+
10+
require_once __DIR__ . '/products_for_search.php';
11+
require_once __DIR__ . '/product_boolean_attribute.php';
12+
13+
$productRepository = $objectManager->get(ProductRepositoryInterface::class);
14+
15+
$yesIds = [101, 102, 104];
16+
$noIds = [103, 105];
17+
18+
foreach ($yesIds as $id) {
19+
$product = $productRepository->getById($id);
20+
$product->setBooleanAttribute(1);
21+
$productRepository->save($product);
22+
}
23+
foreach ($noIds as $id) {
24+
$product = $productRepository->getById($id);
25+
$product->setBooleanAttribute(0);
26+
$productRepository->save($product);
27+
}
28+
CacheCleaner::cleanAll();
29+
/** @var \Magento\Indexer\Model\Indexer\Collection $indexerCollection */
30+
$indexerCollection = $objectManager->get(\Magento\Indexer\Model\Indexer\Collection::class);
31+
$indexerCollection->load();
32+
/** @var \Magento\Indexer\Model\Indexer $indexer */
33+
foreach ($indexerCollection->getItems() as $indexer) {
34+
$indexer->reindexAll();
35+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
require_once __DIR__ . '/products_for_search_rollback.php';
8+
require_once __DIR__ . '/product_boolean_attribute_rollback.php';

0 commit comments

Comments
 (0)