Часть 1. Трагическая. “За что мне все это?!”
Столкнулись мы как-то с необходимостью ввода корректной информации о местонахождении (прописке, регистрации) пользователей, и с тем, что проблема эта решается не совсем так легко и просто, как бы нам хотелось. Сначала мы попробовали КЛАДР, в его бесплатной ипостаси. Не то чтобы нам прямо-таки решительно все не понравилось, но было как минимум одно весьма раздражающее обстоятельство — некоторые адреса отсутствовали в справочнике. Например, дом 10 есть, а 10к1 — извините, не завезли. Вообще КЛАДР был привлекателен тем, что у него есть простой API и плагины (jQuery в частности), которые можно легко встроить в приложение, но отталкивал наполнением. Мы задумались — если нет такого ресурса, содержащего полную и наиболее актуальную адресную информацию, с API и плагинами, то единственный выход — создать такой ресурс самим.
Часть 2. Искательская. “Где собака зарыта?!”
И прослышали мы про ФИАС. О том как он бесконечно полон и прекрасен. А это выход! На сайте ФИАС есть базы, и все что нужно молодой растущей информационной системе! Правда, размер базы составляет более 4 Gb, ну да ладно, это же вся Россия! Обновляется база регулярно, так что есть где разгуляться. Дело за малым — развернуть базу, прикрутить API и плагины. Надо оговориться, что существует несколько релевантных и важных статей. К примеру, цикл вот этих статей, которые изначально очень помогли.
Часть 3. «Как это работает»
Проблемы начались с того, что база данных ФИАС поставляется в формате .dbf, данные из которого надо было как-то перенести в PostgreSQL, который было решено использовать в качестве БД.
Нужно отметить, что кроме формата .dbf, база представлена в формате xml. Как известно, человеку хочется всего и сразу. Чтобы включил и работает. Но разобраться сразу с тем, что же дает ФИАС довольно проблематично.
Итак, по адресу fias.nalog.ru в разделе обновлений имеется следующее:
- Информация про обновления: по официальным данным — выходят не менее раза в неделю. Специфика следующая. Иногда выходят битые обновления. Их размер примерно 1,2 Mb и при скачивании дается битый архив, поэтому торопиться настраиваться только на последнее обновление — вызывает вопросы.
- Обновления в виде fias_delta_. — в форматах .dbf и .xml.
- Собственно база данных fias_. — в форматах .dbf и .xml.
- База данных в формате КЛАДР. Наименование файлов “Base.*” arj или 7z Отличается тем, что отсутствуют многие поля (касательно налоговых данных и т.д.), поиск осуществляется по КЛАДРовскому id. Изначально присутствует соблазн использовать именно данный формат, так как он более легок для понимания и последующего внедрения. Но при более внимательном изучении портала, обнаружится, что данная версия будет поддерживаться только до конца 2017 года, а затем будет deprecated.
Примечание: для работы с .dbf файлами необходимо установить стороннее расширение для php_dbase.dll.
Так какую же все таки выбрать версию для скачивания .xml или .dbf? Отличия следующие:
- xml — каждый файл в архиве — отдельная таблица. В чем состоит проблема. К примеру, размер файла “HOUSEXX” где хранятся дома, составляет более 20 Gb в разархивированном виде. Как его парсить? С одной стороны, если у вас более 30 Gb RAM, то это не составит проблемы. Также, если вы знаток грамотных библиотек, таких как SAX Parser для Java, которые не загружают файл целиком в память, а парсят частями, то может быть что-то и получится.
- dbf — файлы не только разделяются как таблицы, но и подразделяются по регионам. Окажется более приемлемым вариантом, если вам не нужны адреса необъятной Родины, а только одного региона (как в нашем конкретном случае).
Итак, распаковали файлы. Какая информация нам нужна? Видим следующий набор файлов:
- ADDROBХХ.DBF, где ХХ — номер региона — содержит данные непосредственно о регионе, автономных округах, городах и прочих населенных пунктах, улицах.
- HOUSEХХ.DBF, где ХХ — номер региона — содержит информацию о номерах домов.
- NORDOCХХ.DBF, где ХХ — номер региона — содержит информацию о причинах изменений в различных записях.
- ROOMХХ.DBF, где ХХ — номер региона — содержит информацию о помещениях.
- STEADХХ.DBF, где ХХ — номер региона — содержит информацию о земельных участках.
- SOCRBASE.DBF — содержит информацию о сокращениях.
- STRSTAT.DBF — содержит информацию о типе строения.
Кроме данных таблиц существует еще ряд других — служебных таблиц, которые содержат информацию о сокращениях в других таблицах.
В большинстве случаев достаточно сформировать адрес вплоть до дома. Хотя, если кому надо, то можно углубиться и дальше.
Таким образом, создадим 2 таблицы в БД postgresql.
Таблица с адресами:
CREATE TABLE addrs ( "ACTSTATUS" integer, "AOGUID" character varying(36) COLLATE pg_catalog."default", "AOID" character varying(36) COLLATE pg_catalog."default", "AOLEVEL" integer, "AREACODE" integer, "AUTOCODE" integer, "CENTSTATUS" integer, "CITYCODE" integer, "CODE" character varying(20) COLLATE pg_catalog."default", "CURRSTATUS" integer, "ENDDATE" timestamp, "FORMALNAME" character varying(120) COLLATE pg_catalog."default", "IFNSFL" integer, "IFNSUL" integer, "NEXTID" character varying(36) COLLATE pg_catalog."default", "OFFNAME" character varying(120) COLLATE pg_catalog."default", "OKATO" VARCHAR(11), "OKTMO" VARCHAR(11), "OPERSTATUS" integer, "PARENTGUID" character varying(36) COLLATE pg_catalog."default", "PLACECODE" integer, "PLAINCODE" character varying(20) COLLATE pg_catalog."default", "POSTALCODE" integer, "PREVID" character varying(36) COLLATE pg_catalog."default", "REGIONCODE" integer, "SHORTNAME" character varying(15) COLLATE pg_catalog."default", "STARTDATE" timestamp, "STREETCODE" integer, "TERRIFNSFL" integer, "TERRIFNSUL" integer, "UPDATEDATE" timestamp, "CTARCODE" integer, "EXTRCODE" integer, "SEXTCODE" integer, "LIVESTATUS" integer, "NORMDOC" character varying(36) COLLATE pg_catalog."default", "PLANCODE" integer, "CADNUM" integer, "DIVTYPE" integer ) WITH ( OIDS = FALSE ) TABLESPACE pg_default; ALTER TABLE address OWNER to postgres;
Таблица с номерами домов:
CREATE TABLE hous ( "AOGUID" character varying(36) COLLATE pg_catalog."default", "BUILDNUM" character varying(10) COLLATE pg_catalog."default", "ENDDATE" timestamp, "ESTSTATUS" integer, "HOUSEGUID" character varying(36) COLLATE pg_catalog."default", "HOUSEID" character varying(36) COLLATE pg_catalog."default", "HOUSENUM" character varying(15) COLLATE pg_catalog."default", "STATSTATUS" integer, "IFNSFL" integer, "IFNSUL" integer, "OKATO" VARCHAR(11), "OKTMO" VARCHAR(11), "POSTALCODE" integer, "STARTDATE" timestamp, "STRUCNUM" VARCHAR(15), "STRSTATUS" integer, "TERRIFNSFL" integer, "TERRIFNSUL" integer, "UPDATEDATE" timestamp, "NORMDOC" character varying(36) COLLATE pg_catalog."default", "COUNTER" integer, "CADNUM" VARCHAR(50), "DIVTYPE" integer ) WITH ( OIDS = FALSE ) TABLESPACE pg_default;
Импорт данных осуществляется простым способом. Открываем файлы в Excel и сохраняем их как csv. Дополнительно рекомендуется изменить кодировку, так как в отличии от xml файлов, которые представлены в кодировке utf-8, dbf файлы — в кодировке win-866. Открываем файлы в редакторе (для данной цели подойдет notepad++) и преобразуем в utf-8.
Импорт таблицы с адресами:
COPY addrs FROM 'PathToTheFile\ADDROB01.csv' DELIMITER ';' CSV;
Импорт таблицы с домами:
COPY addrs FROM 'PathToTheFile\HOUSE30.csv' DELIMITER ';' CSV;
Из чего сделана таблица ADDROBXX?
Несмотря на обилие полей, понадобятся только некоторые из них.
- ACTSTATUS — поле содержит значения 0 и 1. При внимательном просмотре обнаружится, что в таблице множество улиц с одним и тем же названием. Дело в том, что налоговая база содержит историю объектов, т.е. изменения, которые происходили с каким-либо элементов адреса. К примеру, улиц Ленина может быть 10 штук. Из них 9 — будут неактуальными. Они могли быть переименованы или изменены другим образом. ACTSTATUS — 0 — неактуальные адреса, а 1 — будет единственным актуальным.
- AOGUID — ID записи. Для разных исторических записей будет один и тот же AOGUID. Различаться они будут по полю AOID.
- AOLEVEL — уровень объекта. Под номером 1 — субъект федерации, 4 — город, 7 — улица и т.д.
- CENTSTATUS — статус центра. Например, города в областном центре.
- FORMALNAME — название объекта.
- PARENTGUID — ID родительского элемента. При поиске города к которому принадлежит улица. PARENTGUID улицы будет совпадать с AOGUID улицы.
- POSTALCODE — почтовый индекс.
- REGIONCODE — код субъекта федерации.
Примечание: на fias-nalog.ru можно найти полное описание всех полей.
Важное в таблице HOUSEXX:
- AOGUID — ID улицы в ADDROBXX, в которой находится дом.
- HOUSEGUID — ID дома.
- Сложность заключается в наименовании дома. Оно состоит из 4 полей:
- BUILDNUM — номер корпуса.
- HOUSENUM — номер дома.
- STRUCNUM — номер строения.
- STRSTATUS — признак строения (от 0 до 4, где 0 — никакого, 1 — строение, 2 — сооружение, 3 — литера).
Поля могут содержать схожую или идентичную информацию и выходить из ситуации приходится экспериментальным путем. В классе QuerryController (ссылка на репозиторий — ниже) есть метод: chooseBuilding, в котором мы попытались решить эту задачу. Может быть у кого-то получится лучше. Запросы к БД находятся там же.
Далее, после того, как база была импортирована в Postgres — мы занялись созданием API и плагина для нашей системы.
Для API, дабы не изгаляться, использовали Laravel. Схема запросов получилась достаточно простой. Иерархия объектов выглядит следующим образом:
- Субъект федерации
- Регион субъекта федерации
- Населенный пункт
- Улица
- Дом
К слову, тестируя систему, мы столкнулись с тем, что не во всех населенных пунктах есть улицы, и не на всех улицах есть дома, что нас немало удивило, и озадачило. Это обстоятельство не давало покоя потому, что если предоставить пользователю возможность самому вводить информацию, то понапишут такого, что невольно возникает вопрос — “Эти люди вообще в школе-то учились?!”.
Поэтому было решено не давать пользователю ни малейшей возможности для самодеятельности, а для таких вот “отсутствующих” адресов предоставить возможность подать заявку на включение отсутствующего адреса в справочную систему.
Схема запросов выглядит просто:
- myfias/choose_dstrict — список субъектов федерации
- myfias/choose_region{district_aoguid} — список районов субъекта федерации
- myfias/choose_city{region_aoguid} — список населенных пунктов региона
- myfias/choose_street{city_aoguid} — список улиц населенного пункта
- myfias/choose_building{street_aoguid} — список домов улицы населенного пункта
Для последнего пункта формируется список домов с литерами, корпусами, строениями, и прочим.
После всех мытарств сделали плагин на vue.js, для работы со справочником, и его альтернативу на jQuery.
Регион и район можно убрать за ненадобностью, так как они подтягиваются вместе с городами.
При вводе высплывают autocomplete-подсказки, как и в КЛАДР. Правда разница заключается в том что КЛАДР-плагин предназначен для автодополнения, а здесь валидным считается только адрес, выбранный из подсказок.
В папке ASPUDcomponent — находится VueJs компонент для работы с адресной базой.
Исходники доступны в нашем репозитории.
Часть 4. Как это все обновлять?
С обновлениями ситуация следующая: для начала необходимо по протоколу SOAP получить версии обновлений. Посмотреть как это делается можно в классе UpdateController (метод: filesVersions()).
Примечание: версия, которая указана последней в полученном списке — не обязательно совпадает с той, которую можно скачать на главной странице. Но не стоит спешить скачивать только последнюю версию, так как она может оказаться “битой”. Случались и таким прецеденты. Далее скачивается архив с последней версией и распаковывается. Для работы необходимо использование расширения для php (php_rar.dll).
Ну а далее выбирается необходимый файл региона (или при необходимости все файлы) для обновления БД.
