move intent an config value into config files

This commit is contained in:
team2
2026-04-23 22:07:07 +02:00
parent fce44e971d
commit 55a61e2e71
2 changed files with 415 additions and 124 deletions

View File

@@ -26,6 +26,7 @@ final readonly class ShopSearchService
private CommerceQueryParser $queryParser, private CommerceQueryParser $queryParser,
private ShopwareCriteriaBuilder $criteriaBuilder, private ShopwareCriteriaBuilder $criteriaBuilder,
private StoreApiClient $storeApiClient, private StoreApiClient $storeApiClient,
private ShopServiceConfig $shopConfig,
private LoggerInterface $logger, private LoggerInterface $logger,
private bool $enabled = true, private bool $enabled = true,
private int $maxResults = 25, private int $maxResults = 25,
@@ -53,8 +54,7 @@ final readonly class ShopSearchService
$primaryQuery = $this->queryParser->parse( $primaryQuery = $this->queryParser->parse(
$originalPrompt, $originalPrompt,
$commerceIntent, $commerceIntent,
$commerceHistoryContext, $commerceHistoryContext
$referenceContext
); );
$focusMode = $this->determineFocusMode( $focusMode = $this->determineFocusMode(
@@ -97,8 +97,7 @@ final readonly class ShopSearchService
$fallbackQuery = $this->queryParser->parse( $fallbackQuery = $this->queryParser->parse(
$originalPrompt, $originalPrompt,
$commerceIntent, $commerceIntent,
'', ''
$referenceContext
); );
$this->logger->info('Shop search retry without commerce history context', [ $this->logger->info('Shop search retry without commerce history context', [
@@ -156,7 +155,7 @@ final readonly class ShopSearchService
'available' => $product->available, 'available' => $product->available,
'price' => $product->price, 'price' => $product->price,
], ],
array_slice($finalProducts, 0, 3) array_slice($finalProducts, 0, $this->shopConfig->getTopProductLogLimit())
), ),
]); ]);
@@ -222,10 +221,12 @@ final readonly class ShopSearchService
return []; return [];
} }
$baseSearchText = $referenceContext->buildReferenceSearchText();
$baseQuery = new CommerceSearchQuery( $baseQuery = new CommerceSearchQuery(
originalPrompt: $originalPrompt, originalPrompt: $originalPrompt,
normalizedPrompt: mb_strtolower($referenceContext->buildReferenceSearchText(), 'UTF-8'), normalizedPrompt: mb_strtolower($baseSearchText, 'UTF-8'),
searchText: $referenceContext->buildReferenceSearchText(), searchText: $baseSearchText,
brand: $referenceContext->manufacturer, brand: $referenceContext->manufacturer,
sizes: [], sizes: [],
properties: [], properties: [],
@@ -308,22 +309,11 @@ final readonly class ShopSearchService
private function expandFocusTermVariants(string $focusTerm): array private function expandFocusTermVariants(string $focusTerm): array
{ {
$normalized = $this->normalizeForMatching($focusTerm); $normalized = $this->normalizeForMatching($focusTerm);
$variants = [$normalized]; $variants = [$normalized];
$variantMap = $this->shopConfig->getAccessoryFocusVariantMap();
$map = [ if (isset($variantMap[$normalized]) && is_array($variantMap[$normalized])) {
'indikator' => ['indikator', 'indikatoren'], $variants = array_merge($variants, $variantMap[$normalized]);
'indikatoren' => ['indikator', 'indikatoren'],
'reagenz' => ['reagenz', 'reagenzien'],
'reagenzien' => ['reagenz', 'reagenzien'],
'ersatzteil' => ['ersatzteil', 'ersatzteile'],
'ersatzteile' => ['ersatzteil', 'ersatzteile'],
'service set' => ['service set', 'serviceset', 'service-set'],
'serviceset' => ['service set', 'serviceset', 'service-set'],
];
if (isset($map[$normalized])) {
$variants = array_merge($variants, $map[$normalized]);
} }
return array_values(array_unique(array_filter( return array_values(array_unique(array_filter(
@@ -415,38 +405,11 @@ final readonly class ShopSearchService
): string { ): string {
$normalizedPrompt = $this->normalizeForMatching($originalPrompt); $normalizedPrompt = $this->normalizeForMatching($originalPrompt);
if ($this->containsAnyKeyword($normalizedPrompt, [ if ($this->containsAnyKeyword($normalizedPrompt, $this->shopConfig->getDeviceFocusKeywords())) {
'geräte',
'geraete',
'gerät',
'geraet',
'analysegerät',
'analysegeraet',
'messgerät',
'messgeraet',
'analysator',
'controller',
'monitor',
])) {
return self::FOCUS_DEVICE; return self::FOCUS_DEVICE;
} }
if ($this->containsAnyKeyword($normalizedPrompt, [ if ($this->containsAnyKeyword($normalizedPrompt, $this->shopConfig->getAccessoryFocusKeywords())) {
'indikator',
'indikatoren',
'reagenz',
'reagenzien',
'zubehör',
'zubehor',
'ersatzteil',
'ersatzteile',
'verbrauchsmaterial',
'service set',
'serviceset',
'filter',
'pumpenkopf',
'motorblock',
])) {
return self::FOCUS_ACCESSORY; return self::FOCUS_ACCESSORY;
} }
@@ -673,22 +636,7 @@ final readonly class ShopSearchService
} }
} }
foreach ([ foreach ($this->shopConfig->getAccessoryFocusKeywords() as $candidate) {
'indikator',
'indikatoren',
'reagenz',
'reagenzien',
'zubehor',
'zubehör',
'ersatzteil',
'ersatzteile',
'verbrauchsmaterial',
'service set',
'serviceset',
'filter',
'pumpenkopf',
'motorblock',
] as $candidate) {
$normalizedCandidate = $this->normalizeForMatching($candidate); $normalizedCandidate = $this->normalizeForMatching($candidate);
if ($normalizedCandidate !== '' && str_contains($normalizedPrompt, $normalizedCandidate)) { if ($normalizedCandidate !== '' && str_contains($normalizedPrompt, $normalizedCandidate)) {
@@ -701,22 +649,7 @@ final readonly class ShopSearchService
private function isAccessoryFocusToken(string $token): bool private function isAccessoryFocusToken(string $token): bool
{ {
foreach ([ foreach ($this->shopConfig->getAccessoryFocusKeywords() as $candidate) {
'indikator',
'indikatoren',
'reagenz',
'reagenzien',
'zubehor',
'zubehör',
'ersatzteil',
'ersatzteile',
'verbrauchsmaterial',
'service set',
'serviceset',
'filter',
'pumpenkopf',
'motorblock',
] as $candidate) {
if ($token === $this->normalizeForMatching($candidate)) { if ($token === $this->normalizeForMatching($candidate)) {
return true; return true;
} }
@@ -725,6 +658,9 @@ final readonly class ShopSearchService
return false; return false;
} }
/**
* @param string[] $focusTerms
*/
private function productMatchesAnyFocusTerm(ShopProductResult $product, array $focusTerms): bool private function productMatchesAnyFocusTerm(ShopProductResult $product, array $focusTerms): bool
{ {
if ($focusTerms === []) { if ($focusTerms === []) {
@@ -768,9 +704,11 @@ final readonly class ShopSearchService
return null; return null;
} }
$normalized = str_replace(['€', ' '], '', $price); $normalized = str_replace(
$normalized = str_replace('.', '', $normalized); $this->shopConfig->getPriceNormalizationSearch(),
$normalized = str_replace(',', '.', $normalized); $this->shopConfig->getPriceNormalizationReplace(),
$price
);
return is_numeric($normalized) ? (float) $normalized : null; return is_numeric($normalized) ? (float) $normalized : null;
} }
@@ -806,7 +744,7 @@ final readonly class ShopSearchService
url: $this->buildAbsoluteUrl($relativeUrl), url: $this->buildAbsoluteUrl($relativeUrl),
highlights: $this->extractHighlights($row), highlights: $this->extractHighlights($row),
description: $this->cleanUpDescription($row), description: $this->cleanUpDescription($row),
productImage: $row['cover']['media']['thumbnails'][0]['url'] ?? 'no-image', productImage: $row['cover']['media']['thumbnails'][0]['url'] ?? $this->shopConfig->getMissingProductImagePlaceholder(),
customFields: $this->getRelevantCustomFields($row['customFields'] ?? []) customFields: $this->getRelevantCustomFields($row['customFields'] ?? [])
); );
} }
@@ -890,28 +828,28 @@ final readonly class ShopSearchService
$productCorpusNumberTokens = $this->extractNumberTokens($productCorpusTokens); $productCorpusNumberTokens = $this->extractNumberTokens($productCorpusTokens);
if ($normalizedProductNumber !== '' && $this->containsWholePhrase($normalizedQuery, $normalizedProductNumber)) { if ($normalizedProductNumber !== '' && $this->containsWholePhrase($normalizedQuery, $normalizedProductNumber)) {
$score += 160; $score += $this->shopConfig->getExactProductNumberPhraseScore();
} }
if ($normalizedProductName !== '' && $this->containsWholePhrase($normalizedQuery, $normalizedProductName)) { if ($normalizedProductName !== '' && $this->containsWholePhrase($normalizedQuery, $normalizedProductName)) {
$score += 90; $score += $this->shopConfig->getExactProductNamePhraseScore();
} }
if ($normalizedBrand !== '') { if ($normalizedBrand !== '') {
if ($normalizedManufacturer !== '' && $normalizedManufacturer === $normalizedBrand) { if ($normalizedManufacturer !== '' && $normalizedManufacturer === $normalizedBrand) {
$score += 40; $score += $this->shopConfig->getExactManufacturerMatchScore();
} elseif ($this->containsWholePhrase($normalizedProductName, $normalizedBrand)) { } elseif ($this->containsWholePhrase($normalizedProductName, $normalizedBrand)) {
$score += 20; $score += $this->shopConfig->getBrandContainedInNameScore();
} }
} }
$score += $this->countOverlap($queryTokens, $productNameTokens) * 6; $score += $this->countOverlap($queryTokens, $productNameTokens) * $this->shopConfig->getNameTokenOverlapWeight();
$score += $this->countOverlap($queryTokens, $productNumberTokens) * 10; $score += $this->countOverlap($queryTokens, $productNumberTokens) * $this->shopConfig->getProductNumberTokenOverlapWeight();
$score += $this->countOverlap($queryTokens, $productCorpusTokens) * 2; $score += $this->countOverlap($queryTokens, $productCorpusTokens) * $this->shopConfig->getCorpusTokenOverlapWeight();
$score += $this->countOverlap($queryNumberTokens, $productNameNumberTokens) * 18; $score += $this->countOverlap($queryNumberTokens, $productNameNumberTokens) * $this->shopConfig->getNameNumberOverlapWeight();
$score += $this->countOverlap($queryNumberTokens, $productNumberNumberTokens) * 28; $score += $this->countOverlap($queryNumberTokens, $productNumberNumberTokens) * $this->shopConfig->getProductNumberNumberOverlapWeight();
$score += $this->countOverlap($queryNumberTokens, $productCorpusNumberTokens) * 8; $score += $this->countOverlap($queryNumberTokens, $productCorpusNumberTokens) * $this->shopConfig->getCorpusNumberOverlapWeight();
foreach ($normalizedSizes as $normalizedSize) { foreach ($normalizedSizes as $normalizedSize) {
if ($normalizedSize === '') { if ($normalizedSize === '') {
@@ -923,14 +861,14 @@ final readonly class ShopSearchService
|| $this->containsWholePhrase($normalizedProductNumber, $normalizedSize) || $this->containsWholePhrase($normalizedProductNumber, $normalizedSize)
|| $this->containsWholePhrase($normalizedProductCorpus, $normalizedSize) || $this->containsWholePhrase($normalizedProductCorpus, $normalizedSize)
) { ) {
$score += 12; $score += $this->shopConfig->getSizeMatchScore();
} }
} }
$score += $this->scoreProductTypeMatch($product, $normalizedQuery); $score += $this->scoreProductTypeMatch($product, $normalizedQuery);
if ($product->available === true) { if ($product->available === true) {
$score += 1; $score += $this->shopConfig->getAvailabilityBonusScore();
} }
return $score; return $score;
@@ -952,21 +890,21 @@ final readonly class ShopSearchService
if ($isDeviceQuery && !$isAccessoryQuery) { if ($isDeviceQuery && !$isAccessoryQuery) {
if ($isDeviceLikeProduct) { if ($isDeviceLikeProduct) {
$score += 60; $score += $this->shopConfig->getDeviceQueryDeviceProductBonus();
} }
if ($isAccessoryLikeProduct) { if ($isAccessoryLikeProduct) {
$score -= 120; $score -= $this->shopConfig->getDeviceQueryAccessoryPenalty();
} }
} }
if ($isAccessoryQuery) { if ($isAccessoryQuery) {
if ($isAccessoryLikeProduct) { if ($isAccessoryLikeProduct) {
$score += 30; $score += $this->shopConfig->getAccessoryQueryAccessoryProductBonus();
} }
if ($isDeviceLikeProduct) { if ($isDeviceLikeProduct) {
$score += 10; $score += $this->shopConfig->getAccessoryQueryDeviceProductBonus();
} }
} }
@@ -975,7 +913,7 @@ final readonly class ShopSearchService
private function isDeviceQuery(string $normalizedQuery): bool private function isDeviceQuery(string $normalizedQuery): bool
{ {
foreach (ShopServiceConfig::DEVICE_QUERY_KEYWORDS as $keyword) { foreach ($this->shopConfig->getDeviceQueryKeywords() as $keyword) {
if (str_contains($normalizedQuery, $this->normalizeForMatching($keyword))) { if (str_contains($normalizedQuery, $this->normalizeForMatching($keyword))) {
return true; return true;
} }
@@ -986,7 +924,7 @@ final readonly class ShopSearchService
private function isAccessoryQuery(string $normalizedQuery): bool private function isAccessoryQuery(string $normalizedQuery): bool
{ {
foreach (ShopServiceConfig::ACCESSORY_QUERY_KEYWORDS as $keyword) { foreach ($this->shopConfig->getAccessoryQueryKeywords() as $keyword) {
if (str_contains($normalizedQuery, $this->normalizeForMatching($keyword))) { if (str_contains($normalizedQuery, $this->normalizeForMatching($keyword))) {
return true; return true;
} }
@@ -999,7 +937,7 @@ final readonly class ShopSearchService
{ {
$corpus = $this->buildNormalizedProductCorpus($product); $corpus = $this->buildNormalizedProductCorpus($product);
foreach (ShopServiceConfig::ACCESSORY_PRODUCT_KEYWORDS as $keyword) { foreach ($this->shopConfig->getAccessoryProductKeywords() as $keyword) {
if (str_contains($corpus, $this->normalizeForMatching($keyword))) { if (str_contains($corpus, $this->normalizeForMatching($keyword))) {
return true; return true;
} }
@@ -1012,7 +950,7 @@ final readonly class ShopSearchService
{ {
$corpus = $this->buildNormalizedProductCorpus($product); $corpus = $this->buildNormalizedProductCorpus($product);
foreach (ShopServiceConfig::DEVICE_PRODUCT_KEYWORDS as $keyword) { foreach ($this->shopConfig->getDeviceProductKeywords() as $keyword) {
if (str_contains($corpus, $this->normalizeForMatching($keyword))) { if (str_contains($corpus, $this->normalizeForMatching($keyword))) {
return true; return true;
} }
@@ -1058,15 +996,15 @@ final readonly class ShopSearchService
{ {
return array_values(array_filter( return array_values(array_filter(
$tokens, $tokens,
static fn(string $token): bool => preg_match('/\d/u', $token) === 1 fn(string $token): bool => preg_match($this->shopConfig->getContainsDigitPattern(), $token) === 1
)); ));
} }
private function normalizeForMatching(string $value): string private function normalizeForMatching(string $value): string
{ {
$value = mb_strtolower(trim($value), 'UTF-8'); $value = mb_strtolower(trim($value), 'UTF-8');
$value = preg_replace('/[^\p{L}\p{N}]+/u', ' ', $value) ?? $value; $value = preg_replace($this->shopConfig->getMatchingCleanupPattern(), ' ', $value) ?? $value;
$value = preg_replace('/\s+/u', ' ', $value) ?? $value; $value = preg_replace($this->shopConfig->getWhitespaceCollapsePattern(), ' ', $value) ?? $value;
return trim($value); return trim($value);
} }
@@ -1080,7 +1018,12 @@ final readonly class ShopSearchService
return []; return [];
} }
return preg_split('/[^\p{L}\p{N}]+/u', $value, -1, PREG_SPLIT_NO_EMPTY) ?: []; return preg_split(
$this->shopConfig->getTokenSplitPattern(),
$value,
-1,
PREG_SPLIT_NO_EMPTY
) ?: [];
} }
private function containsWholePhrase(string $normalizedText, string $normalizedPhrase): bool private function containsWholePhrase(string $normalizedText, string $normalizedPhrase): bool
@@ -1089,7 +1032,10 @@ final readonly class ShopSearchService
return false; return false;
} }
return str_contains(' ' . $normalizedText . ' ', ' ' . $normalizedPhrase . ' '); return str_contains(
$this->shopConfig->wrapWithPaddingSpaces($normalizedText),
$this->shopConfig->wrapWithPaddingSpaces($normalizedPhrase)
);
} }
/** /**
@@ -1097,11 +1043,26 @@ final readonly class ShopSearchService
*/ */
private function getRelevantCustomFields(array $customField): string private function getRelevantCustomFields(array $customField): string
{ {
$result = ($customField['migration_Backup_product_attr1'] ?? '') . ': ' . ($customField['migration_Backup_product_attr2'] ?? ''); $primary = (string) ($customField[$this->shopConfig->getPrimaryCustomFieldKey()] ?? '');
$result .= ' | Einsatzgebiete: ' . ($customField['migration_Backup_product_attr4'] ?? ''); $secondary = (string) ($customField[$this->shopConfig->getSecondaryCustomFieldKey()] ?? '');
$result .= ' | Sprachen: ' . ($customField['migration_Backup_product_attr5'] ?? ''); $useCases = (string) ($customField[$this->shopConfig->getUseCasesCustomFieldKey()] ?? '');
$languages = (string) ($customField[$this->shopConfig->getLanguagesCustomFieldKey()] ?? '');
return trim($result); $parts = [];
if ($primary !== '' || $secondary !== '') {
$parts[] = trim($primary . $this->shopConfig->getPrimarySecondarySeparator() . $secondary);
}
if ($useCases !== '') {
$parts[] = $this->shopConfig->getUseCasesLabel() . $useCases;
}
if ($languages !== '') {
$parts[] = $this->shopConfig->getLanguagesLabel() . $languages;
}
return trim(implode($this->shopConfig->getCustomFieldJoinSeparator(), array_filter($parts)));
} }
/** /**
@@ -1115,11 +1076,11 @@ final readonly class ShopSearchService
$newDesc = strip_tags((string) ($description['translated']['description'])); $newDesc = strip_tags((string) ($description['translated']['description']));
$newDesc = html_entity_decode($newDesc); $newDesc = html_entity_decode($newDesc);
$newDesc = preg_replace('/^[ \t]*\R/m', '', $newDesc) ?? $newDesc; $newDesc = preg_replace($this->shopConfig->getDescriptionEmptyLinePattern(), '', $newDesc) ?? $newDesc;
$newDesc = preg_replace('/[ \t]{2,}/', ' ', $newDesc) ?? $newDesc; $newDesc = preg_replace($this->shopConfig->getDescriptionWhitespaceCleanupPattern(), ' ', $newDesc) ?? $newDesc;
$result = trim((string) $newDesc); $result = trim((string) $newDesc);
return mb_substr($result, 0, 1500); return mb_substr($result, 0, $this->shopConfig->getDescriptionMaxLength());
} }
/** /**
@@ -1165,7 +1126,12 @@ final readonly class ShopSearchService
$value = (float) $candidate; $value = (float) $candidate;
if ($value > 0.0) { if ($value > 0.0) {
return number_format($value, 2, ',', '.') . ' €'; return number_format(
$value,
$this->shopConfig->getPriceDecimals(),
$this->shopConfig->getPriceDecimalSeparator(),
$this->shopConfig->getPriceThousandsSeparator()
) . $this->shopConfig->getPriceSuffix();
} }
} }
@@ -1191,7 +1157,7 @@ final readonly class ShopSearchService
$path = $seoUrl['seoPathInfo'] ?? null; $path = $seoUrl['seoPathInfo'] ?? null;
if (is_string($path) && trim($path) !== '') { if (is_string($path) && trim($path) !== '') {
return '/' . ltrim($path, '/'); return $this->shopConfig->buildRelativeSeoUrl($path);
} }
} }
@@ -1216,11 +1182,13 @@ final readonly class ShopSearchService
$highlights = []; $highlights = [];
if (isset($row['available'])) { if (isset($row['available'])) {
$highlights[] = (bool) $row['available'] ? 'Verfügbar' : 'Nicht verfügbar'; $highlights[] = (bool) $row['available']
? $this->shopConfig->getAvailableHighlightLabel()
: $this->shopConfig->getUnavailableHighlightLabel();
} }
if (isset($row['productNumber']) && is_string($row['productNumber']) && trim($row['productNumber']) !== '') { if (isset($row['productNumber']) && is_string($row['productNumber']) && trim($row['productNumber']) !== '') {
$highlights[] = 'Produktnummer: ' . trim($row['productNumber']); $highlights[] = $this->shopConfig->getProductNumberHighlightPrefix() . trim($row['productNumber']);
} }
return array_values(array_unique($highlights)); return array_values(array_unique($highlights));
@@ -1236,7 +1204,7 @@ final readonly class ShopSearchService
$seen = []; $seen = [];
foreach ($products as $product) { foreach ($products as $product) {
$key = mb_strtolower(trim(implode('|', [ $key = mb_strtolower(trim(implode($this->shopConfig->getDeduplicationSeparator(), [
$product->id, $product->id,
$product->productNumber ?? '', $product->productNumber ?? '',
$product->name, $product->name,

View File

@@ -25,7 +25,6 @@ final class ShopServiceConfig
'monitor', 'monitor',
'monitore', 'monitore',
'controller', 'controller',
'controller',
'gerät für', 'gerät für',
'geraet fuer', 'geraet fuer',
'geräte für', 'geräte für',
@@ -131,4 +130,328 @@ final class ShopServiceConfig
'geräte', 'geräte',
'geraete', 'geraete',
]; ];
public function getTopProductLogLimit(): int
{
return 3;
}
/**
* @return string[]
*/
public function getDeviceFocusKeywords(): array
{
return [
'geräte',
'geraete',
'gerät',
'geraet',
'analysegerät',
'analysegeraet',
'messgerät',
'messgeraet',
'analysator',
'controller',
'monitor',
];
}
/**
* @return string[]
*/
public function getAccessoryFocusKeywords(): array
{
return [
'indikator',
'indikatoren',
'reagenz',
'reagenzien',
'zubehör',
'zubehor',
'ersatzteil',
'ersatzteile',
'verbrauchsmaterial',
'service set',
'serviceset',
'filter',
'pumpenkopf',
'motorblock',
];
}
/**
* @return array<string, string[]>
*/
public function getAccessoryFocusVariantMap(): array
{
return [
'indikator' => ['indikator', 'indikatoren'],
'indikatoren' => ['indikator', 'indikatoren'],
'reagenz' => ['reagenz', 'reagenzien'],
'reagenzien' => ['reagenz', 'reagenzien'],
'ersatzteil' => ['ersatzteil', 'ersatzteile'],
'ersatzteile' => ['ersatzteil', 'ersatzteile'],
'service set' => ['service set', 'serviceset', 'service-set'],
'serviceset' => ['service set', 'serviceset', 'service-set'],
'service-set' => ['service set', 'serviceset', 'service-set'],
];
}
/**
* @return string[]
*/
public function getDeviceQueryKeywords(): array
{
return self::DEVICE_QUERY_KEYWORDS;
}
/**
* @return string[]
*/
public function getAccessoryQueryKeywords(): array
{
return self::ACCESSORY_QUERY_KEYWORDS;
}
/**
* @return string[]
*/
public function getAccessoryProductKeywords(): array
{
return self::ACCESSORY_PRODUCT_KEYWORDS;
}
/**
* @return string[]
*/
public function getDeviceProductKeywords(): array
{
return self::DEVICE_PRODUCT_KEYWORDS;
}
public function getExactProductNumberPhraseScore(): int
{
return 160;
}
public function getExactProductNamePhraseScore(): int
{
return 90;
}
public function getExactManufacturerMatchScore(): int
{
return 40;
}
public function getBrandContainedInNameScore(): int
{
return 20;
}
public function getNameTokenOverlapWeight(): int
{
return 6;
}
public function getProductNumberTokenOverlapWeight(): int
{
return 10;
}
public function getCorpusTokenOverlapWeight(): int
{
return 2;
}
public function getNameNumberOverlapWeight(): int
{
return 18;
}
public function getProductNumberNumberOverlapWeight(): int
{
return 28;
}
public function getCorpusNumberOverlapWeight(): int
{
return 8;
}
public function getSizeMatchScore(): int
{
return 12;
}
public function getAvailabilityBonusScore(): int
{
return 1;
}
public function getDeviceQueryDeviceProductBonus(): int
{
return 60;
}
public function getDeviceQueryAccessoryPenalty(): int
{
return 120;
}
public function getAccessoryQueryAccessoryProductBonus(): int
{
return 30;
}
public function getAccessoryQueryDeviceProductBonus(): int
{
return 10;
}
public function getContainsDigitPattern(): string
{
return '/\d/u';
}
public function getMatchingCleanupPattern(): string
{
return '/[^\p{L}\p{N}]+/u';
}
public function getWhitespaceCollapsePattern(): string
{
return '/\s+/u';
}
public function getTokenSplitPattern(): string
{
return '/[^\p{L}\p{N}]+/u';
}
public function wrapWithPaddingSpaces(string $value): string
{
return ' ' . trim($value) . ' ';
}
/**
* @return string[]
*/
public function getPriceNormalizationSearch(): array
{
return ['€', ' ', '.'];
}
/**
* @return string[]
*/
public function getPriceNormalizationReplace(): array
{
return ['', '', ''];
}
public function getPrimaryCustomFieldKey(): string
{
return 'migration_Backup_product_attr1';
}
public function getSecondaryCustomFieldKey(): string
{
return 'migration_Backup_product_attr2';
}
public function getUseCasesCustomFieldKey(): string
{
return 'migration_Backup_product_attr4';
}
public function getLanguagesCustomFieldKey(): string
{
return 'migration_Backup_product_attr5';
}
public function getPrimarySecondarySeparator(): string
{
return ': ';
}
public function getUseCasesLabel(): string
{
return 'Einsatzgebiete: ';
}
public function getLanguagesLabel(): string
{
return 'Sprachen: ';
}
public function getCustomFieldJoinSeparator(): string
{
return ' | ';
}
public function getDescriptionEmptyLinePattern(): string
{
return '/^[ \t]*\R/m';
}
public function getDescriptionWhitespaceCleanupPattern(): string
{
return '/[ \t]{2,}/';
}
public function getDescriptionMaxLength(): int
{
return 1500;
}
public function getPriceDecimals(): int
{
return 2;
}
public function getPriceDecimalSeparator(): string
{
return ',';
}
public function getPriceThousandsSeparator(): string
{
return '.';
}
public function getPriceSuffix(): string
{
return ' €';
}
public function buildRelativeSeoUrl(string $path): string
{
return '/' . ltrim($path, '/');
}
public function getAvailableHighlightLabel(): string
{
return 'Verfügbar';
}
public function getUnavailableHighlightLabel(): string
{
return 'Nicht verfügbar';
}
public function getProductNumberHighlightPrefix(): string
{
return 'Produktnummer: ';
}
public function getMissingProductImagePlaceholder(): string
{
return 'no-image';
}
public function getDeduplicationSeparator(): string
{
return '|';
}
} }