From 072a34b03a4bec66f73386287896c40b9bb4616d Mon Sep 17 00:00:00 2001 From: Betty Suravech Date: Wed, 8 Jan 2020 14:53:22 +0100 Subject: [PATCH] Release 0.0.1 (#8) ### UPDATES - Updated composer.json (#4) (by @vmalyk) - Fix Bad Request Warning and Builder Aggregation (#6) --- Adapter/Aggregation/Builder.php | 168 +++++++++++++++++++++++ Adapter/AlgoliaElasticSearch5Adapter.php | 29 ++-- Adapter/AlgoliaElasticSearchAdapter.php | 29 ++-- CHANGELOG.md | 7 + Helper/ElasticAdapterHelper.php | 64 +++++++++ Plugin/FulltextCollection.php | 108 +++++++++++++++ composer.json | 6 +- etc/di.xml | 4 + etc/module.xml | 2 +- 9 files changed, 386 insertions(+), 31 deletions(-) create mode 100644 Adapter/Aggregation/Builder.php create mode 100644 CHANGELOG.md create mode 100644 Helper/ElasticAdapterHelper.php create mode 100644 Plugin/FulltextCollection.php diff --git a/Adapter/Aggregation/Builder.php b/Adapter/Aggregation/Builder.php new file mode 100644 index 0000000..cbbf223 --- /dev/null +++ b/Adapter/Aggregation/Builder.php @@ -0,0 +1,168 @@ +dataProviderContainer = array_map( + function (DataProviderInterface $dataProvider) { + return $dataProvider; + }, + $dataProviderContainer + ); + $this->aggregationContainer = array_map( + function (BucketBuilderInterface $bucketBuilder) { + return $bucketBuilder; + }, + $aggregationContainer + ); + $this->dataProviderFactory = $dataProviderFactory + ?: ObjectManager::getInstance()->get(DataProviderFactory::class); + + $this->productFactory = $productFactory; + } + + public function build(RequestInterface $request, array $queryResult) + { + $aggregations = []; + $buckets = $request->getAggregation(); + + $facets = $this->getFacets(); + + $dataProvider = $this->dataProviderFactory->create( + $this->dataProviderContainer[$request->getIndex()], + $this->query + ); + + foreach ($buckets as $bucket) { + if (count($facets) && isset($facets[$bucket->getField()])) { + $aggregations[$bucket->getName()] = + $this->formatAggregation($bucket->getField(), $facets[$bucket->getField()]); + } else { + $bucketAggregationBuilder = $this->aggregationContainer[$bucket->getType()]; + $aggregations[$bucket->getName()] = $bucketAggregationBuilder->build( + $bucket, + $request->getDimensions(), + $queryResult, + $dataProvider + ); + } + } + + $this->query = null; + + return $aggregations; + } + + + private function formatAggregation($attribute, $facetData) + { + $aggregation = []; + + foreach ($facetData as $value => $count) { + $optionId = $this->getOptionIdByLabel($attribute, $value); + $aggregation[$optionId] = [ + 'value' => (string) $optionId, + 'count' => (string) $count, + ]; + } + + return $aggregation; + } + + private function getOptionIdByLabel($attributeCode, $optionLabel) + { + $product = $this->getProduct(); + $isAttributeExist = $product->getResource()->getAttribute($attributeCode); + $optionId = ''; + if ($isAttributeExist && $isAttributeExist->usesSource()) { + $optionId = $isAttributeExist->getSource()->getOptionId($optionLabel); + } + + return $optionId; + } + + /** + * @return \Magento\Catalog\Model\Product + */ + private function getProduct() + { + if (!$this->product) { + $this->product = $this->productFactory->create(); + } + + return $this->product; + } + + /** + * Sets the QueryContainer instance to the internal property in order to use it in build process + * + * @param QueryContainer $query + * @return \Magento\Elasticsearch\SearchAdapter\Aggregation\Builder + */ + public function setQuery(QueryContainer $query) + { + $this->query = $query; + + return $this; + } + + public function setFacets($facets) + { + $this->facets = $facets; + } + + private function getFacets() + { + return $this->facets; + } + +} diff --git a/Adapter/AlgoliaElasticSearch5Adapter.php b/Adapter/AlgoliaElasticSearch5Adapter.php index bed46b6..6d7d24f 100755 --- a/Adapter/AlgoliaElasticSearch5Adapter.php +++ b/Adapter/AlgoliaElasticSearch5Adapter.php @@ -3,9 +3,10 @@ namespace Algolia\AlgoliaSearchElastic\Adapter; use Algolia\AlgoliaSearch\Helper\AdapterHelper; +use Algolia\AlgoliaSearchElastic\Helper\ElasticAdapterHelper; use Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Adapter as ElasticSearch5Adapter; use Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Mapper; -use Magento\Elasticsearch\SearchAdapter\Aggregation\Builder as AggregationBuilder; +use Algolia\AlgoliaSearchElastic\Adapter\Aggregation\Builder as AggregationBuilder; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; use Magento\Elasticsearch\SearchAdapter\ResponseFactory; use Magento\Elasticsearch\SearchAdapter\QueryContainerFactory; @@ -14,10 +15,12 @@ class AlgoliaElasticSearch5Adapter extends ElasticSearch5Adapter { - /** @var AdapterHelper */ private $adapterHelper; + /** @var ElasticAdapterHelper */ + private $esAdapterHelper; + /** @var QueryContainerFactory */ private $queryContainerFactory; @@ -30,6 +33,7 @@ class AlgoliaElasticSearch5Adapter extends ElasticSearch5Adapter * @param QueryContainerFactory $queryContainerFactory * @param LoggerInterface|null $logger * @param AdapterHelper $adapterHelper + * @param ElasticAdapterHelper $esAdapterHelper */ public function __construct( ConnectionManager $connectionManager, @@ -38,13 +42,15 @@ public function __construct( AggregationBuilder $aggregationBuilder, QueryContainerFactory $queryContainerFactory, LoggerInterface $logger = null, - AdapterHelper $adapterHelper + AdapterHelper $adapterHelper, + ElasticAdapterHelper $esAdapterHelper ) { parent::__construct($connectionManager, $mapper, $responseFactory, $aggregationBuilder, $queryContainerFactory, $logger); $this->adapterHelper = $adapterHelper; + $this->esAdapterHelper = $esAdapterHelper; $this->queryContainerFactory = $queryContainerFactory; } @@ -55,19 +61,13 @@ public function __construct( */ public function query(RequestInterface $request) { - if (!$this->adapterHelper->isAllowed() - || !( - $this->adapterHelper->isSearch() || - $this->adapterHelper->isReplaceCategory() || - $this->adapterHelper->isReplaceAdvancedSearch() || - $this->adapterHelper->isLandingPage() - ) - ) { + if (!$this->esAdapterHelper->replaceElasticSearchResults()) { return parent::query($request); } $aggregationBuilder = $this->aggregationBuilder; $query = $this->mapper->buildQuery($request); + $aggregationBuilder->setQuery($this->queryContainerFactory->create(['query' => $query])); $rawResponse = []; @@ -77,7 +77,7 @@ public function query(RequestInterface $request) try { // If instant search is on, do not make a search query unless SEO request is set to 'Yes' if (!$this->adapterHelper->isInstantEnabled() || $this->adapterHelper->makeSeoRequest()) { - list($rawResponse, $totalHits) = $this->adapterHelper->getDocumentsFromAlgolia($request); + list($rawResponse, $totalHits, $facets) = $this->adapterHelper->getDocumentsFromAlgolia(); $rawResponse = $this->transformResponseForElastic($rawResponse); } @@ -85,7 +85,9 @@ public function query(RequestInterface $request) return parent::query($request); } + $aggregationBuilder->setFacets($facets); $aggregations = $aggregationBuilder->build($request, $rawResponse); + $response = [ 'documents' => $rawResponse, 'aggregations' => $aggregations, @@ -105,9 +107,10 @@ private function transformResponseForElastic(array $rawResponse) foreach ($rawResponse as &$hit) { $hit['_id'] = $hit['entity_id']; } - $rawResponse['hits'] = ['hits' => $rawResponse]; } + $rawResponse['hits'] = ['hits' => $rawResponse]; + return $rawResponse; } } diff --git a/Adapter/AlgoliaElasticSearchAdapter.php b/Adapter/AlgoliaElasticSearchAdapter.php index d331a05..2da3b48 100755 --- a/Adapter/AlgoliaElasticSearchAdapter.php +++ b/Adapter/AlgoliaElasticSearchAdapter.php @@ -3,8 +3,9 @@ namespace Algolia\AlgoliaSearchElastic\Adapter; use Algolia\AlgoliaSearch\Helper\AdapterHelper; +use Algolia\AlgoliaSearchElastic\Helper\ElasticAdapterHelper; use Magento\Elasticsearch\SearchAdapter\Adapter as ElasticSearchAdapter; -use Magento\Elasticsearch\SearchAdapter\Aggregation\Builder as AggregationBuilder; +use Algolia\AlgoliaSearchElastic\Adapter\Aggregation\Builder as AggregationBuilder; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; use Magento\Elasticsearch\SearchAdapter\Mapper; use Magento\Elasticsearch\SearchAdapter\ResponseFactory; @@ -17,17 +18,21 @@ class AlgoliaElasticSearchAdapter extends ElasticSearchAdapter /** @var AdapterHelper */ private $adapterHelper; + /** @var ElasticAdapterHelper */ + private $esAdapterHelper; + /** @var QueryContainerFactory */ private $queryContainerFactory; /** - * AlgoliaElasticSearch5Adapter constructor. + * AlgoliaElasticSearchAdapter constructor. * @param ConnectionManager $connectionManager * @param Mapper $mapper * @param ResponseFactory $responseFactory * @param AggregationBuilder $aggregationBuilder * @param QueryContainerFactory $queryContainerFactory * @param AdapterHelper $adapterHelper + * @param ElasticAdapterHelper $esAdapterHelper */ public function __construct( ConnectionManager $connectionManager, @@ -35,12 +40,14 @@ public function __construct( ResponseFactory $responseFactory, AggregationBuilder $aggregationBuilder, QueryContainerFactory $queryContainerFactory, - AdapterHelper $adapterHelper + AdapterHelper $adapterHelper, + ElasticAdapterHelper $esAdapterHelper ) { parent::__construct($connectionManager, $mapper, $responseFactory, $aggregationBuilder, $queryContainerFactory); $this->adapterHelper = $adapterHelper; + $this->esAdapterHelper = $esAdapterHelper; } /** @@ -49,14 +56,7 @@ public function __construct( */ public function query(RequestInterface $request) { - if (!$this->adapterHelper->isAllowed() - || !( - $this->adapterHelper->isSearch() || - $this->adapterHelper->isReplaceCategory() || - $this->adapterHelper->isReplaceAdvancedSearch() || - $this->adapterHelper->isLandingPage() - ) - ) { + if (!$this->esAdapterHelper->replaceElasticSearchResults()) { return parent::query($request); } @@ -71,7 +71,7 @@ public function query(RequestInterface $request) try { // If instant search is on, do not make a search query unless SEO request is set to 'Yes' if (!$this->adapterHelper->isInstantEnabled() || $this->adapterHelper->makeSeoRequest()) { - list($rawResponse, $totalHits) = $this->adapterHelper->getDocumentsFromAlgolia($request); + list($rawResponse, $totalHits, $facets) = $this->adapterHelper->getDocumentsFromAlgolia(); $rawResponse = $this->transformResponseForElastic($rawResponse); } @@ -79,7 +79,9 @@ public function query(RequestInterface $request) return parent::query($request); } + $aggregationBuilder->setFacets($facets); $aggregations = $aggregationBuilder->build($request, $rawResponse); + $response = [ 'documents' => $rawResponse, 'aggregations' => $aggregations, @@ -99,9 +101,10 @@ private function transformResponseForElastic(array $rawResponse) foreach ($rawResponse as &$hit) { $hit['_id'] = $hit['entity_id']; } - $rawResponse['hits'] = ['hits' => $rawResponse]; } + $rawResponse['hits'] = ['hits' => $rawResponse]; + return $rawResponse; } } diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..dd5ad76 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +# CHANGE LOG + +## 0.0.1 + +### UPDATES +- Updated composer.json (#4) (by @vmalyk) +- Fix Bad Request Warning and Builder Aggregation (#6) \ No newline at end of file diff --git a/Helper/ElasticAdapterHelper.php b/Helper/ElasticAdapterHelper.php new file mode 100644 index 0000000..56d6d11 --- /dev/null +++ b/Helper/ElasticAdapterHelper.php @@ -0,0 +1,64 @@ +adapterHelper = $adapterHelper; + $this->configHelper = $configHelper; + } + + /** + * @return bool + */ + public function replaceElasticSearchResults() + { + if (!$this->adapterHelper->isAllowed() + || !( + $this->adapterHelper->isSearch() || + $this->adapterHelper->isReplaceCategory() || + $this->adapterHelper->isReplaceAdvancedSearch() || + $this->adapterHelper->isLandingPage() + ) + ) { + return false; + } + + return true; + } + + public function getStoreId() + { + return $this->configHelper->getStoreId(); + } + + public function getFacets() + { + return $this->configHelper->getFacets($this->getStoreId()); + } + +} diff --git a/Plugin/FulltextCollection.php b/Plugin/FulltextCollection.php new file mode 100644 index 0000000..ec44326 --- /dev/null +++ b/Plugin/FulltextCollection.php @@ -0,0 +1,108 @@ +adapterHelper = $adapterHelper; + $this->esAdapterHelper = $esAdapterHelper; + $this->productFactory = $productFactory; + } + + /** + * @param \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection $subject + * @param $field + * @param null $condition + */ + public function beforeAddFieldToFilter( + \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection $subject, + $field, + $condition = null + ) { + if (!$condition || !$this->esAdapterHelper->replaceElasticSearchResults()) { + return [$field, $condition]; + } + + if (is_array($this->getFacets()) && in_array($field, $this->getFacets())) { + $condition = $this->getOptionIdByLabel($field, $condition); + } + + return [$field, $condition]; + } + + /** + * @param string $attributeCode + * @param null $optionLabel + * @return string + */ + private function getOptionIdByLabel($attributeCode, $optionLabel = null) + { + if ($optionLabel && !is_array($optionLabel)) { + $product = $this->getProduct(); + $isAttributeExist = $product->getResource()->getAttribute($attributeCode); + if ($isAttributeExist && $isAttributeExist->usesSource()) { + $optionLabel = $isAttributeExist->getSource()->getOptionId($optionLabel); + } + } + + return $optionLabel; + } + + /** + * @return \Magento\Catalog\Model\Product + */ + public function getProduct() + { + if (!$this->product) { + $this->product = $this->productFactory->create(); + } + + return $this->product; + } + + /** + * @return array + */ + public function getFacets() + { + if (!$this->facets) { + $facets = []; + $configFacets = $this->esAdapterHelper->getFacets(); + if (is_array($configFacets) && count($configFacets)) { + $facets = array_map(function ($facet) { + return $facet['attribute']; + }, $configFacets); + } + $this->facets = $facets; + } + + return $this->facets; + } + +} diff --git a/composer.json b/composer.json index ac87e7d..b4ff4ad 100644 --- a/composer.json +++ b/composer.json @@ -3,11 +3,9 @@ "description": "Algolia Search ES Compatibility module for Magento >=2.3.1|>=2.2.8 and Algolia Search >=1.12 extension", "type": "magento2-module", "license": ["MIT"], - "version": "0.0.0", + "version": "0.0.1", "require": { - "php": "~7.1.3||~7.2.0", - "algolia/algoliasearch-client-php": ">=1.27.0 <2.0", - "algolia/algoliasearch-magento-2": ">=1.8.0", + "algolia/algoliasearch-magento-2": ">=1.12.0", "magento/module-elasticsearch": ">=100.2.7" }, "autoload": { diff --git a/etc/di.xml b/etc/di.xml index de86527..81a3e28 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -4,4 +4,8 @@ + + + + diff --git a/etc/module.xml b/etc/module.xml index 24c1a2d..5042df8 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,6 +1,6 @@ - +