Постановка задачи

В рамках работ по "автоматизации процессов комплаенс-контроля" есть тема по поиску и фиксации совпадений по разным признакам между данными клиентов и данными "субъектов списков Росфинмониторинга" (разного рода террористы-экстремисты).

В данном случае - совпадения по адресам. Но не просто "адрес клиента равен адресу субъекта" - это было бы слишком просто, а "все уникальные элементы нормализованного адреса субъекта входят в адрес клиента" (но не наоборот). Порядок следования и наличие повторений элемента в адресе не играют роли.

Нормализация адреса - приведение его к верхнему регистру, удаление лишних пробелов и удаление всяких "город", "г.", "улица", "ул." и т.п. Т.е. "Российская Федерация, г.Мухосранск, ул.Коммунистический тупик, д.13, кв.666" нормализуется в "МУХОСРАНСК КОММУНИСТИЧЕСКИЙ ТУПИК 13 666". "Элементом" адреса является отдельное слово (разделенное пробелами).

Дабы облегчить себе жизнь, есть т.н. "витрины адресов" - три таблицы. В первой содержится идентификатор клиента/субъекта + идентификатор адреса (для субъектов есть еще т.н. "ключевое слово" - элемент адреса субъекта, который реже всего встречается в витрине адресов клиентов). Во второй - набор связей адрес-элемент - идентификатор адреса + идентификатор элемента + номер элемента в строке адреса. И в третьей - список элементов - элемент + идентификатор элемента. Т.о. имеем две "витрины" - адреса клиентов и адреса субъектов.

На промсреде витрина адресов клиентов содержит порядка 96млн адресов. Витрина адресов субъектов - порядка 8тыс адресов. Сравнить надо всех со всеми - 768млрд комбинаций где-то...

Запуск всего этого подразумевается в трех режимах

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

  • Поиск по дельте "от клиентов" - запускается каждый день с отбором для проверки только тех клиентов, у которых за прошедшие сутки были изменения в адресах.

  • Поиск по дельте "от субъектов" - запускается после загрузки очередной версии списка росфина

Соответственно, при обработке у нас есть варианты

  • Появилось новое совпадение - нужно добавить в таблицу

  • Совпадение уже было, но в нем что-то изменилось (там есть ряд дополнительных признаков - тип адреса и т.п.) - обновляем запись

  • Совпадение было и ничего не поменялось - ничего не делаем

  • Совпадение было в таблице, но сейчас его нет - удаляем запись из таблицы

По времени. Заказчик хочет чтобы все это занимало не более 3-4 часов. Т.е. если процесс запущен в 4-5 утра, то к началу рабочего дня (9 утра) все должно уже завершиться.


Реализация

Параллельная обработка

Сразу понятно, что объем данных достаточно велик чтобы обрабатывать все это в один поток. Т.е. надо распараллеливать обработку. Что в наших условиях является стандартным подходом, для которого есть готовые решения, оформленные в виде шаблонов ("скелетонов"). Реализуется все это примерно так:

  • Есть "головное задание" и "задание-обработчик".

  • Задача головного задания - отбор данных, задача обработчика - обработка одного элемента.

  • Головное задание при старте запускает нужное количество фоновых заданий-обработчиков, затем делает выборку данных для обработки, объединяет их в пакеты (по 10-100 элементов) и выкладывает на конвейер. В качестве конвейера можно использовать, например, pipe или очередь (про доступные нам системные очереди писал тут). Когда данные закончились, на конвейер выкладываются пустые пакеты (по количество обработчиков), означающие что данных больше нет и можно завершать работу.

  • Обработчик "подхватывает" с конвейера очередной пакет, обрабатывает все содержащиеся в нем элементы и идет за следующим. И так, пока не придет пустой пакет. Тогда обработчик завершает работу.

Таким образом имеем два комплекса - один работает "от клиентов" - головное задание делает отбор адресов клиентов (все или изменившиеся за последние стуки), обработчик для каждого адреса клиента ищет совпадения с адресами субъектов. Второй работает "от субъектов" - головное задание отбирает адреса субъектов (аналогично - все или загруженные в рамках последнего списка), обработчик ищет совпадения адреса субъекта с адресами клиентов.

Над всем этим небольшая запускалка с указанием режима работы - все, по текущей дате, по последнему списку, которая уже выбирает какой именно комплекс надо запустить (забегая вперед - для режима "все" запускается комплекс "от субъектов" т.к. он работает существенно быстрее).

Общий алгоритм

Головные задания вопросов не вызывают - отбор данных там достаточно простой, кроме того, отбор данных для обработки всегда на порядки быстрее самой их обработки.

Все вопросы возникают по реализации обработчика. Общий алгоритм таков: при получении очередного клиента/субъекта проходим по таблице совпадений и формируем список уже имеющихся совпадений для данного клиента/субъекта. Это список "того, что есть сейчас". Дальше нужно найти совпадения и получить список "того, что должно быть". Имея эти два списка, идем по списку того что есть и для каждой записи ищем соответствие в списке того, что должно быть. Не нашли - удаляем запись из таблицы. Нашли - проверяем требуется ли изменение (если да, то изменяем), после чего удаляем найденную запись из списка того, что должно быть.

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

А теперь самое интересное...

Поиск совпадений и формирование списка того, что должно быть

Первая попытка решения была "пристрелочной" - просто перебором и проверкой каждого адреса на совпадение. Чтобы просто понимать с чем имеем дело и какой порядок времени все это занимает. Изначально вообще была всего одна витрина - только по адресам клиентов. Адреса субъектов нормализовались и разбивались на элементы в рантайме.

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

Такие запросы были реализованы как для работы "от клиента", так и для работы "от субъекта". Выглядели страшненько, но работали. Для случая "по дельтам" укладывались в заданное временное окно с хорошим запасом, но была проблема с полной проверкой - "от клиентов" она работала около 2-х суток, "от субъекта" - порядка 10 часов. Ни то, ни другое не устраивало.

На этом этапе уже стало понятно, что работа по дельтам в целом укладывается в заданные условия, но с работой по полной версии нужно что-то решать. И что наиболее перспективной здесь выглядит работа "от субъекта" - во-первых, там меньше данных, во-вторых, алгоритм сравнения ассиметричен -

"все уникальные элементы нормализованного адреса субъекта входят в адрес клиента" (но не наоборот)

И тут вступают в силу особенности языка RPG (о возможностях которого писал тут). В частности - возможность работать с БД средствами прямого доступа (позиционирование по индексу и т.п.). В результате родился такой вот "play-off" алгоритм для каждого адреса субъекта:

  • Определяем идентификатор элемента витрины адресов клиентов, соответствующего ключевому слову адреса субъекта (1)

  • Заполняем массив уникальных идентификаторов из витрины адресов клиентов для остальных элементов адреса субъекта (2)

  • Заполняем массив идентификаторов адресов клиентов, содержащих найденный идентификатор элемента (список тех, кто потенциально может дать совпадение) (2)

  • В цикле для всех остальных (кроме ключевого слова) уникальных идентификаторов элементов адреса субъекта в витрине адресов клиентов проходим по имеющемуся массиву идентификаторов адресов клиентов и проверяем наличие в индексе пары идентификатор адреса клиента + идентификатор элемента адреса субъекта в витрине адресов клиентов (3). Если пара отсутствует в индексе, идентификатор адреса клиента удаляется из массива.

  • На выходе из цикла имеем массив идентификаторов адресов клиентов, которые содержат все уникальные элементы адреса субъекта - остальные из него удалены в процессе просеивания. На основе этого массива уже строим список того, что должно быть для последующего сравнения со списком того, что есть в БД.

Пояснения:
(1) - чтение записи по уникальному ключу
(2) - позиционирование на первую запись с заданным значением ключа и дальше в цикле чтение записей до тех пор, пока значение ключа не поменяется.
(3) - проверка наличия в индексе записи для заданного значения ключа без чтения самой записи из таблицы.
Все это выполняется при помощи команд прямого доступа к БД, предоставляемых RPG.

В реальных условиях первичное заполнение массива может дать достаточно большое количество элементов (на тестах на копии промсреды получали 500-600 тысяч) - селективность по элементам адреса не очень хорошая - ладно если там какое-нибудь село Синьял-Котяки (реально есть такое в Удмуртии), а если это, скажем, Екатеринбург, проспект Космонавтов? Сколько клиентов в полуторамиллионном Екатеринбурге? Сколько клиентов живет по всей стране на Космонавтов? Спасает то, что дальше, на каждом "обороте" цикла размер этого списка будет только сокращаться за счет исключения из него элементов.

Очень хорошо то, что самая массовая (по количеству обращений к БД) операция - это (3), которая не требует затрат времени на чтение записи из таблицы, только проверка ее наличия в индексе.

В результате полная проверка "от субъектов" на копии промсреды занимает 15 (пятнадцать!) минут (вместо 10 часов в варианте с SQL запросом). Что можно считать просто замечательным результатом.

К сожалению, такой play-off алгоритм может быть реализован только для поиска совпадений "от субъекта к клиенту". В обратную сторону все будет сложнее - массив не получится сокращать, он будет расти и придется для каждого совпадения считать количество совпавших элементов и сравнивать с количеством уникальных элементов адреса субъекта для которого найдено совпадение.

В итоге решено оставить проверку "от клиента" как есть, на SQL, и использовать ее только для работе "по дельте" - проверять только тех клиентов, у которых за сутки было изменение адреса. В этом режиме данных для проверки значительно меньше и время работы ее невелико (в пределах часа в среднем). А полную проверку (всех со всеми) и проверку по последнему загруженному списку запускать "от субъектов" (что и прописано в программе-запускалке).

Вот такие вот попадаются задачки...

Комментарии (29)


  1. Akina
    30.05.2024 05:52
    +3

    Т.е. "Российская Федерация, г.Мухосранск, ул.Коммунистический тупик, д.13, кв.666" нормализуется в "МУХОСРАНСК КОММУНИСТИЧЕСКИЙ ТУПИК 13 666". "Элементом" адреса является отдельное слово (разделенное пробелами).

    Гм... ну, может, оно и пригодно для Мухосранска. Но в большом городе одноимённые площадь и улица, наличие/отсутствие разного количества уровней в нумерации домов/квартир (где-то есть корпус, где-то строение, где-то нет квартиры и пр.) и прочие артефакты адреса явно требуют введения однозначной идентификации типа или субтипа токена. Как решалась эта задача?

    Практический пример:

    г. Москва, г. Зеленоград, ул. Юности, д. 3, стр. 1

    г. Москва, г. Зеленоград, пл. Юности, д. 3, кв. 1

    Оба адреса после вашей формализации дадут абсолютно идентичное

    МОСКВА ЗЕЛЕНОГРАД ЮНОСТИ 3 1


    1. SpiderEkb Автор
      30.05.2024 05:52
      +2

      К сожалению, никак :-(

      Строго говоря, для клиентов у нас несколько типов адресов (мы проводим сравнение по 5-ти типам) - регистрации, почтовый, фактический, юридический и т.п. Часть типов "структурированные" - там есть разделение достаточно жесткое - страна, регион (+тип региона), населенный пункт (+тип населенного пункта) и т.п. Часть - не структурированная - просто строка (как ее ввели, возможно с ошибкой).

      Для субъектов же все хуже - там адреса неструктурированные. Просто строки так, как их присылает росфин. И тут мы ничего не можем поделать. Там еще хуже - адрес может быть не полным (без дома, без квартиры - город и улица, все...). И порядок следования элементов в адресе тоже не детерминирован - как написали, так написали. Увы, но такие вот реалии...

      Разбирать каждый адрес руками (структурировать) физически невозможно.

      В любом случае - совпадение это не приговор, а маркер для более пристального анализа. Кроме совпадений по адресам есть еще ряд признаков (там отдельные списки совпадений) - ФИО + ДР (для ФЛ) или наименование (для ЮЛ), ДУЛ (документ, удостоверяющий личность для ФЛ), ИНН... И это тоже маркеры.

      Все это делается скорее для того, чтобы сразу пропускать операции, где совпадений нет. А те, что дали совпадение отправляются на ручной разбор (безусловной автоматической блокировки нет - все, что вызвало подозрения, отправляется в службу финмона для ручного анализа). Основная цель - не завалить их тем, что на 146% валидно, оставили лишь то, что потенциально может быть опасным с точки зрения законодательства.


    1. inkelyad
      30.05.2024 05:52

      Самое непонятное - зачем неструктурированный адрес делать еще более неструктурированным, превращая одно имя "Коммунистический тупик" в два равнозначных слова. Можно же было использовать какой-нибудь более внятный разделитель из тех, что были придуманы в незапамятные времена. Ну или хотя бы табуляцией.


      1. SpiderEkb Автор
        30.05.2024 05:52

        С одной стороны у нас всегда только неструктурированные адреса. Причем, еще и неполные часто. Так что для сравнения все должно быть приведено к одному виду.

        И структурированный адрес это вообще другое. Это когда каждый элемент суть структура вида

        Тип элемента (например - регион)
        Вид типа элемента (например, типа региона - область автономная область, республика, город федерального значения и т.п.)
        Название элемента (Воронежская, Башкортостан, Москва и т.п.)

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

        А просто в строке когда может быть в одном местное Коммунистический тупик, в другом - тупик Коммунистический еще придется тратить время на то, чтобы угадать - тупик, оно вообще к чему относится? К тому что после него или к тому что перед ним?

        В целом, все варианты обсуждались с заказчиком (финмон) - их предупреждали что с такими данными мы не можем гарантировать 100% точность совпадений. Им главное чтобы отсеялось то, что точно (на 146%) не даст совпадения, но не прошло то, что может потенциально дать совпадение. Дальше они уже руками анализируют. Полностью это не автоматизируется в силу неполноты полученных от росфина данных.


        1. inkelyad
          30.05.2024 05:52
          +1

          А просто в строке когда может быть в одном местное Коммунистический тупик, в другом - тупик Коммунистический еще придется тратить время на то, чтобы угадать - тупик, оно вообще к чему относится? К тому что после него или к тому что перед ним?

          Так разбираться все равно надо? Иначе 'Российская Федерация, г.Мухосранск, ул.Коммунистический тупик, д.13, кв.666' и 'Российская Федерация, г.Мухосранск, тупик Коммунистический , д.13, кв.666' дадут два разных представления:

          'МУХОСРАНСК КОММУНИСТИЧЕСКИЙ ТУПИК 13 666' и 'МУХОСРАНСК ТУПИК КОММУНИСТИЧЕСКИЙ 13 666', а не одно - первое. Насколько именно 'каноническое представление' одного и того же адреса однозначно?

          А если не разбираться - то мне все равно кажется, что 'МУХОСРАНСК<US>КОММУНИСТИЧЕСКИЙ ТУПИК<US>13<US>666' и 'МУХОСРАНСК<US>ТУПИК КОММУНИСТИЧЕСКИЙ<US>13<US>666' удобнее для дальнейшего сравнения, чем просто все через пробел.


          1. SpiderEkb Автор
            30.05.2024 05:52
            +1

            Удобнее. Но кто будет этим заниматься? В том списке, что приходит из росфина, все одной строкой через пробелы. Дальше должен сидеть человек, который все это будет приводить к нормализованному виду? Или как? Даже есть что-то там автоматизировать, то все равно человек должен сидеть и проверять как оно там разложилось - правильно ли вставились все разделители.

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

            Есть адреса где нет улиц (реально так) - "4-й микрорайон, 36/1, Тобольск, Тюменская область" это вполне реальный адрес. И рядом же "улица Знаменского, 56, Тобольск, Тюменская область". Причем, оба могут быть записаны и как "Тюменская обл., г.Тобольск, мкр.4, д.36, корп.1" и "Тюменская обл., г.Тобольск, ул. Знаменского, д.56". Причем, один раз так, один раз этак и один раз еще как-то...

            И мы ну никак не можем сказать Росфину - "эй, пацаны, ну достали уже реально, присылайте нам адреса в нормализованном виде".

            Нормализовать полученный откуда-то адрес просто по каким-то ключевым словам или позиционно так просто не получится, увы. Тут или человек, или ИИ (и человек за ним присматривающий) нужен.

            И, повторюсь, тут ложноположительный результат допустим (он увеличит нагрузку на людей из финмона, но не приведет к нарушению законодательства), а вот ложноотрицательный - прямой путь к нарушению 115ФЗ... От этого и пляшем.

            В целом - ну все понимают проблему. Но решить ее сходу нет ни времени ни ресурсов (и если бы это была единственная проблема - это одна из многих проектных задач у нас). Так что решаем поэтапно. В старой версии было все еще хуже и работало намного дольше. Пока только решили проблему со скоростью. Дальше есть надежда что все это от нас заберут (и это будет более правильно т.к. разгрузит сервер от того, чем его можно не нагружать) как забрали совпадения по именам-ДУЛам-ИНН - там нам прилетают уже готовые совпадения, мы только актуализируем таблицу.


            1. beho1der
              30.05.2024 05:52

              Достаточно чтобы они добавили класификаторы адресов ГАР\ФИАС и 80% адресов не надо будет нормализовывать и разбирать руками


              1. SpiderEkb Автор
                30.05.2024 05:52

                Для этого, повторюсь, потребуется реплицировать (и потом регулярно обновлять) к себе БД ГАР\ФИАС. Потому что доступов с серверов наружу нет.

                И опять - все это сработает только с той частью адресов, которые переданы корректно и в полном объеме. Но они и так хорошо обрабатываются. А вот ка связать с той БД проблемные адреса типа

                КИРГИЗИЯ, ОШСКАЯ ОБЛ., , КЫЗЫЛ-КИЯ Г, , , , ,

                или

                С. АРШТЫ СУНЖЕНСКОГО РАЙОНА ЧИАССР

                которые являются проблемными?


                1. beho1der
                  30.05.2024 05:52
                  +1

                  есть уже готовые реализации парсеров, файлы инкрементальных обновлений можно ложить руками. Как раз одним из таких занимаюсь(+ связка с координатами OSM) пока не выкладывал его в открытый доступ,но нормализация адреса там тоже есть.
                  С. АРШТЫ СУНЖЕНСКОГО РАЙОНА ЧИАССР
                  вот пример что выдает мой поиск

                  не знаю насколько корректно,но вроде то что нужно!
                  Если вам было бы полезно мог бы отдать с исходным кодом,пока не все готово для публикации в Open Source, едиственное поиск по адресу заточен на то что нужно знать регион\область


                  1. SpiderEkb Автор
                    30.05.2024 05:52

                    файлы инкрементальных обновлений можно ложить руками.

                    Нет, нельзя. Доступ к боевому серверу банка у очень ограниченного количества людей. И никто из них руками туда ничего класть не будет. Это нарушение регламента ИБ.

                    не знаю насколько корректно, но вроде то что нужно!

                    Абсолютно не то. Есть неполный адрес. Мы не имеем права додумывать "а что это может быть". Мы можем только показать все совпадения с этим неполным адресом и ничего более. Да, их будет много. Но дальше уже только ручной разбор. В т.ч. есть ли совпадения по другим маркерам (ФИО + ДР, ДУЛ, ИНН...).

                    Вы же не можете сказать - где именно живет террорист, у которого стоит адрес 'С. АРШТЫ СУНЖЕНСКОГО РАЙОНА ЧИАССР' - на улице Ушата Помоева д.5, или на улице Рулона Обоева д.7? Вот и не додумывайте - не угадаете - получите ложноотрицательный результат и попадете под 115ФЗ...

                    Тут нет задачи восстановить полный адрес по неполному. Задача исключительно в том, чтобы найти все совпадения такие, что "все уникальные элементы строки из таблицы А входят в строку из таблицы Б, но не наоборот". Дублирование элементов не имеет значения. Порядок следования элементов не имеет значения.

                    Интерпретацией совпадений занимается служба финмониторинга. Наша задача отсеять для них то, что точно не совпадает и дать идентификаторы (ссылки) кто с кем совпал. Они потом могут просмотреть результаты и проставить свои статусы - "совпадение подтверждено" или "совпадение не подтверждено". Но это уже их внутренние регламенты. Мы только автоматизируем подготовку данных для них.

                    Точно также работает комплекс комплаенс-проверок в системе расчетов - платежный документ прогоняется через ряд проверок - если все сошлось, присваивается статус "пропустить" и платеж уходит дальше в обработку. Если что-то не срослось - проверка возвращает соответствующий код (что и где не понравилось), платежу присваивается статус "на ручной контроль" и дальше он с этим кодом идет на анализ в финмон. Конечное решение всегда принимает специально обученный человек, наделенный полномочиями и несущий за это решение персональную ответственность.

                    Есть регламенты (построенные на основе законодательства и правил, устанавливаемых регулятором), мы обязаны им следовать. Нарушение регламентов влечет за собой штрафные санкции (в т.ч. и штрафы с очень многими нулями) со стороны регулятора. Систематическое нарушение может привести к отзыву лицензий.

                    Думаете просто так многие категорически не хотят в банках работать, тем более, на уровне центральных серверов?


    1. asdfddsa
      30.05.2024 05:52
      +1

      Практический пример:

      У нас в стране почти вся автоматизация так и работает. Это только в интернете все математические гении, сеньоры-помидоры и тимлид-архитекты, а куда не придешь, там говнокодинг и такие бизнес-процессы, что кровь из глаз.

      В куче контор, включая топ-банки, постоянно сталкиваюсь с проблемой двойного адреса. Ладно, если напишут сокращенный, а то бывает реальная дичь, что улица от одного дома, а дом от другой улицы. Не представляю, как живут люди с более сложными написаниями адресов, и во всяких объектах, которых даже в ФИАС нет. Пожалуй самый распространенный кейс - окошечко "улица" и отдельно окошечко "дом", в юридически значимых данных, где нужен полный адрес. Такую чушь даже в печатных бланках дофига где увидишь, включая штампы паспортного стола!


      1. SpiderEkb Автор
        30.05.2024 05:52

        Да, увы, все так.

        В целом, таблица совпадений потом выверяется руками (это уже бизнес делает) и каждому ставится статус "подтверждено" или "не подтверждено". Ну не получается сделать что-то большее.

        Это на клиентах оператор может вводить структурированный адрес по полям - регион, тип региона (из списка), название региона, населенный пункт - тип населенного пункта (из списка типов), название населенного пункта, улица - тип улицы (из списка), название улицы... И при этом пытать клиента если чего непонятно, то с субъектами - что прилетело, то прилетело... Не уточнишь. Да и сами он могут не знать полного адреса - до дома проследили терроритста, а в какую квартиру зашел наружка не установила (условно, конечно). Вот и напишут город-улицу-дом, но без квартиры.

        Не говоря уже о том, что на одного персонажа списка может быть три паспорта на пять имен, четыре адреса и два разных ИНН...


  1. CitizenOfDreams
    30.05.2024 05:52

    Или как адреса "производителей" на упаковках некоторых товаров: "ООО Вектор Плюс, г. Мухосранск, улица Фейковая, дом 1, корпус 2, строение 3, крыло 4, этаж 5, кабинет 6Ж". Если в этом "строении" заведется хоть один "экстремист", доказывать неверблюдство придется всем его соседям.


    1. SpiderEkb Автор
      30.05.2024 05:52
      +2

      Увы, да. Соседям (см. выше) ничего доказывать не придется, но ребятам из финмона работы добавится - анализировать всех руками. Ну и платеж (например) может тормознуться - не за минуту пролетит, а придет через несколько часов или на следующий день.

      Вообще, комплаенс тема очень мутная сама по себе. Автоматизировать его полностью нереально. "Субъекты" в значительной степени - те, кто посильно старается затруднить свою идентификацию. Там несколько имен, паспортов, адресов на каждого...

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


  1. rukhi7
    30.05.2024 05:52
    +2

    Сравнить надо всех со всеми - 768млрд комбинаций где-то...

    По моему это пример задачи от "эффективного" менеджера который прикрывается тем что это задача от заказчика. Хотя, вроде как, заказчика должен интересовать только результат, то есть что для разных типов порядка (например) входных данных система выдает правильный результат поиска. Заказчика не должен интересовать алгоритм поиска, если конечно он не хочет присвоить этот алгоритм.
    Сложность сравнения всех со всеми в лоб слишком большая? Да ладно компьютер железный ему пофигу!

    удаление всяких "город", "г.", "улица", "ул." и т.п.

    всякие "город", "г.", "улица", "ул." и т.п. и даже анализ заглавные-прописные могут значительно облегчить-ускорить поиск, разве нет?
    "Эффективный" менеджер не рассматривает умные решения! "Эффективный" менеджер точно знает что "Эффективнее" всего решать задачу в лоб.


    1. SpiderEkb Автор
      30.05.2024 05:52
      +1

      Вы правы. Заказчика интересует результат. Полученный в строго определенное время. Т.е. на "к завтрашнему дню", а сейчас. Кроме того, есть сопровождение. Которое следит чтобы процесс получения результата не дал повышенную нагрузку на сервер - это все не на выделенной машине работает тут еще 100500 других, не менее важных процессов крутится. Т.е. тут есть сочетание "результат + временное окно + нагрузка в рамках временного окна". К сочетанию этих требований приходится привыкать. Но соблюдение их обязательно.

      И да. Нам таки удалось вынести поиск совпадение по именам/наименованиям, ДУЛам, ИНН с центральных серверов на "внешнюю систему" (это ведет к снижению нагрузки на основной сервер) - мы оттуда получаем уже готовые совпадения которые просто раскладываем по своей базе. И это уже не наша головная боль. Надеемся со временем и адреса вынести. Но пока вот так - 115ФЗ на паузу никто не поставит просто потому что мы чего-то там не успели еще. А за несоблюдение его получить по голове можно очень жестко, вплоть до лишения лицензии.

      всякие "город", "г.", "улица", "ул." и т.п. и даже анализ заглавные-прописные могут значительно облегчить-ускорить поиск, разве нет?

      Ускорить точно нет если вы работает с неструктурированным адресом. Скорее замедлит и усложнит - придется анализировать не просто элемент, а сочетание элементов - "Космонавтов" - это что? улица? проспект? переулок? Или вообще поселок?

      Если бы мы работали только со структурированными адресами, все было бы проще - там элемент характеризуется набором двух свойств - тип элемента + название элемента. И сравнение становится проще. Но такого у нас, увы, нет (структурированные адреса есть только для клиентов и то не все типы адресов являются структурированными). Для субъектов адрес не структурирован. И там может быть как "улица Космонавтов", так и "Космонавтов улица". Про регистр вообще молчу - там как бог на душу положит может быть.

      Увы, но задача в реальной жизни не имеет точного и эффективного решения. Повторюсь - задача скорее в том, чтобы уменьшить объемы ручного разбора, отсеяв сразу тех, кто заведомо не совпадает.


  1. joger
    30.05.2024 05:52
    +1

    Занимаюсь практически тем же, но в Германии и немного по другому. Сама нормализация адресов у меня иначе. Я скачал с немецкой почты "каноничные" комбинации город, индекс, улица. Дальше уже сложнее, но и кол-во кандидатов меньше. Все эти комбинации закинул в эластик и индексировал n-gram 3-x. Каждый новый адрес идёт запросом в эластик, получаем несколько кандидатов и пытаемся в несколько этапов понять, что есть что. На выходе полностью стандартизированный адрес, который мы и сохраняем в ДБ. Искать по таким адресам вообще не проблема


    1. SpiderEkb Автор
      30.05.2024 05:52
      +1

      Тут проблема в том, что сои адреса (клиентов) мы вводим сами (ну не мы, конечно, но операторы при регистрации клиента). И там его сразу можно вводить структурированным - тип элемента + название элемента.

      Вся проблема в том, что от росфина мы получаем то, что получаем. И никак на это повлиять не можем. И там возможны неполные адреса (у них просто нет полного - это же злодеи всякие, они же прячутся, пароли-явки меняют...). Именно отсюда и идет требование "все уникальные элементы адреса субъекта входят в адрес клиента" - от того, что адрес субъекта не является 100% точным и подтвержденным (в отличии от адреса клиента) - это "вроде бы где-то примерно там, наверное..."

      И да, эластика на нашей платформе нет. Вот спихнем это на внешние системы - пусть там сами со всем этим разбираются как хотят.

      Тут даже не про то как я адресами работаем. Тут про то, что разные подходы к решению одной задачи могут дать на порядки разные результаты по скорости и эффективности. Ну и немножко про то, что SQL далеко не всегда является наиболее эффективным способом работы с БД.


  1. alexhott
    30.05.2024 05:52
    +1

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

    На качестве результате это бы никак не сказалось, но работало бы быстрее и дало бы дополнительные данные из справочников.

    А бонусом если ошибки парсинга еще кто-нибудь бы обрабатывал регулярно то и качество бы повысилось


    1. SpiderEkb Автор
      30.05.2024 05:52

      Увы, но ресурсов на это пока нет. Да и адреса могут быть не только российскими. И не факт что адрес будет полным. И что там не будет что-то типа "Томск, Космонавтов 3-5" - попробуй угадай что там где. Космонавтов - это что? Проспект, улица, переулок или вообще поселок (тут вариантов море, вплоть до "город Томск. поселок Космонавтов, 3-й микрорайон, дом 5"...)

      Ну и поэлементное сравнение адреса будет точно дольше. Здесь соль алгоритма в предварительной выборке всех, кто содержит "ключевое слово" (которое выбирается как минимально часто встречающееся) и затем удаление тех, кто не содержит хоть одно слово из остальных слов адреса.

      Если структурировать, то витрину придется строить для каждого типа элемента отдельно. В качестве ключевого выбирать тот элемент, который реже всего встречается в соотв. витрине.

      В целом реализуемо, конечно. Быстрее - вряд ли. Но проблема со структурированием непойми какой строки остается. Там придется добавлять ручной контроль как оно структурировалось. И непонятно кто этим заниматься будет и когда - возникнет лаг между загрузкой очередного списка и формированием нового списка совпадений. Не уверен что это допустимо.

      Там же наравне с достатчоно внятными адресами

      РОССИЙСКАЯ ФЕДЕРАЦИЯ, ТЮМЕНСКАЯ ОБЛАСТЬ, ХАНТЫ-МАНСИЙСКИЙ АВТОНОМНЫЙ ОКРУГ-ЮГРА, Г. СУРГУТ, УЛ. ТЮМЕНСКИЙ ТРАКТ, Д. 4, КВ. 301

      Могут попадаться и неполные

      Г. ЛЕСОЗАВОДСК ПРИМОРСКОГО КРАЯ
      С. АРШТЫ СУНЖЕНСКОГО РАЙОНА ЧИАССР

      Или вообще

      ,КИРГИЗИЯ,,,,,,,,
      КИРГИЗИЯ, ОШСКАЯ ОБЛ., , КЫЗЫЛ-КИЯ Г, , , , ,

      и заграничные

      РЕСПУБЛИКА СИНГАПУР, 80 RAFFLES PLACE, #17-22 UOB PLAZA, SINGAPORE, 048624, SINGAPORE,
      50, PURDUE STREET, CUBAO, QUEZON CITY, PHILIPPINES

      Это все из реальных данных... Что реально от росфина приходит


      1. Drucocu
        30.05.2024 05:52

        Увы, но ресурсов на это пока нет.

        Как же больно это читать от представителя одного из крупнейших банков. У кого тогда ресурсы-то, ёлы-палы?

        а еще лучше сразу с ГАР сопоставлять

        Что есть ГАР не знаю, знаю КЛАДР. По хорошему, задача должна была бы сводиться к поиску в нём, но этот факт всё портит:

        Да и адреса могут быть не только российскими.

        Хотя, опять же, к Российским адресам можно применять поиск по КЛАДРу, а к иностранным - прочие эвристики. Российских же наверняка больший процент.


        1. Akina
          30.05.2024 05:52

          Что есть ГАР не знаю, знаю КЛАДР.

          ГАР == Государственный Адресный Реестр. https://fias.nalog.ru/FiasInfo

          КЛАДР, в отличие от ГАР - это ведомственная БД, а не общегосударственная.


          1. Drucocu
            30.05.2024 05:52

            Кажется, это таки одно и то же с разной обёрткой, ибо на сайте КЛАДР пишут:

            мы получаем актуальные данные Государственного реестра адресов ФНС России.

            Что, в общем-то, логично. Ещё бы тут были какие-то разные базы.


        1. SpiderEkb Автор
          30.05.2024 05:52
          +1

          Как же больно это читать от представителя одного из крупнейших банков. У кого тогда ресурсы-то, ёлы-палы?

          Ни у кого :-)

          Я ж не говорю что совсем нет. Есть, но... Хотелось бы больше :-)

          Вот конкретно у нас в функциональном подчинении управления автоматизации процессов комплаенс-контроля разработчиков полторы штуки. Полторы - потому что один совмещает еще функции архитектора направления (то, от чего я отказался, честно попробовав полгода этим позаниматься - не мое). А там более 60% времени уходит на согласования и обсуждения (причем, часто час обсуждаем, потом расходимся со словами "давайте еще подумаем и через неделю соберемся снова").

          Да, есть еще вендорры-аутстаферы, но там свои проблемы с ними.

          И да, с аналитиками в целом все неплохо - их хватает (на нашем направлении их сейчас четверо плюс "консультант направления", но он ближе к бизнесу - с ним все непонятные тонкости хорошо решать)

          Вот тут на последнем CoreSystems DevConf 2024 меня прорвало когда один из докладчиков выдвинул тезис о том, что "нет времени на исправление дефектов - это миф"

          Что есть ГАР не знаю, знаю КЛАДР. По хорошему, задача должна была бы сводиться к поиску в нём

          На самом деле тут есть проблемы. Мы не можем напрямую обращаться куда-то наружу - сервера изолированы от внешнего мира. Доступ к ним возможен только через веб-сервисы (UWS-шина) или очереди (MQ, недавно вот еще Kafka прикрутили).

          Т.е. нам придется реплицировать эти базы каким-то образом к себе (ты, например, реплицируем БД недействительных паспортов МВД для своих проверок). И не факт что поиск по ним будет быстрым. А время на таких объемах очень критично.

          Более того, полные адреса (которые можно было бы с чем-то увязать) и так неплохо "бьются" за счет большого количества элементов (и "оборотов" play-off цикла). Большое количество ложно-положительных результатов дают неполные адреса (которых там изрядное количество). А их, увы, не нормализовать и ни к чему не привязать.

          Но заказчик сказал четко - "лучше 1000 ложноположительных результатов чем один ложноотрицательный". Потому что попадать под 115ФЗ ну очень не хочется.


          1. Drucocu
            30.05.2024 05:52

            На самом деле тут есть проблемы. Мы не можем напрямую обращаться куда-то наружу - сервера изолированы от внешнего мира.

            Да, я догадался по критичности данных)

            Не понятно, будут ли этот КЛАДР предоставлять для локального использования и сколько это будет стоить.


  1. Drucocu
    30.05.2024 05:52

    Я, конечно, иногда ловлю себя на том, что набигаю в ваши комментарии и пишу там много нехорошего, но статьи у вас интересные.

    Но, опять же, мы существуем в очень разных IT-мирах. У нас бы эта задача решалась загрузкой этих самых "субъектов списков Росфинмониторинга" в какой-нибудь Elasticsearch, и дальше бы мы занимались настройкой параметров, пока оно не начало бы выдавать приемлемый результат. Данные, естесственно, всё равно пришлось бы нормализовать что там, что там, но вся эврестика легла бы на плечи поискового движка.


    1. SpiderEkb Автор
      30.05.2024 05:52

      Я, конечно, иногда ловлю себя на том, что набигаю в ваши комментарии и пишу там много нехорошего, но статьи у вас интересные.

      Спасибо :-) Я давно понял что кто-то тут за мной следит :-)

      Но, опять же, мы существуем в очень разных IT-мирах.

      Да, это так. Я сам когда в этот мир попал, не сразу "встроился". Вообще, если интересно "чем дышим"

      Записи трансляций последнего DevConf (22-24 мая с.г.). К сожалению, без разбивки по докладам.

      Но это так, лирика.

      У нас бы эта задача решалась загрузкой этих самых "субъектов списков Росфинмониторинга" в какой-нибудь Elasticsearch, и дальше бы мы занимались настройкой параметров, пока оно не начало бы выдавать приемлемый результат. Данные, естесственно, всё равно пришлось бы нормализовать что там, что там, но вся эврестика легла бы на плечи поискового движка.

      Увы, но эластика у нас нет. А БД субъектов росфина нам нужна у себя и не только для задачи формирования списков совпадений (фактически, эти списки являются своего рода "витриной").

      И, на самом деле, мы уже в процессе вытаскивания поиска совпадений из ядра (с центрального сервера) на "внешнюю систему". Т.е. это временное решение. Целевое - вообще не грузить этим центральные сервера, а получать и загружать к себе уже готовые совпадения.

      Ну критичный для нас момент - будет эластики работать быстрее описанного алгоритма? Будет он экономичнее по ресурсам? Для нас это краеугольный камень. Куда важнее чем затраты и удобство разработки.

      Описанный алгоритм очень быстрый и очень "легкий". Быстрее и легче SQL. Там, где используется чтение нескольких записей по ключу ((2) в описанном алгоритме), реализован режим блочного чтения - соотношение по времени и ресурсам по сравнению с обычным примерно 1:3 по моим экспериментам с PEX статистиками

      _QRNX_DB_READE - внутренняя функция при чтении по одной записи за раз
      _QRNX_DB_READ - внутренняя функция при блочном чтении.

      Это на одном и том же массиве данных.

      Проверка наличия записи в таблице ((3) в алгоритме - самая массовая операция с БД) выполняется просто проверкой наличия соответствующего значения ключа в индексе, без затрат времени на чтение самой записи. Причем, делается это двумя строками кода

                setll (arrEID(1): dsCltAddrID.addrId) NMACP20LF;
      
                if %equal(NMACP20LF);
                  exsr srStoreID;
                endif;

      setll - позиционирование в индексе NMACP20LF по значению ключа (arrEID(1) + dsCltAddrID.addrId), %equal - проверка что спозиционировались именно на заданное значение ключа. srStoreID - сабрутина обработки ситуации когда очередной элемент входит в заданный адрес.

      И при этом, как уже написал в комментарии выше, для полных адресов оно работает очень хорошо - ложноположительных результатов там практически нет. А с неполными проблемы большого количества ложноположительных будут в любом случае просто в силу неполноты данных.

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


    1. AndrewKainov
      30.05.2024 05:52

      Почитав ваши комментарии (@Drucocu), любой придёт к выводу, что вы везде пишите много нехорошего. Видимо это ваше кредо.


      1. Drucocu
        30.05.2024 05:52

        У меня появился собственный сталкер. Выходит, я хабразнаменитость)