Загрузка прайс-листов сторонних поставщиков в систему 1С — это классическая задача, с которой сталкивается практически каждый программист в сфере торговли. Основная сложность заключается в том, что наименования одних и тех же товаров у разных поставщиков могут значительно отличаться. Использование стандартного полнотекстового поиска 1С не всегда дает нужный результат, а специализированные компоненты вроде FuzzySearch (или StrMatch.dll) при больших объемах данных начинают демонстрировать низкую производительность. Рассмотрим подробнее, как оптимизировать этот процесс, внедрив современные алгоритмы и правильную архитектуру данных.
Стандартные компоненты нечеткого поиска часто работают по принципу прямого перебора. Если подать на вход строку, содержащую тысячи наименований номенклатуры, алгоритм будет строить матрицы сравнения для каждой пары, что приведет к квадратичному росту нагрузки. Проанализируем ситуацию: передача «громадной строки» со всеми наименованиями в COM-объект или внешнюю компоненту может вызвать не только задержки, но и критическое потребление оперативной памяти. Выясним причину замедления — чаще всего она кроется в отсутствии предварительной фильтрации и избыточности сравниваемых данных.
Прежде чем приступать к нечеткому сравнению, необходимо максимально сблизить строки «на берегу». Проанализируем методику нормализации, которая позволяет отсечь до 50% мусорных несовпадений. Разберем по шагам, что нужно сделать с наименованием перед поиском:
Рассмотрим пример функции нормализации на языке 1С:
Функция НормализоватьСтроку(Знач ВходнаяСтрока)
// Убираем лишние символы
ДопустимыеСимволы = "абвгдеёжзийклмнопрстуфхцчшщъыьэюя0123456789abcdefghijklmnopqrstuvwxyz";
Результат = "";
ВходнаяСтрока = НРег(ВходнаяСтрока);
Для Индекс = 1 По СтрДлина(ВходнаяСтрока) Цикл
Символ = Сред(ВходнаяСтрока, Индекс, 1);
Если Найти(ДопустимыеСимволы, Символ) > 0 Тогда
Результат = Результат + Символ;
Иначе
Результат = Результат + " ";
КонецЕсли;
КонецЦикла;
// Удаляем двойные пробелы и сортируем слова (упрощенно)
СписокСлов = СтрРазделить(Результат, " ", Ложь);
СписокСлов.Сортировать();
Возврат СтрСоединить(СписокСлов, " ");
КонецФункции
Вместо того чтобы сравнивать каждую строку прайса с каждой строкой справочника Номенклатура, используем идею хеширования. Посмотрим на алгоритм Charikar (SimHash). Его особенность в том, что у похожих строк получаются похожие хеши. Разберем принцип работы:
Мы заранее вычисляем хеш для всей нашей номенклатуры и храним его в отдельном реквизите или регистре. При загрузке прайса вычисляем хеш для строки поставщика. Теперь вместо тяжелого нечеткого поиска мы можем использовать быстрый поиск по диапазону хешей или вычислять «расстояние Хэмминга» между битами хеша, что выполняется на порядок быстрее, чем сравнение строк. Также хорошие результаты показывает метод N-gram (разбиение строки на фрагменты по N символов), который позволяет быстро отсеять заведомо неподходящие варианты.
Проанализируем систему организации труда операторов, которая позволяет обрабатывать тысячи позиций за приемлемое время. Одной технической компоненты недостаточно, нужен правильный бизнес-процесс:
НоменклатураПоставщиков). При повторной загрузке система сначала ищет точное совпадение в этом регистре.Если производительность внутри 1С все еще не устраивает, рассмотрим вариант выноса логики вовне. Напишем небольшой HTTP-сервис на Python, используя библиотеки RapidFuzz или TheFuzz. Эти библиотеки реализованы на C++ и работают в десятки раз быстрее любой компоненты 1С. Процесс будет выглядеть так:
ElasticSearch или простую SQL-таблицу с индексами).Посмотрим на пример использования расстояния Левенштейна (алгоритм Вагнера-Фишера), который можно реализовать даже средствами 1С для небольших списков:
Функция РасстояниеЛевенштейна(Стр1, Стр2)
н = СтрДлина(Стр1);
м = СтрДлина(Стр2);
Если н = 0 Тогда Возврат м; КонецЕсли;
Если м = 0 Тогда Возврат н; КонецЕсли;
Матрица = Новый Массив(н + 1, м + 1);
Для и = 0 По н Цикл Матрица[и][0] = и; КонецЦикла;
Для й = 0 По м Цикл Матрица[0][й] = й; КонецЦикла;
Для и = 1 По н Цикл
Для й = 1 По м Цикл
Стоимость = ?(Сред(Стр1, и, 1) = Сред(Стр2, й, 1), 0, 1);
Матрица[и][й] = Мин(Матрица[и-1][й] + 1,
Матрица[и][й-1] + 1,
Матрица[и-1][й-1] + Стоимость);
КонецЦикла;
КонецЦикла;
Возврат Матрица[н][м];
КонецФункции
Для русского языка крайне эффективны фонетические алгоритмы. Разберем ситуацию: поставщик написал «карова», а у нас «корова». Обычный поиск по подстроке не сработает. Фонетический алгоритм (адаптированный Metaphone) преобразует оба слова в одну и ту же маску «КАРАВА». Сравнение таких масок позволяет нивелировать ошибки в написании гласных и созвучных согласных. Дополнительно применим стемминг — отсечение окончаний, чтобы слова «красный», «красная», «красного» превратились в основу «красн».
Важный совет: Для ускорения работы в 1С 8.3 активно используйте ФоновыеЗадания. Разбейте прайс на несколько частей (например, по 500 строк) и запустите их обработку параллельно. Это позволит задействовать все ядра процессора сервера и сократить время ожидания в несколько раз.
Таким образом, комбинируя предварительную нормализацию, предварительный расчет хешей и кэширование результатов в регистрах 1С, можно добиться высокой скорости сопоставления даже на прайсах в десятки тысяч строк, не полагаясь только на возможности внешних компонент.