Перейти к содержанию

Сопоставление документов с реестром

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

Приведение значений к единому виду

Перед сравнением исходные строки приводятся к нормализованному представлению. Способ нормализации зависит от типа поля.

Текст

Функция _normalize_text приводит строку к нижнему регистру, удаляет знаки препинания (точки, запятые, скобки, кавычки, тире, косую черту) и схлопывает повторяющиеся пробелы. В результате строки «ООО Ромашка» и ооо ромашка становятся одинаковыми.

Номера

Функция _normalize_number оставляет из строки только цифры и отбрасывает ведущие нули. Так, № 007/45 и 00745 оба превращаются в 745. Сравнение номеров всегда точное: если нормализованные цифры совпали --- оценка 1.0, иначе --- 0.0.

Даты

Нормализация дат выполняется через централизованный модуль date_utils (подробнее --- в разделе Работа с неоднозначными датами). При сравнении учитываются все возможные толкования даты, а оценка уверенности зависит от того, были ли даты однозначными.

Суммы

Из строки удаляются все символы кроме цифр, точки и запятой. Запятая заменяется на точку. Если в результате получается несколько точек (например, 1.234.567,89), все кроме последней трактуются как разделители тысяч. Затем значения сравниваются как числа с плавающей точкой:

Расхождение Оценка
Менее 0,01 (абсолютное) 1.0
Менее 1 % (относительное) 0.95
Менее 5 % (относительное) 0.8
Более 5 % 0.0

Способы сравнения

Модуль поддерживает два способа поиска: точный (по указателю) и приблизительный (нечёткий).

Точный поиск по указателю

При загрузке документов из базы данных модуль строит указатель --- словарь, где ключ --- нормализованное значение поля, а значение --- список идентификаторов документов. Для номеров ключом служит строка из цифр, для дат --- все возможные нормализации в формате ГГГГ-ММ-ДД.

Поиск по указателю работает за постоянное время: нормализованное значение ячейки реестра ищется в словаре напрямую. Если найдено --- возвращается список документов без перебора.

Для дат в указатель записываются все толкования. Например, если документ содержит дату 03/05/2023, в указатель попадают две записи: 2023-05-03 и 2023-03-05. При поиске значение ячейки реестра тоже раскладывается на все толкования, и каждое проверяется по указателю.

Приблизительный (нечёткий) поиск

Применяется к текстовым полям (контрагент, тема, примечание). Используются три алгоритма из библиотеки rapidfuzz (при её отсутствии --- из стандартной библиотеки difflib):

  • Полное сходство (ratio) --- посимвольное сравнение двух строк целиком;
  • Частичное сходство (partial_ratio) --- поиск лучшего совпадения подстроки внутри более длинной строки;
  • Сортировка по словам (token_sort_ratio) --- слова в обеих строках сортируются по алфавиту, затем сравниваются посимвольно.

Итоговая оценка --- максимум из трёх значений (с понижающими коэффициентами 0.95 для частичного и 0.9 для сортировки по словам).

Оптимизации, ускоряющие нечёткий поиск:

  • если длины строк различаются более чем втрое --- сравнение пропускается;
  • если порог совпадения 80 % и выше, допускается расхождение длин не более чем вдвое;
  • кандидаты сортируются по близости длины к строке из реестра, и проверяются только первые 100;
  • если полное сходство превышает 0.95, остальные алгоритмы не запускаются;
  • если полное сходство ниже 0.5, дополнительные алгоритмы тоже не запускаются.

Двухфазный поиск

При обработке строки реестра поиск идёт в две фазы:

  1. Фаза точного поиска. Для полей номеров и дат значение ищется через указатель. Если найдено достаточно совпадений (по умолчанию --- 10 на строку), вторая фаза пропускается.
  2. Фаза нечёткого поиска. Для текстовых полей перебираются документы из кэша и вычисляется степень сходства. Документы, уже найденные в первой фазе, пропускаются.

Результаты обеих фаз объединяются и сортируются по убыванию оценки уверенности. Возвращаются не более limit_per_row лучших совпадений (по умолчанию --- 10).

Асинхронная обработка

Метод search_documents_multi обрабатывает строки реестра группами по 5 штук (микропартиями). После каждой группы выполняется короткая пауза (asyncio.sleep), чтобы не блокировать цикл обработки событий и позволить серверу обрабатывать другие запросы.

Каждые 50 строк в журнал записывается сообщение о прогрессе. Если передана функция обратного вызова progress_callback, она вызывается после каждой микропартии с текущим и общим числом обработанных строк.

Фоновый поиск с отслеживанием состояния

Функция run_background_search запускает поиск как фоновую задачу и отслеживает её состояние в словаре _search_tasks. Состояние содержит статус (running, completed, failed), процент выполнения и время начала или завершения. Клиент может периодически запрашивать состояние через get_search_status.

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

Порог совпадения

Класс DocumentMatcher принимает параметр threshold (по умолчанию --- 80). Это минимальная оценка уверенности в процентах, ниже которой совпадение не считается найденным. Пользователь может менять порог при настройке реестра.

Порог Когда полезен
90--100 Когда важна точность: номера, суммы, даты
70--90 Для текстовых полей с возможными опечатками
50--70 Для поиска по неполным или сокращённым названиям