Реалізація автоматичного маппінгу характеристик товарів з фільтрами сайту

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація автоматичного маппінгу характеристик товарів з фільтрами сайту
Складна
~5 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація автоматичного маппінгу характеристик товарів з фільтрами сайту

Постачальники називають одні й ті ж характеристики по-різному: «колір», «Колір виробу», «color», «Colour», «RAL». На сайті — один фільтр «Колір». Ручне приведення характеристик від десятків постачальників до фільтрів сайту — це сотні годин роботи, які потрібно автоматизувати.

Архітектура системи маппінгу

Три рівні абстракції:

Характеристика постачальника  →  Атрибут сайту  →  Значення фільтра
"Колір: Червоний"             →  color           →  "Червоний"
"color: Red"                   →  color           →  "Червоний"  (з нормалізацією)
"RAL 3020"                     →  color           →  "Червоний"  (через довідник RAL)

Таблиця маппінгу імен атрибутів:

CREATE TABLE attribute_name_mapping (
    id               serial PRIMARY KEY,
    supplier_id      int,          -- NULL = універсальний маппінг
    supplier_name    varchar(200), -- як називає постачальник
    site_attribute   varchar(100), -- внутрішній ключ атрибуту
    created_at       timestamptz DEFAULT now(),
    UNIQUE (supplier_id, supplier_name)
);

Таблиця маппінгу значень атрибутів:

CREATE TABLE attribute_value_mapping (
    id               serial PRIMARY KEY,
    site_attribute   varchar(100),
    supplier_value   varchar(500),
    site_value       varchar(500),  -- нормалізоване значення
    supplier_id      int,           -- NULL = для всіх
    UNIQUE (site_attribute, supplier_value, supplier_id)
);

Алгоритм маппінгу

class AttributeMapper
{
    public function mapAttribute(
        string $supplierName,
        string $supplierValue,
        ?int   $supplierId = null
    ): ?MappedAttribute {
        // Крок 1: знайти маппінг імені атрибуту
        $siteAttribute = $this->resolveAttributeName($supplierName, $supplierId);
        if (!$siteAttribute) {
            $this->logUnknownAttribute($supplierName, $supplierId);
            return null;
        }

        // Крок 2: знайти маппінг значення
        $siteValue = $this->resolveAttributeValue($siteAttribute, $supplierValue, $supplierId);
        if (!$siteValue) {
            // Спробуємо нормалізувати без маппінгу
            $siteValue = $this->autoNormalizeValue($siteAttribute, $supplierValue);
        }

        return new MappedAttribute($siteAttribute, $siteValue);
    }

    private function resolveAttributeName(string $name, ?int $supplierId): ?string
    {
        $normalized = mb_strtolower(trim($name));

        // Спочатку специфічний маппінг для постачальника
        if ($supplierId) {
            $mapping = AttributeNameMapping::where([
                'supplier_id'   => $supplierId,
                'supplier_name' => $normalized,
            ])->value('site_attribute');
            if ($mapping) return $mapping;
        }

        // Потім універсальний
        return AttributeNameMapping::whereNull('supplier_id')
            ->where('supplier_name', $normalized)
            ->value('site_attribute');
    }
}

Автонормалізація значень

Частину значень не потребує словникового маппінгу — їх можна привести до стандарту автоматично:

private function autoNormalizeValue(string $attribute, string $raw): string
{
    return match ($attribute) {
        'spec_weight_kg' => $this->parseWeight($raw),
        'spec_dimensions' => $this->parseDimensions($raw),
        'spec_voltage'  => $this->parseVoltage($raw),
        default         => $this->capitalizeFirst($raw),
    };
}

private function parseWeight(string $raw): string
{
    // "2,5 кг" | "2500 г" | "2.5kg" → "2.5"
    if (preg_match('/(\d+[.,]\d+|\d+)\s*(г|kg)/iu', $raw, $m)) {
        $value = (float) str_replace(',', '.', $m[1]);
        $unit = strtolower($m[2]);
        return (string) ($unit === 'г' ? $value / 1000 : $value);
    }
    return $raw;
}

Fuzzy-маппінг нових значень

Для нових значень, яких ще немає в маппінгу, використовуємо pg_trgm схожість:

public function suggestValueMapping(string $attribute, string $supplierValue): array
{
    $normalized = mb_strtolower(trim($supplierValue));

    return DB::select(
        "SELECT site_value,
                similarity(lower(supplier_value), ?) AS score
         FROM attribute_value_mapping
         WHERE site_attribute = ?
           AND similarity(lower(supplier_value), ?) > 0.6
         ORDER BY score DESC
         LIMIT 5",
        [$normalized, $attribute, $normalized]
    );
}

Пропонуємо оператору: «Схоже, "Черв." = "Червоний" (score 0.82). Створити маппінг?»

Автонавчання з підтверджених маппінгів

Коли оператор приймає запропонований маппінг — він попадає в attribute_value_mapping. При наступному імпорті від цього постачальника значення маппиться автоматично.

public function confirmMapping(
    string $attribute,
    string $supplierValue,
    string $siteValue,
    ?int   $supplierId
): void {
    AttributeValueMapping::updateOrCreate(
        [
            'site_attribute'  => $attribute,
            'supplier_value'  => mb_strtolower(trim($supplierValue)),
            'supplier_id'     => $supplierId,
        ],
        ['site_value' => $siteValue]
    );
}

Управління фільтрами: зв'язок атрибутів та фасетів

Кожен сопоставлений атрибут прив'язаний до фільтра сайту:

CREATE TABLE filter_attributes (
    filter_id    int REFERENCES filters(id),
    attribute    varchar(100),
    display_name varchar(200),
    sort         smallint,
    PRIMARY KEY (filter_id, attribute)
);

Після маппінгу значення пишуться в денормалізовану таблицю product_filter_values — саме по ній працює пошук з фасетною фільтрацією:

CREATE TABLE product_filter_values (
    product_id   int,
    filter_id    int,
    value        varchar(500),
    value_slug   varchar(500),
    PRIMARY KEY (product_id, filter_id, value)
);
CREATE INDEX pfv_filter_value_idx ON product_filter_values (filter_id, value_slug);

Нераспізнані атрибути: чергу обробки

Всі атрибути, для яких не знайшлось маппінгу, накопичуються в черзі:

CREATE TABLE unmapped_attributes (
    id             serial PRIMARY KEY,
    supplier_id    int,
    attribute_name varchar(200),
    sample_values  text[],       -- до 10 прикладів
    occurrences    int DEFAULT 1,
    first_seen_at  timestamptz DEFAULT now()
);

В админці — таблиця з частотою. Найчастіші атрибути — першые кандидати для створення маппінгу.

Тривалість реалізації

  • Базовий маппінг імен атрибутів (словник + запит) + запис у product_filter_values3 дні
  • Автонормалізація числових значень (вага, розміри, напруга) — +1 день
  • Fuzzy-пропозиції + чергу нераспізнаних + admin UI — +2–3 дні
  • Повна інтеграція з фасетним пошуком та пересчет при змінах маппінгу — +1–2 дні