В работе с данными часто возникает необходимость найти записи, которые похожи друг на друга, но не являются идентичными. Это может быть связано с опечатками, разными вариантами написания, синонимами или особенностями ввода информации. Традиционный поиск по точному совпадению в таких случаях неэффективен. Именно здесь на помощь приходит нечеткий поиск (или fuzzy search) — метод, позволяющий определить степень схожести между строками, даже если они содержат незначительные различия.
Мы вместе рассмотрим различные подходы к реализации нечеткого поиска в 1С:Предприятии, начиная от простых и часто достаточных, и заканчивая более сложными алгоритмами. Выясним, какие инструменты и методы платформы 1С помогут нам решить эту задачу эффективно, и проанализируем преимущества и недостатки каждого из них.
Этот подход является одним из самых простых в реализации и часто оказывается достаточным для большинства случаев нечеткого поиска. Он основан на идее предварительной подготовки (нормализации) строк и использовании встроенного в 1С оператора ПОДОБНО в запросах.
Прежде чем сравнивать строки, мы рекомендуем их нормализовать. Это поможет уменьшить количество различий, не связанных с сутью искомой информации, и повысит точность поиска. Давайте рассмотрим основные этапы нормализации:
НРег().СтрЗаменить().Посмотрим на пример кода для функции нормализации строки:
// Функция нормализует строку для нечеткого поиска
// ИсходнаяСтрока: Строка, которую нужно нормализовать
// Возвращает: Нормализованную строку
Функция НормализоватьСтрокуДляПоиска(ИсходнаяСтрока)
// 1. Приведение к нижнему регистру
ПеременнаяСтрока = НРег(ИсходнаяСтрока);
// 2. Замена "ё" на "е"
ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, "ё", "е");
// 3. Замена точек и других знаков пунктуации на пробелы
// Можно расширить список символов для замены
ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, ".", " ");
ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, ",", " ");
ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, "-", " ");
ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, "(", " ");
ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, ")", " ");
ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, """", " "); // Замена кавычек
ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, "/", " ");
ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, "\", " ");
ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, ":", " ");
ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, ";", " ");
// Удаляем повторяющиеся пробелы
Пока СтрНайти(ПеременнаяСтрока, " ") > 0 Цикл
ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, " ", " ");
КонецЦикла;
// Удаляем пробелы по краям
ПеременнаяСтрока = СокрЛП(ПеременнаяСтрока);
Возврат ПеременнаяСтрока;
КонецФункции
// Функция разбивает нормализованную строку на таблицу подстрок
// ИсходнаяСтрока: Нормализованная строка
// Возвращает: ТаблицуЗначений с колонками "Подстрока" и "ДлинаСтроки"
Функция ПолучитьТаблицуПодстрокДляПоиска(ИсходнаяСтрока)
ТаблицаПодстрок = Новый ТаблицаЗначений;
ТаблицаПодстрок.Колонки.Добавить("Подстрока");
ТаблицаПодстрок.Колонки.Добавить("ДлинаСтроки", Новый ОписаниеТипов("Число"));
МассивСлов = СтрРазделить(ИсходнаяСтрока, " ", Ложь); // Разбиваем по пробелам
Для Каждого Слово Из МассивСлов Цикл
Если Не ПустаяСтрока(Слово) Тогда
// Формируем паттерн для ПОДОБНО
Паттерн = "%" + Слово + "%";
НоваяСтрока = ТаблицаПодстрок.Добавить();
НоваяСтрока.Подстрока = Паттерн;
НоваяСтрока.ДлинаСтроки = СтрДлина(Слово);
КонецЕсли;
КонецЦикла;
// Дополнительно можно добавить паттерн для всей строки целиком
Если Не ПустаяСтрока(ИсходнаяСтрока) Тогда
НоваяСтрока = ТаблицаПодстрок.Добавить();
НоваяСтрока.Подстрока = "%" + ИсходнаяСтрока + "%";
НоваяСтрока.ДлинаСтроки = СтрДлина(ИсходнаяСтрока);
КонецЕсли;
Возврат ТаблицаПодстрок;
КонецФункции
После того как мы подготовили таблицу подстрок, мы можем использовать ее в запросе 1С с оператором ПОДОБНО. Этот оператор позволяет искать строки, соответствующие определенному шаблону. Для расширения возможностей стандартных отборов в интерфейсе можно применить расширение для поиска по списку соответствий шаблону, что значительно упростит работу пользователям — для этого есть расширение для поиска по нескольким фрагментам в 1С.
Мы создадим временную таблицу из нашей таблицы подстрок, а затем выполним соединение с целевой таблицей, в которой ищем похожие наименования. Чтобы приоритизировать более релевантные совпадения, мы будем учитывать длину подстроки, по которой произошло совпадение.
// Пример использования в запросе 1С
// Предполагается, что у нас есть ТаблицаПодстрок, полученная ранее
// из искомой строки (например, "Иванов Петр")
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.Подстрока,
| ТЗ.ДлинаСтроки
|ПОМЕСТИТЬ ВТ_Подстроки
|ИЗ
| &ТаблицаПодстрок КАК ТЗ
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ ПЕРВЫЕ 10 // Получим до 10 наиболее похожих
| ГдеИщем.Наименование,
| МАКСИМУМ(ВТ_Подстроки.ДлинаСтроки) КАК Приоритет
|ИЗ
| Справочник.Контрагенты КАК ГдеИщем // Замените на ваш справочник/документ
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Подстроки КАК ВТ_Подстроки
| ПО (ГдеИщем.Наименование ПОДОБНО ВТ_Подстроки.Подстрока)
|ГДЕ
| НЕ ГдеИщем.ПометкаУдаления // Исключаем удаленные элементы
|СГРУППИРОВАТЬ ПО
| ГдеИщем.Наименование
|УПОРЯДОЧИТЬ ПО
| Приоритет УБЫВ";
Запрос.УстановитьПараметр("ТаблицаПодстрок", ТаблицаПодстрок); // Передаем нашу ТЗ
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить("Найдено: " + Выборка.Наименование + ", Приоритет: " + Выборка.Приоритет);
КонецЦикла;
Преимущества этого метода: простота реализации, хорошая производительность для умеренных объемов данных, возможность использования стандартных средств запросов 1С.
Недостатки: не учитывает порядок слов, нечувствителен к опечаткам внутри слов.
Если нам требуется более точный и математически обоснованный подход к определению схожести строк, мы можем использовать алгоритм расстояния Левенштейна. Для более продвинутых задач рекомендуется использовать улучшенный алгоритм Левенштейна для 1С, который учитывает лингвистические особенности русского языка и обеспечивает интеллектуальное сравнение.
Расстояние Левенштейна (или редакционное расстояние) — это минимальное количество операций вставки, удаления или замены одного символа, необходимых для превращения одной строки в другую. Чем меньше расстояние, тем более похожи строки.
Давайте рассмотрим реализацию базовой функции для расчета расстояния Левенштейна на встроенном языке 1С. Мы будем использовать динамическое программирование для построения матрицы:
// Функция РасстояниеЛевенштейна
// Строка1: Первая строка для сравнения
// Строка2: Вторая строка для сравнения
// Возвращает: Целое число - расстояние Левенштейна между Строка1 и Строка2
Функция РасстояниеЛевенштейна(Строка1, Строка2)
len1 = СтрДлина(Строка1);
len2 = СтрДлина(Строка2);
матрица = Новый Массив(len1 + 1);
Для i = 0 По len1 Цикл
матрица[i] = Новый Массив(len2 + 1);
КонецЦикла;
Для j = 0 По len2 Цикл
матрица[0][j] = j;
КонецЦикла;
Для i = 0 По len1 Цикл
матрица[i][0] = i;
КонецЦикла;
Для i = 1 По len1 Цикл
Для j = 1 По len2 Цикл
символ1 = Сред(Строка1, i, 1);
символ2 = Сред(Строка2, j, 1);
cost = ?(символ1 = символ2, 0, 1);
матрица[i][j] = Мин(
матрица[i-1][j] + 1, // Удаление
матрица[i][j-1] + 1, // Вставка
матрица[i-1][j-1] + cost // Замена
);
КонецЦикла;
КонецЦикла;
Возврат матрица[len1][len2];
КонецФункции
Как использовать: Вы можете применить эту функцию, перебирая элементы справочника. Чтобы не писать код с нуля, можно воспользоваться готовой обработкой по поиску по схожести наименований, которая уже реализует подобную логику — для этого подойдёт готовая обработка поиска и устранения дублей в 1С.
Пример использования:
ИскомаяСтрока = "Апельсин";
ПорогСхожести = 2; // Максимальное допустимое расстояние
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Наименование ИЗ Справочник.Номенклатура ГДЕ НЕ ПометкаУдаления";
ВыборкаНоменклатуры = Запрос.Выполнить().Выбрать();
Пока ВыборкаНоменклатуры.Следующий() Цикл
Расстояние = РасстояниеЛевенштейна(ИскомаяСтрока, ВыборкаНоменклатуры.Наименование);
Если Расстояние <= ПорогСхожести Тогда
Сообщить("Найдено похожее: " + ВыборкаНоменклатуры.Наименование + ", Расстояние: " + Расстояние);
КонецЕсли;
КонецЦикла;
Помимо рассмотренных выше методов, существуют и другие, более специализированные средства, которые могут быть полезны.
Платформа 1С:Предприятие имеет встроенный механизм полнотекстового поиска.
В стандартных конфигурациях часто требуется найти и объединить похожие записи — для этого подойдёт обработка интеллектуального сопоставления номенклатуры в 1С. Для анализа результатов поиска дублей в обычном интерфейсе может быть крайне полезен отчет по дублям элементов справочников, который наглядно выводит найденные совпадения.
Нечеткий поиск может быть ресурсоемким. Мы должны учитывать следующие моменты при оптимизации:
ПОДОБНО, для точных — алгоритмы редакционного расстояния.Выбор конкретного метода зависит от требований к точности и объему данных. Мы рекомендуем начать с простых подходов и использовать специализированные внешние обработки и расширения, если стандартных средств становится недостаточно.