Skip to content

Commit

Permalink
Merge pull request #22 from magento-commerce/imported-magento-catalog…
Browse files Browse the repository at this point in the history
…-storefront-442

[Imported] Variants GetVariantsMatch Service
  • Loading branch information
RuslanKostiv1 authored Dec 4, 2020
2 parents 2479462 + 191d679 commit 91c93e3
Show file tree
Hide file tree
Showing 14 changed files with 1,001 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,13 @@ public function __construct(
/**
* Fetch product variants data from storage
*
* @param int $parentId
* @param array $parentIds
* @return array
* @throws NotFoundException
* @throws RuntimeException
* @throws \Throwable
*/
public function fetchByProductId(int $parentId): array
public function fetchByParentIds(array $parentIds): array
{
// TODO: Adapt to work without store code https://github.com/magento/catalog-storefront/issues/417
$storageName = $this->storageState->getCurrentDataSourceName(
Expand All @@ -68,21 +69,91 @@ public function fetchByProductId(int $parentId): array
$entities = $this->query->searchFilteredEntries(
$storageName,
ProductVariant::ENTITY_NAME,
['parent_id' => $parentId]
['parent_id' => $parentIds]
);
} catch (NotFoundException $notFoundException) {
$this->logger->error(
\sprintf(
'Cannot find product variants for product id "%s"',
$parentId,
\implode(",", $parentIds),
),
['exception' => $notFoundException]
);
return [];
} catch (\Throwable $e) {
$this->logger->error($e);
$this->logger->error('Error while trying to fetch product variants by parent ids: ' . $e);
throw $e;
}
return $entities->toArray(false);
}

/**
* Fetch product variants data from storage
*
* @param array $variantIds
* @return array
* @throws RuntimeException
* @throws \Throwable
*/
public function fetchByVariantIds(array $variantIds): array
{
// TODO: Adapt to work without store code https://github.com/magento/catalog-storefront/issues/417
$storageName = $this->storageState->getCurrentDataSourceName(
[VariantService::EMPTY_STORE_CODE, ProductVariant::ENTITY_NAME]
);
try {
$entities = $this->query->searchFilteredEntries(
$storageName,
ProductVariant::ENTITY_NAME,
['id' => $variantIds]
);
} catch (NotFoundException $notFoundException) {
$this->logger->error(
\sprintf(
'Cannot find product variants for variant ids "%s"',
\implode(",", $variantIds),
),
['exception' => $notFoundException]
);
return [];
} catch (\Throwable $e) {
$this->logger->error('Error while trying to fetch product variants by id: ' . $e);
throw $e;
}
return $entities->toArray(false);
}

/**
* Get matching variant ids by option values
*
* If strict is true, only variants which contain all of the provided values, will be returned
*
* @param array $values
* @param bool $strict
* @return array
* @throws \Throwable
*/
public function fetchVariantIdsByOptionValues(array $values, bool $strict = true): array
{
// TODO: Adapt to work without store code https://github.com/magento/catalog-storefront/issues/417
$storageName = $this->storageState->getCurrentDataSourceName(
[VariantService::EMPTY_STORE_CODE, ProductVariant::ENTITY_NAME]
);

$minDocCount = $strict ? count($values) : 1;
try {
$queryResult = $this->query->searchAggregatedFilteredEntries(
$storageName,
ProductVariant::ENTITY_NAME,
['option_value' => $values],
'id',
$minDocCount
);
$variantIds = \array_column($queryResult, 'id');
} catch (\Throwable $e) {
$this->logger->error('Error while trying to fetch product variants by option values: ' . $e);
throw $e;
}
return $variantIds;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ public function searchMatchedEntries(
array $searchBody,
?string $queryContext = 'must'
): EntryIteratorInterface {
return $this->searchEntries($indexName, $entityName, $searchBody, $queryContext, 'match');
$searchQuery = $this->buildSearchQuery($searchBody, $queryContext, 'match');
$searchResult = $this->searchEntries($indexName, $entityName, $searchQuery);
return $this->searchResultIteratorFactory->create($searchResult);
}

/**
Expand All @@ -114,42 +116,84 @@ public function searchFilteredEntries(
string $indexName,
string $entityName,
array $searchBody,
?string $clauseType = 'term'
?string $clauseType = 'terms'
): EntryIteratorInterface {
return $this->searchEntries($indexName, $entityName, $searchBody, 'filter', $clauseType);
$searchQuery = $this->buildSearchQuery($searchBody, 'filter', $clauseType);
$searchResult = $this->searchEntries($indexName, $entityName, $searchQuery);
return $this->searchResultIteratorFactory->create($searchResult);
}

/**
* @inheritdoc
*/
public function searchAggregatedFilteredEntries(
string $indexName,
string $entityName,
array $searchBody,
string $aggregateField,
int $minDocCount,
?string $clauseType = 'terms'
): array {
$searchQuery = $this->buildSearchQuery($searchBody, 'filter', $clauseType);
$aggregationQuery = $this->buildTermsAggregationQuery($aggregateField, $minDocCount, $entityName);
$searchResult = $this->searchEntries($indexName, $entityName, $searchQuery, $aggregationQuery);
$buckets = $searchResult['aggregations'][$entityName]['buckets'];
$result = [];
foreach ($buckets as $match) {
$result[] = [
$aggregateField => $match['key'],
'doc_count' => $match['doc_count']
];
}
return $result;
}

/**
* Searches entries into elastic search storage.
*
* @param string $indexName
* @param string $entityName
* @param array $searchBody
* @param string $queryContext
* @param string $clauseType
* @return EntryIteratorInterface
* @throws NotFoundException
* @param array $searchQuery
* @param array $aggregationQuery
* @return array
* @throws RuntimeException
* @throws \OverflowException
*/
private function searchEntries(
string $indexName,
string $entityName,
array $searchBody,
string $queryContext,
string $clauseType
): EntryIteratorInterface {
//TODO: Add pagination and remove max size search size https://github.com/magento/catalog-storefront/issues/418
array $searchQuery,
array $aggregationQuery = []
): array {
$searchBody['query'] = $searchQuery;
$size = self::SEARCH_LIMIT;
if (!empty($aggregationQuery)) {
$searchBody['aggregations'] = $aggregationQuery;
$size = 0;
}
return $this->searchRequest($indexName, $entityName, $searchBody, $size);
}

/**
* Perform client search request.
*
* @param string $indexName
* @param string $entityName
* @param array $searchBody
* @param int $size
* @return array
* @throws RuntimeException
* @throws \OverflowException
*/
private function searchRequest(string $indexName, string $entityName, array $searchBody, int $size): array
{
$query = [
'index' => $indexName,
'type' => $entityName,
'body' => [],
'size' => self::SEARCH_LIMIT
'body' => $searchBody,
'size' => $size
];

foreach ($searchBody as $key => $value) {
$query['body']['query']['bool'][$queryContext][][$clauseType][$key] = $value;
}

try {
$result = $this->connectionPull->getConnection()->search($query);
} catch (\Throwable $throwable) {
Expand All @@ -158,16 +202,51 @@ private function searchEntries(
$throwable
);
}

//TODO: Add pagination and remove max size search size https://github.com/magento/catalog-storefront/issues/418
if (isset($result['hits']['total']['value']) && $result['hits']['total']['value'] > self::SEARCH_LIMIT) {
throw new \OverflowException(
"Storage error: Search returned too many results to handle. Query was: " . \json_encode($query)
);
}

$this->checkErrors($result, $indexName);
return $result;
}

return $this->searchResultIteratorFactory->create($result);
/**
* Form a search query
*
* @param array $searchBody
* @param string $queryContext
* @param string $clauseType
* @return array
*/
private function buildSearchQuery(array $searchBody, string $queryContext, string $clauseType): array
{
$query = [];
foreach ($searchBody as $key => $value) {
$query['bool'][$queryContext][][$clauseType][$key] = $value;
}
return $query;
}

/**
* Form a query for terms aggregation
*
* @param string $field
* @param int $minDocCount
* @param string $entityName
* @return array
*/
private function buildTermsAggregationQuery(string $field, int $minDocCount, string $entityName): array
{
//TODO: Add pagination and remove max size search size https://github.com/magento/catalog-storefront/issues/418
$query[$entityName]['terms'] = [
'size' => self::SEARCH_LIMIT,
'field' => $field,
'min_doc_count' => $minDocCount
];
return $query;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public function getEntries(
* @return EntryIteratorInterface
* @throws NotFoundException
* @throws RuntimeException
* @throws \OverflowException
*/
public function searchMatchedEntries(
string $indexName,
Expand All @@ -92,11 +93,39 @@ public function searchMatchedEntries(
* @return EntryIteratorInterface
* @throws NotFoundException
* @throws RuntimeException
* @throws \OverflowException
*/
public function searchFilteredEntries(
string $indexName,
string $entityName,
array $searchBody,
?string $clauseType = 'term'
?string $clauseType = 'terms'
): EntryIteratorInterface;

/**
* Search entries by specified search arguments, then aggregate the results by a specific field
*
* $searchBody contains "search field" -> "search value".
* "search field" must be indexed in order for this query to work.
* $aggregateField contains a field which will be used for the aggregate query,
* $minDocCount is the minimum match requirement for the aggregate
*
* @param string $indexName
* @param string $entityName
* @param array $searchBody
* @param string $aggregateField
* @param int $minDocCount
* @param string|null $clauseType
* @return array
* @throws RuntimeException
* @throws \OverflowException
*/
public function searchAggregatedFilteredEntries(
string $indexName,
string $entityName,
array $searchBody,
string $aggregateField,
int $minDocCount,
?string $clauseType = 'terms'
): array;
}
Loading

0 comments on commit 91c93e3

Please sign in to comment.