fix p47
This commit is contained in:
@@ -22,6 +22,23 @@ final readonly class CommerceQueryParser
|
||||
) {
|
||||
}
|
||||
|
||||
public function extractExactProductNumberSearchText(string $searchText): ?string
|
||||
{
|
||||
$searchText = trim($this->normalize($searchText));
|
||||
|
||||
if ($searchText === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (preg_match($this->config->getExactProductNumberSearchTextPattern(), $searchText, $matches) !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$productNumber = trim((string) ($matches[1] ?? ''));
|
||||
|
||||
return $productNumber !== '' ? $productNumber : null;
|
||||
}
|
||||
|
||||
public function parse(
|
||||
string $originalPrompt,
|
||||
string $intent,
|
||||
|
||||
@@ -377,6 +377,18 @@ final class ShopSearchService
|
||||
string $originalPrompt,
|
||||
bool $usesHistoryContext
|
||||
): array {
|
||||
$exactProductNumber = $this->queryParser->extractExactProductNumberSearchText($query->searchText);
|
||||
|
||||
if ($exactProductNumber !== null) {
|
||||
return $this->executeExactProductNumberSearch(
|
||||
productNumber: $exactProductNumber,
|
||||
query: $query,
|
||||
commerceIntent: $commerceIntent,
|
||||
originalPrompt: $originalPrompt,
|
||||
usesHistoryContext: $usesHistoryContext
|
||||
);
|
||||
}
|
||||
|
||||
$criteria = $this->criteriaBuilder->build($query, $this->maxResults);
|
||||
|
||||
try {
|
||||
@@ -448,6 +460,53 @@ final class ShopSearchService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ShopProductResult[]
|
||||
*/
|
||||
private function executeExactProductNumberSearch(
|
||||
string $productNumber,
|
||||
CommerceSearchQuery $query,
|
||||
string $commerceIntent,
|
||||
string $originalPrompt,
|
||||
bool $usesHistoryContext
|
||||
): array {
|
||||
$criteria = $this->criteriaBuilder->buildExactProductNumber($productNumber, 5);
|
||||
|
||||
$this->logger->info('Shop search using exact product-number criteria', [
|
||||
'commerceIntent' => $commerceIntent,
|
||||
'originalPrompt' => $originalPrompt,
|
||||
'normalizedPrompt' => $query->normalizedPrompt,
|
||||
'searchText' => $query->searchText,
|
||||
'productNumber' => $productNumber,
|
||||
'usesHistoryContext' => $usesHistoryContext,
|
||||
]);
|
||||
|
||||
try {
|
||||
$response = $this->storeApiClient->searchProducts($criteria);
|
||||
|
||||
return $this->mapAndLogSearchResponse(
|
||||
response: $response,
|
||||
query: $query,
|
||||
commerceIntent: $commerceIntent,
|
||||
originalPrompt: $originalPrompt,
|
||||
usesHistoryContext: $usesHistoryContext,
|
||||
usedSafeCriteria: true
|
||||
);
|
||||
} catch (
|
||||
ClientExceptionInterface |
|
||||
RedirectionExceptionInterface |
|
||||
ServerExceptionInterface |
|
||||
TransportExceptionInterface |
|
||||
StoreApiException |
|
||||
\RuntimeException $e
|
||||
) {
|
||||
$this->recordFailedSearch($e);
|
||||
$this->logShopSearchFailure($query, $commerceIntent, $originalPrompt, $usesHistoryContext, $e);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ShopProductResult[]|null
|
||||
*/
|
||||
|
||||
@@ -206,6 +206,11 @@ final class CommerceQueryParserConfig
|
||||
return $this->string('patterns.direct_product_digit');
|
||||
}
|
||||
|
||||
public function getExactProductNumberSearchTextPattern(): string
|
||||
{
|
||||
return $this->string('patterns.exact_product_number_search_text');
|
||||
}
|
||||
|
||||
public function getDirectProductMaxTokens(): int
|
||||
{
|
||||
return $this->int('limits.direct_product_max_tokens');
|
||||
|
||||
@@ -65,6 +65,66 @@ final class ShopwareCriteriaBuilder
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a narrow, rich-text-free criteria payload for exact article-number lookups.
|
||||
* Pure product-number searches should not run through Shopware full-text search,
|
||||
* because broad numeric search can surface unrelated products and trigger JSON
|
||||
* encoding problems in rich text/custom fields.
|
||||
*/
|
||||
public function buildExactProductNumber(string $productNumber, ?int $limit = 5): array
|
||||
{
|
||||
$productNumber = trim($productNumber);
|
||||
|
||||
$criteria = [
|
||||
'page' => 1,
|
||||
'limit' => max(1, $limit),
|
||||
'total-count-mode' => 0,
|
||||
'includes' => [
|
||||
'product' => [
|
||||
'id',
|
||||
'name',
|
||||
'translated.name',
|
||||
'productNumber',
|
||||
'available',
|
||||
'calculatedPrice',
|
||||
'manufacturer',
|
||||
],
|
||||
'product_manufacturer' => [
|
||||
'name',
|
||||
],
|
||||
'calculated_price' => [
|
||||
'unitPrice',
|
||||
'totalPrice',
|
||||
'referencePrice',
|
||||
'listPrice',
|
||||
'regulationPrice',
|
||||
],
|
||||
],
|
||||
'associations' => [
|
||||
'manufacturer' => new \stdClass(),
|
||||
],
|
||||
'filter' => [
|
||||
[
|
||||
'type' => 'equals',
|
||||
'field' => 'active',
|
||||
'value' => true,
|
||||
],
|
||||
[
|
||||
'type' => 'equals',
|
||||
'field' => 'available',
|
||||
'value' => true,
|
||||
],
|
||||
[
|
||||
'type' => 'equals',
|
||||
'field' => 'productNumber',
|
||||
'value' => $productNumber,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $criteria;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an ultra-safe Store API payload for the final recovery attempt.
|
||||
* It keeps only identity, availability and price fields and intentionally
|
||||
|
||||
Reference in New Issue
Block a user