Как реализовать нечеткий поиск строк в 1С:Предприятии для нахождения похожих значений?

Программист 1С v8.3 (Управляемые формы)
← На главную

В работе с данными часто возникает необходимость найти записи, которые похожи друг на друга, но не являются идентичными. Это может быть связано с опечатками, разными вариантами написания, синонимами или особенностями ввода информации. Традиционный поиск по точному совпадению в таких случаях неэффективен. Именно здесь на помощь приходит нечеткий поиск (или fuzzy search) — метод, позволяющий определить степень схожести между строками, даже если они содержат незначительные различия.

Мы вместе рассмотрим различные подходы к реализации нечеткого поиска в 1С:Предприятии, начиная от простых и часто достаточных, и заканчивая более сложными алгоритмами. Выясним, какие инструменты и методы платформы 1С помогут нам решить эту задачу эффективно, и проанализируем преимущества и недостатки каждого из них.

Метод 1: Простой поиск с оператором "ПОДОБНО" и предварительной обработкой строк

Этот подход является одним из самых простых в реализации и часто оказывается достаточным для большинства случаев нечеткого поиска. Он основан на идее предварительной подготовки (нормализации) строк и использовании встроенного в 1С оператора ПОДОБНО в запросах.

Шаг 1: Подготовка и нормализация строк

Прежде чем сравнивать строки, мы рекомендуем их нормализовать. Это поможет уменьшить количество различий, не связанных с сутью искомой информации, и повысит точность поиска. Давайте рассмотрим основные этапы нормализации:

  1. Приведение регистра: Преобразуем все символы строки к одному регистру (например, к нижнему), чтобы избежать различий из-за прописных и строчных букв. Для этого используем функцию НРег().
  2. Замена схожих символов: Заменим букву "ё" на "е", так как они часто взаимозаменяемы в русском языке и могут быть причиной несовпадений при поиске. Используем функцию СтрЗаменить().
  3. Удаление пунктуации и лишних пробелов: Заменим все знаки препинания на пробелы, а затем удалим повторяющиеся пробелы и пробелы по краям строки. Это поможет избежать различий из-за лишних символов и форматирования.
  4. Разбиение на подстроки: Разобьем нормализованную строку на отдельные слова или части по пробелам. Это позволит нам искать совпадения по отдельным компонентам строки, а не по всей строке целиком, что значительно увеличивает гибкость поиска.

Посмотрим на пример кода для функции нормализации строки:


// Функция нормализует строку для нечеткого поиска
// ИсходнаяСтрока: Строка, которую нужно нормализовать
// Возвращает: Нормализованную строку
Функция НормализоватьСтрокуДляПоиска(ИсходнаяСтрока)
    // 1. Приведение к нижнему регистру
    ПеременнаяСтрока = НРег(ИсходнаяСтрока);

    // 2. Замена "ё" на "е"
    ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, "ё", "е");

    // 3. Замена точек и других знаков пунктуации на пробелы
    // Можно расширить список символов для замены
    ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, ".", " ");
    ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, ",", " ");
    ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, "-", " ");
    ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, "(", " ");
    ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, ")", " ");
    ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, """", " "); // Замена кавычек
    ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, "/", " ");
    ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, "\", " ");
    ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, ":", " ");
    ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, ";", " ");

    // Удаляем повторяющиеся пробелы
    Пока СтрНайти(ПеременнаяСтрока, "  ") > 0 Цикл
        ПеременнаяСтрока = СтрЗаменить(ПеременнаяСтрока, "  ", " ");
    КонецЦикла;

    // Удаляем пробелы по краям
    ПеременнаяСтрока = СокрЛП(ПеременнаяСтрока);

    Возврат ПеременнаяСтрока;
КонецФункции

// Функция разбивает нормализованную строку на таблицу подстрок
// ИсходнаяСтрока: Нормализованная строка
// Возвращает: ТаблицуЗначений с колонками "Подстрока" и "ДлинаСтроки"
Функция ПолучитьТаблицуПодстрокДляПоиска(ИсходнаяСтрока)
    ТаблицаПодстрок = Новый ТаблицаЗначений;
    ТаблицаПодстрок.Колонки.Добавить("Подстрока");
    ТаблицаПодстрок.Колонки.Добавить("ДлинаСтроки", Новый ОписаниеТипов("Число"));

    МассивСлов = СтрРазделить(ИсходнаяСтрока, " ", Ложь); // Разбиваем по пробелам

    Для Каждого Слово Из МассивСлов Цикл
        Если Не ПустаяСтрока(Слово) Тогда
            // Формируем паттерн для ПОДОБНО
            Паттерн = "%" + Слово + "%";
            НоваяСтрока = ТаблицаПодстрок.Добавить();
            НоваяСтрока.Подстрока = Паттерн;
            НоваяСтрока.ДлинаСтроки = СтрДлина(Слово);
        КонецЕсли;
    КонецЦикла;

    // Дополнительно можно добавить паттерн для всей строки целиком
    Если Не ПустаяСтрока(ИсходнаяСтрока) Тогда
        НоваяСтрока = ТаблицаПодстрок.Добавить();
        НоваяСтрока.Подстрока = "%" + ИсходнаяСтрока + "%";
        НоваяСтрока.ДлинаСтроки = СтрДлина(ИсходнаяСтрока);
    КонецЕсли;

    Возврат ТаблицаПодстрок;
КонецФункции

Шаг 2: Построение запроса с оператором "ПОДОБНО"

После того как мы подготовили таблицу подстрок, мы можем использовать ее в запросе 1С с оператором ПОДОБНО. Этот оператор позволяет искать строки, соответствующие определенному шаблону. Для расширения возможностей стандартных отборов в интерфейсе можно применить расширение для поиска по списку соответствий шаблону, что значительно упростит работу пользователям — для этого есть расширение для поиска по нескольким фрагментам в 1С.

Мы создадим временную таблицу из нашей таблицы подстрок, а затем выполним соединение с целевой таблицей, в которой ищем похожие наименования. Чтобы приоритизировать более релевантные совпадения, мы будем учитывать длину подстроки, по которой произошло совпадение.


// Пример использования в запросе 1С
// Предполагается, что у нас есть ТаблицаПодстрок, полученная ранее
// из искомой строки (например, "Иванов Петр")

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
    |    ТЗ.Подстрока,
    |    ТЗ.ДлинаСтроки
    |ПОМЕСТИТЬ ВТ_Подстроки
    |ИЗ
    |    &ТаблицаПодстрок КАК ТЗ
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ ПЕРВЫЕ 10 // Получим до 10 наиболее похожих
    |    ГдеИщем.Наименование,
    |    МАКСИМУМ(ВТ_Подстроки.ДлинаСтроки) КАК Приоритет
    |ИЗ
    |    Справочник.Контрагенты КАК ГдеИщем // Замените на ваш справочник/документ
    |        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Подстроки КАК ВТ_Подстроки
    |        ПО (ГдеИщем.Наименование ПОДОБНО ВТ_Подстроки.Подстрока)
    |ГДЕ
    |    НЕ ГдеИщем.ПометкаУдаления // Исключаем удаленные элементы
    |СГРУППИРОВАТЬ ПО
    |    ГдеИщем.Наименование
    |УПОРЯДОЧИТЬ ПО
    |    Приоритет УБЫВ";

Запрос.УстановитьПараметр("ТаблицаПодстрок", ТаблицаПодстрок); // Передаем нашу ТЗ

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    Сообщить("Найдено: " + Выборка.Наименование + ", Приоритет: " + Выборка.Приоритет);
КонецЦикла;

Преимущества этого метода: простота реализации, хорошая производительность для умеренных объемов данных, возможность использования стандартных средств запросов 1С.

Недостатки: не учитывает порядок слов, нечувствителен к опечаткам внутри слов.

Метод 2: Алгоритм расстояния Левенштейна

Если нам требуется более точный и математически обоснованный подход к определению схожести строк, мы можем использовать алгоритм расстояния Левенштейна. Для более продвинутых задач рекомендуется использовать улучшенный алгоритм Левенштейна для 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; // Максимальное допустимое расстояние

Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Наименование ИЗ Справочник.Номенклатура ГДЕ НЕ ПометкаУдаления";
ВыборкаНоменклатуры = Запрос.Выполнить().Выбрать();

Пока ВыборкаНоменклатуры.Следующий() Цикл
    Расстояние = РасстояниеЛевенштейна(ИскомаяСтрока, ВыборкаНоменклатуры.Наименование);
    Если Расстояние <= ПорогСхожести Тогда
        Сообщить("Найдено похожее: " + ВыборкаНоменклатуры.Наименование + ", Расстояние: " + Расстояние);
    КонецЕсли;
КонецЦикла;

Метод 3: Другие алгоритмы нечеткого поиска и встроенные механизмы 1С

Помимо рассмотренных выше методов, существуют и другие, более специализированные средства, которые могут быть полезны.

1. Другие алгоритмические подходы

2. Встроенный полнотекстовый поиск 1С и БСП

Платформа 1С:Предприятие имеет встроенный механизм полнотекстового поиска.

3. Поиск дублей и сопоставление номенклатуры

В стандартных конфигурациях часто требуется найти и объединить похожие записи — для этого подойдёт обработка интеллектуального сопоставления номенклатуры в 1С. Для анализа результатов поиска дублей в обычном интерфейсе может быть крайне полезен отчет по дублям элементов справочников, который наглядно выводит найденные совпадения.

Производительность и оптимизация

Нечеткий поиск может быть ресурсоемким. Мы должны учитывать следующие моменты при оптимизации:

Выбор конкретного метода зависит от требований к точности и объему данных. Мы рекомендуем начать с простых подходов и использовать специализированные внешние обработки и расширения, если стандартных средств становится недостаточно.

← На главную