Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
AdapterTest.php
Go to the documentation of this file.
1 <?php
7 
14 
23 class AdapterTest extends \PHPUnit\Framework\TestCase
24 {
28  private $adapter;
29 
33  private $requestBuilder;
34 
38  protected $objectManager;
39 
44 
45  protected function setUp()
46  {
47  $this->objectManager = Bootstrap::getObjectManager();
48 
50  $converter = $this->objectManager->create(\Magento\Framework\Search\Request\Config\Converter::class);
51 
52  $document = new \DOMDocument();
53  $document->load($this->getRequestConfigPath());
54  $requestConfig = $converter->convert($document);
55 
57  $config = $this->objectManager->create(\Magento\Framework\Search\Request\Config::class);
58  $config->merge($requestConfig);
59 
60  $this->requestBuilder = $this->objectManager->create(
61  \Magento\Framework\Search\Request\Builder::class,
62  ['config' => $config]
63  );
64 
65  $this->adapter = $this->createAdapter();
66 
67  $indexer = $this->objectManager->create(\Magento\Indexer\Model\Indexer::class);
68  $indexer->load('catalogsearch_fulltext');
69  $indexer->reindexAll();
70  }
71 
77  protected function getRequestConfigPath()
78  {
79  return __DIR__ . '/../../_files/requests.xml';
80  }
81 
85  protected function assertPreConditions()
86  {
87  $currentEngine = $this->objectManager->get(EngineResolverInterface::class)->getCurrentSearchEngine();
88  $this->assertEquals($this->searchEngine, $currentEngine);
89  }
90 
94  protected function createAdapter()
95  {
96  return $this->objectManager->create(\Magento\Framework\Search\Adapter\Mysql\Adapter::class);
97  }
98 
102  private function executeQuery()
103  {
105  $queryRequest = $this->requestBuilder->create();
106 
107  $queryResponse = $this->adapter->query($queryRequest);
108 
109  return $queryResponse;
110  }
111 
116  private function assertProductIds($queryResponse, $expectedIds)
117  {
118  $actualIds = [];
119  foreach ($queryResponse as $document) {
121  $actualIds[] = $document->getId();
122  }
123  sort($actualIds);
124  sort($expectedIds);
125  $this->assertEquals($expectedIds, $actualIds);
126  }
127 
132  private function assertOrderedProductIds($queryResponse, $expectedIds)
133  {
134  $actualIds = [];
135  foreach ($queryResponse as $document) {
137  $actualIds[] = $document->getId();
138  }
139  $this->assertEquals($expectedIds, $actualIds);
140  }
141 
145  public function testMatchQuery()
146  {
147  $this->requestBuilder->bind('fulltext_search_query', 'socks');
148  $this->requestBuilder->setRequestName('one_match');
149 
150  $queryResponse = $this->executeQuery();
151 
152  $this->assertEquals(1, $queryResponse->count());
153  }
154 
159  public function testMatchOrderedQuery()
160  {
161  $expectedIds = [8, 7, 6, 5, 2];
162 
163  //Verify that MySql randomized result of equal-weighted results
164  //consistently ordered by entity_id after multiple calls
165  $this->requestBuilder->bind('fulltext_search_query', 'shorts');
166  $this->requestBuilder->setRequestName('one_match');
167  $queryResponse = $this->executeQuery();
168 
169  $this->assertEquals(5, $queryResponse->count());
170  $this->assertOrderedProductIds($queryResponse, $expectedIds);
171  }
172 
176  public function testAggregationsQuery()
177  {
178  $this->requestBuilder->bind('fulltext_search_query', 'peoples');
179  $this->requestBuilder->setRequestName('one_aggregations');
180 
181  $queryResponse = $this->executeQuery();
182 
183  $this->assertEquals(2, $queryResponse->count());
184  $this->assertEquals(
185  ['weight_bucket', 'price_bucket', 'dynamic_price'],
186  $queryResponse->getAggregations()->getBucketNames()
187  );
188  }
189 
193  public function testMatchQueryFilters()
194  {
195  $this->requestBuilder->bind('fulltext_search_query', 'socks');
196  $this->requestBuilder->bind('pidm_from', 11);
197  $this->requestBuilder->bind('pidm_to', 17);
198  $this->requestBuilder->bind('pidsh', 18);
199  $this->requestBuilder->setRequestName('one_match_filters');
200 
201  $queryResponse = $this->executeQuery();
202  $this->assertEquals(1, $queryResponse->count());
203  }
204 
211  {
212  $this->requestBuilder->bind('range_filter_from', 11);
213  $this->requestBuilder->bind('range_filter_to', 17);
214  $this->requestBuilder->setRequestName('range_filter');
215 
216  $queryResponse = $this->executeQuery();
217  $this->assertEquals(3, $queryResponse->count());
218  }
219 
226  {
227  $this->requestBuilder->bind('range_filter_to', 18);
228  $this->requestBuilder->setRequestName('range_filter_without_from_field');
229 
230  $queryResponse = $this->executeQuery();
231  $this->assertEquals(4, $queryResponse->count());
232  }
233 
240  {
241  $this->requestBuilder->bind('range_filter_from', 14);
242  $this->requestBuilder->setRequestName('range_filter_without_to_field');
243 
244  $queryResponse = $this->executeQuery();
245  $this->assertEquals(4, $queryResponse->count());
246  }
247 
253  public function testTermFilter()
254  {
255  $this->requestBuilder->bind('request.price', 18);
256  $this->requestBuilder->setRequestName('term_filter');
257 
258  $queryResponse = $this->executeQuery();
259  $this->assertEquals(1, $queryResponse->count());
260  $this->assertEquals(4, $queryResponse->getIterator()->offsetGet(0)->getId());
261  }
262 
268  public function testTermFilterArray()
269  {
270  $this->requestBuilder->bind('request.price', [17, 18]);
271  $this->requestBuilder->setRequestName('term_filter');
272 
273  $queryResponse = $this->executeQuery();
274  $this->assertEquals(2, $queryResponse->count());
275  }
276 
282  public function testWildcardFilter()
283  {
284  $expectedIds = [1, 3, 5];
285  $this->requestBuilder->bind('wildcard_filter', 're');
286  $this->requestBuilder->setRequestName('one_wildcard');
287 
288  $queryResponse = $this->executeQuery();
289  $this->assertEquals(3, $queryResponse->count());
290  $this->assertProductIds($queryResponse, $expectedIds);
291  }
292 
298  public function testSearchLimit()
299  {
300  $this->requestBuilder->bind('wildcard_filter', 're');
301  $this->requestBuilder->setFrom(1);
302  $this->requestBuilder->setSize(2);
303  $this->requestBuilder->setRequestName('one_wildcard');
304 
305  $queryResponse = $this->executeQuery();
306  $this->assertEquals(2, $queryResponse->count());
307  }
308 
314  public function testBoolFilter()
315  {
316  $expectedIds = [2, 3];
317  $this->requestBuilder->bind('must_range_filter1_from', 13);
318  $this->requestBuilder->bind('must_range_filter1_to', 22);
319  $this->requestBuilder->bind('should_term_filter1', 13);
320  $this->requestBuilder->bind('should_term_filter2', 15);
321  $this->requestBuilder->bind('should_term_filter3', 17);
322  $this->requestBuilder->bind('should_term_filter4', 18);
323  $this->requestBuilder->bind('not_term_filter1', 13);
324  $this->requestBuilder->bind('not_term_filter2', 18);
325  $this->requestBuilder->setRequestName('bool_filter');
326 
327  $queryResponse = $this->executeQuery();
328  $this->assertEquals(count($expectedIds), $queryResponse->count());
329  $this->assertProductIds($queryResponse, $expectedIds);
330  }
331 
338  {
339  $expectedIds = [1];
340  $this->requestBuilder->bind('not_range_filter_from', 14);
341  $this->requestBuilder->bind('not_range_filter_to', 20);
342  $this->requestBuilder->bind('nested_not_term_filter', 13);
343  $this->requestBuilder->setRequestName('bool_filter_with_nested_bool_filter');
344 
345  $queryResponse = $this->executeQuery();
346  $this->assertEquals(count($expectedIds), $queryResponse->count());
347  $this->assertProductIds($queryResponse, $expectedIds);
348  }
349 
356  {
357  $expectedIds = [1, 5];
358  $this->requestBuilder->bind('nested_must_range_filter_from', 14);
359  $this->requestBuilder->bind('nested_must_range_filter_to', 18);
360  $this->requestBuilder->setRequestName('bool_filter_with_range_in_nested_negative_filter');
361 
362  $queryResponse = $this->executeQuery();
363  $this->assertEquals(count($expectedIds), $queryResponse->count());
364  $this->assertProductIds($queryResponse, $expectedIds);
365  }
366 
377  public function testSimpleAdvancedSearch(
378  $nameQuery,
379  $descriptionQuery,
380  $rangeFilter,
381  $expectedRecordsCount
382  ) {
383  $this->requestBuilder->bind('name_query', $nameQuery);
384  $this->requestBuilder->bind('description_query', $descriptionQuery);
385  $this->requestBuilder->bind('request.from_price', $rangeFilter['from']);
386  $this->requestBuilder->bind('request.to_price', $rangeFilter['to']);
387  $this->requestBuilder->setRequestName('advanced_search_test');
388 
389  $queryResponse = $this->executeQuery();
390  $this->assertEquals($expectedRecordsCount, $queryResponse->count());
391  }
392 
396  public function advancedSearchDataProvider()
397  {
398  return [
399  ['white', 'shorts', ['from' => '16', 'to' => '18'], 0],
400  ['white', 'shorts',['from' => '12', 'to' => '18'], 1],
401  ['black', 'tshirts', ['from' => '12', 'to' => '20'], 0],
402  ['shorts', 'green', ['from' => '12', 'to' => '22'], 1],
403  //Search with empty fields/values
404  ['white', ' ', ['from' => '12', 'to' => '22'], 1],
405  [' ', 'green', ['from' => '12', 'to' => '22'], 2]
406  ];
407  }
408 
413  public function testCustomFilterableAttribute()
414  {
416  $attribute = $this->objectManager->get(Attribute::class)
417  ->loadByCode(Product::ENTITY, 'select_attribute');
419  $selectOptions = $this->objectManager
420  ->create(Collection::class)
421  ->setAttributeFilter($attribute->getId());
422 
423  $attribute->loadByCode(Product::ENTITY, 'multiselect_attribute');
425  $multiselectOptions = $this->objectManager
426  ->create(Collection::class)
427  ->setAttributeFilter($attribute->getId());
428 
429  $this->requestBuilder->bind('select_attribute', $selectOptions->getLastItem()->getId());
430  $this->requestBuilder->bind('multiselect_attribute', $multiselectOptions->getLastItem()->getId());
431  $this->requestBuilder->bind('price.from', 98);
432  $this->requestBuilder->bind('price.to', 100);
433  $this->requestBuilder->bind('category_ids', 2);
434  $this->requestBuilder->setRequestName('filterable_custom_attributes');
435  $queryResponse = $this->executeQuery();
436  $this->assertEquals(1, $queryResponse->count());
437  }
438 
445  {
446  return [
447  'quick_search_container' => [
448  'quick_search_container',
449  [
450  // Make sure search uses "should" cause.
451  'search_term' => 'Simple Product',
452  ],
453  ],
454  'advanced_search_container' => [
455  'advanced_search_container',
456  [
457  // Make sure "wildcard" feature works.
458  'sku' => 'simple_product',
459  ]
460  ],
461  'catalog_view_container' => [
462  'catalog_view_container',
463  [
464  'category_ids' => 2
465  ]
466  ]
467  ];
468  }
469 
480  public function testFilterByAttributeValues($requestName, $additionalData)
481  {
483  $attribute = $this->objectManager->get(Attribute::class)
484  ->loadByCode(Product::ENTITY, 'select_attribute_1');
486  $selectOptions1 = $this->objectManager
487  ->create(Collection::class)
488  ->setAttributeFilter($attribute->getId());
489  $attribute->loadByCode(Product::ENTITY, 'select_attribute_2');
491  $selectOptions2 = $this->objectManager
492  ->create(Collection::class)
493  ->setAttributeFilter($attribute->getId());
494  $this->requestBuilder->bind('select_attribute_1', $selectOptions1->getLastItem()->getId());
495  $this->requestBuilder->bind('select_attribute_2', $selectOptions2->getLastItem()->getId());
496  // Binds for specific containers.
497  foreach ($additionalData as $key => $value) {
498  $this->requestBuilder->bind($key, $value);
499  }
500  $this->requestBuilder->setRequestName($requestName);
501  $queryResponse = $this->executeQuery();
502  $this->assertEquals(1, $queryResponse->count());
503  }
504 
514  public function testAdvancedSearchDateField($rangeFilter, $expectedRecordsCount)
515  {
516  $this->requestBuilder->bind('date.from', $rangeFilter['from']);
517  $this->requestBuilder->bind('date.to', $rangeFilter['to']);
518  $this->requestBuilder->setRequestName('advanced_search_date_field');
519 
520  $queryResponse = $this->executeQuery();
521  $this->assertEquals($expectedRecordsCount, $queryResponse->count());
522  }
523 
528  public function testAdvancedSearchCompositeProductWithOutOfStockOption()
529  {
531  $attribute = $this->objectManager->get(Attribute::class)
532  ->loadByCode(Product::ENTITY, 'test_configurable');
534  $selectOptions = $this->objectManager
535  ->create(Collection::class)
536  ->setAttributeFilter($attribute->getId());
537 
538  $visibility = [
539  \Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_SEARCH,
540  \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH,
541  ];
542 
543  $firstOption = $selectOptions->getFirstItem();
544  $firstOptionId = $firstOption->getId();
545  $this->requestBuilder->bind('test_configurable', $firstOptionId);
546  $this->requestBuilder->bind('visibility', $visibility);
547  $this->requestBuilder->setRequestName('filter_out_of_stock_child');
548 
549  $queryResponse = $this->executeQuery();
550  $this->assertEquals(0, $queryResponse->count());
551 
552  $secondOption = $selectOptions->getLastItem();
553  $secondOptionId = $secondOption->getId();
554  $this->requestBuilder->bind('test_configurable', $secondOptionId);
555  $this->requestBuilder->bind('visibility', $visibility);
556  $this->requestBuilder->setRequestName('filter_out_of_stock_child');
557 
558  $queryResponse = $this->executeQuery();
559  $this->assertEquals(1, $queryResponse->count());
560  }
561 
566  public function testAdvancedSearchCompositeProductWithDisabledChild()
567  {
569  $attribute = $this->objectManager->get(Attribute::class)
570  ->loadByCode(Product::ENTITY, 'test_configurable');
572  $selectOptions = $this->objectManager
573  ->create(Collection::class)
574  ->setAttributeFilter($attribute->getId());
575 
576  $firstOption = $selectOptions->getFirstItem();
577  $firstOptionId = $firstOption->getId();
578  $this->requestBuilder->bind('test_configurable', $firstOptionId);
579  $this->requestBuilder->setRequestName('filter_out_of_stock_child');
580 
581  $queryResponse = $this->executeQuery();
582  $this->assertEquals(0, $queryResponse->count());
583 
584  $secondOption = $selectOptions->getLastItem();
585  $secondOptionId = $secondOption->getId();
586  $this->requestBuilder->bind('test_configurable', $secondOptionId);
587  $this->requestBuilder->setRequestName('filter_out_of_stock_child');
588 
589  $queryResponse = $this->executeQuery();
590  $this->assertEquals(0, $queryResponse->count());
591  }
592 
600  public function testSearchQueryBoost()
601  {
602  $this->requestBuilder->bind('query', 'antarctica');
603  $this->requestBuilder->setRequestName('search_boost');
604  $queryResponse = $this->executeQuery();
605  $this->assertEquals(2, $queryResponse->count());
606 
608  $products = iterator_to_array($queryResponse);
609  /*
610  * Products now contain search query in two attributes which are boosted with the same value: 1
611  * The search keyword (antarctica) is mentioned twice only in one of the products.
612  * And, as both attributes have the same search weight and boost, we expect that
613  * the product with doubled keyword should be prioritized by a search engine as a most relevant
614  * and therefore will be first in the search result.
615  */
616  $firstProduct = reset($products);
617  $this->assertEquals(1222, $firstProduct->getId());
618  $secondProduct = end($products);
619  $this->assertEquals(1221, $secondProduct->getId());
620 
622  $productAttributeRepository = $this->objectManager->get(
623  \Magento\Catalog\Api\ProductAttributeRepositoryInterface::class
624  );
625 
632  $attribute->setSearchWeight(20);
634 
635  $this->requestBuilder->bind('query', 'antarctica');
636  $this->requestBuilder->setRequestName('search_boost_name');
637  $queryResponse = $this->executeQuery();
638  $this->assertEquals(2, $queryResponse->count());
639 
641  $products = iterator_to_array($queryResponse);
642  /*
643  * As for the first case, we have two the same products.
644  * One of them has search keyword mentioned twice in the field which has search weight 1.
645  * However, we've changed the search weight of another attribute
646  * which has only one mention of the search keyword in another product.
647  *
648  * The case is mostly the same but search weight has been changed and we expect that
649  * less relevant (with only one mention) but more boosted (search weight = 20) product
650  * will be prioritized higher than more relevant, but less boosted product.
651  */
652  $firstProduct = reset($products);
653  $this->assertEquals(1221, $firstProduct->getId());
654  //$firstProduct
655  $secondProduct = end($products);
656  $this->assertEquals(1222, $secondProduct->getId());
657  }
658 
659  public function dateDataProvider()
660  {
661  return [
662  [['from' => '2000-01-01T00:00:00Z', 'to' => '2000-01-01T00:00:00Z'], 1], //Y-m-d
663  [['from' => '2000-01-01T00:00:00Z', 'to' => ''], 1],
664  [['from' => '1999-12-31T00:00:00Z', 'to' => '2000-01-01T00:00:00Z'], 1],
665  [['from' => '2000-02-01T00:00:00Z', 'to' => ''], 0],
666  ];
667  }
668 }
$config
Definition: fraud_order.php:17
defined('TESTS_BP')||define('TESTS_BP' __DIR__
Definition: _bootstrap.php:60
testAdvancedSearchDateField($rangeFilter, $expectedRecordsCount)
$value
Definition: gender.phtml:16
$productAttributeRepository
testSimpleAdvancedSearch( $nameQuery, $descriptionQuery, $rangeFilter, $expectedRecordsCount)
$selectOptions