Сопоставление документов с реестром¶
Модуль 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, дополнительные алгоритмы тоже не запускаются.
Двухфазный поиск¶
При обработке строки реестра поиск идёт в две фазы:
- Фаза точного поиска. Для полей номеров и дат значение ищется через указатель. Если найдено достаточно совпадений (по умолчанию --- 10 на строку), вторая фаза пропускается.
- Фаза нечёткого поиска. Для текстовых полей перебираются документы из кэша и вычисляется степень сходства. Документы, уже найденные в первой фазе, пропускаются.
Результаты обеих фаз объединяются и сортируются по убыванию оценки уверенности. Возвращаются не более 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 | Для поиска по неполным или сокращённым названиям |