Meu endereço não é uma casa ou rua, meu endereço é a União Soviética?

microBIGDATA ou FIAS no seu bolso


Peter Brueghel, o Jovem, Pagamento de Impostos , 1640

A última entrada no barbeador nos objetos foi. Continuaremos o reconhecimento em batalha. Hoje vamos falar sobre o difícil. Ainda não se trata de BIG DATA, mas já é inconveniente trabalhar - grandes quantidades de dados. Nem todos cabem em toda a RAM, mas alguns nem cabem no disco (não há espaço suficiente, mas muito lixo). O nome do nosso banco de dados FIAS de confiança é o banco de dados do sistema de informações de endereço federal. Arquivo de 5,5 GB. E é compactado em um arquivo XML. Após descompactar, haverá 53 GB completos (armazene 110 GB para descompactar). E quando você começa a analisá-lo e convertê-lo, 110 GB não serão suficientes. Sobre a quantidade necessária de RAM também será.

Tudo ficaria bem, mas você pode ir além. Existe um projeto internacional de coleta e sistematização de dados de código aberto - OpenAddresses . Portanto, haverá mais bancos de dados. A cobertura atual do planeta tem muitas manchas brancas, por exemplo, a Rússia está quase ausente. O tamanho do arquivo é de 10 GB.

Ou um banco de dados de um projeto OpenStreetMaps bastante conhecido. É construído por voluntários com base na Wikipedia. Muito detalhado e multilíngue. Agora, um arquivo completo de XML compactado com um tamanho de 74 GB.
Se eles começaram a falar sobre endereços, chegaram notícias inesperadas do DuckDuckGo , o melhor mecanismo de pesquisa seguro de hoje, sobre sua transição para os cartões da Apple. Mais precisamente no Apple MapKit JS. A característica mais interessante em nosso contexto é a "busca aprimorada de endereços". A Apple é a melhor coletiva e proteção cuidadosa de nossos dados? Será necessário rastrear ...
Então o desafio. Como colocar toda essa riqueza de endereços em um repositório agradável para uso, para permitir que você sonhe com uma API gratuita (em Python, é claro) e para não deixar seu querido ferro engasgar com uma carga não lida. Vamos chamá-lo de MicroBigData - mcBD ou μBG em inglês :-)

Na economia de cada segundo (ou mesmo do primeiro) desenvolvedor, esse é um diretório de endereços, também é um diretório de topônimos, algo muito necessário. E quando também é normativo, preparado, limpo e bem documentado pelo corpo certo, é apenas um conto de fadas. Devemos prestar homenagem, o serviço fiscal russo está fazendo bem sua produção digital. Tanto quanto possível. Provavelmente existem algumas falhas no interior e a limpeza dos dados continua. Como resolver esse problema, deixe os chefes de Estado pensarem. Eles decidem por nós mesmos e pelo benefício de todos nós. A propósito, um erro de digitação do FIAS foi encontrado no exemplo abaixo. O resultado não é afetado. Eu não consertei. Você vai encontrar?

Não sei até que ponto os dados de endereço são relevantes em seus projetos - todas são regiões, cidades, ruas. Mas parece que nem um único projeto para as pessoas pode prescindir delas. Esse é o endereço para onde procurar uma pessoa ou para onde enviar encomendas para ela. Os detalhes do passaporte ou qualquer outro documento devem ser salvos. Ou talvez seja o endereço do escritório ou das atrações que você recomenda visitar. E o que fazer? Onde obter

A solução mais simples, sem considerar erros e duplicatas, são objetos primitivos que contêm literais simples de cadeia de caracteres (são constantes de cadeia, também são cadeias de caracteres). Permita que os usuários insiram as próximas entradas recebidas neles. E os objetos são capazes de se salvar - já passamos por isso .

Tais objetos são, por exemplo, conforme descrito na classe abaixo. Diretamente do livro , embora americano, mas ajustado à nossa realidade russa - em vez de seu CEP, haverá o nosso código postal. Eu também substituiria o CEP por um número, mas por uma questão de monotonia, deixo uma string. Qualquer pessoa que reconheça imediatamente uma linguagem, e este é o ObjectScript, tem direito a um incentivo semelhante.

Class Soviet.Address Extends %Persistent { Property streetName As %String; Property cityName As %String; Property areaName As %String; Property postalCode As %String; } 

É claro que muitos ficarão indignados, dizem que tudo está saindo dos bolsos do objeto (literais). Onde foi visto, para que o objeto brilhe publicamente seus campos ?! Vamos deixar assim, dói também um exemplo eloqüente e é compreensível para qualquer aluno.

Isso é realmente tudo o que é necessário. Preenchido os campos. Coloque em armazenamento. Transferido para outros objetos para o trabalho. Herdado ainda mais por alguém. Tudo funciona. E armazenado!
Mas algumas palavras por que não vale a pena fazer isso, é preciso dizer. Qual é o nosso endereço de objeto? Por que não pode ser apenas um grupo de cadeias de texto? As objeções mais óbvias que vêm à mente vêm do contexto - quem usa este endereço, de que forma e com que finalidade? Tente deixar de lado seu pensamento de programação e imagine como um “turista estrangeiro”, “historiador”, “inspetor de impostos”, “advogado” e assim por diante pensam.

Creio que imediatamente surgem uma série de perguntas e esclarecimentos adicionais: que idioma usar, em que codificação armazenar e fornecer, em que época classificar, quais documentos são efetivados, legais ou postais? Uma cidade é um assentamento ou o quê? Mesmo uma rua pode se transformar em uma avenida, uma rua lateral, avenida ou outra coisa. O que fazer com todos esses detalhes importantes de implementação?

Tome um exemplo vivo. O Google agora é gerenciado por Sundar Pichai. Ele próprio é da Índia. Nascido na cidade de Chennai (também conhecido como Chennai). Ou em Madras? Em 1996, os índios decidiram que o nome da cidade era muito português e renomearam a capital de Tamil Nadu, de Madras para Chennai. E o que Sundar e 72 milhões de seus compatriotas devem escrever em seus documentos eletrônicos?

Em geral, toda a ciência lida com isso - toponímia aplicada .
Então as perguntas imploram. Como lidar com hora e data ? O dinheiro é tão óbvio ? As coordenadas geográficas são tão simples? E como isso é implementado no seu código? Você pode transferir para o DBMS selecionado sem diminuir o nível de abstração? Como não entrar nos tipos atômicos de dados da máquina e pensar constantemente em sua reconstrução? Aqui, vale a pena procurar a fonte de uma API primitiva ou, inversamente, sólida. Pense nisso à vontade.

Em resumo, o contexto é o mais importante. E o modelo de objeto nos dá a oportunidade de usá-lo diretamente, encapsulando “dados da máquina” e implementando um comportamento “ativo” sensível ao contexto. Nem todas as tuplas de baixo nível dispostas nas tabelas ;-)

Enquanto isso, voltemos à implementação "primitiva" e complicamos nossas vidas. Para começar, elimine erros e duplicatas. Ou seja, procuraremos uma maneira de escrever endereços imediatamente. Ao mesmo tempo, ajudaremos os desenvolvedores da interface do usuário a organizar dicas para os usuários ao preencher os campos de entrada de dados.
Quando duas pessoas se reúnem em um só lugar - textos e a plataforma de dados InterSystems IRIS, o desenvolvedor tem uma oportunidade real de implantar ao máximo sem sair da máquina. Por exemplo, usando os componentes de objeto internos iKnow e iFind . Esses são componentes para trabalhar com dados não estruturados e pesquisa de texto completo , respectivamente. O russo é suportado "pronto para uso".
Primeiramente, ensinaremos o Endereço a ler os dados necessários da fonte original. Felizmente, o conjunto de dados do serviço tributário federal possui descrições prontas da estrutura dos documentos XML. De acordo com a descrição anexada aos dados do site do FIAS , precisamos do conjunto de dados ADDROBJ, que, no meu caso, corresponde ao arquivo AS_ADDROBJ_2_250_01_04_01_01.xsd

Em seguida, usaremos o conversor do sistema do modelo XSD na estrutura de campo correspondente da classe% XML.Adaptor, gentilmente preparada para nós pelos desenvolvedores do IRIS. O sinal de porcentagem no início significa apenas que essa é uma classe da biblioteca do sistema. Detalhes de uso estão na documentação . Vamos realizar operações no terminal.

 set xmlScheme = ##class(%XML.Utils.SchemaReader).%New() do xmlScheme.Process("http://localhost/AS_ADDROBJ_2_250_01_04_01_01.xsd") 

O mesmo pode ser obtido no Atelier IDE (no menu Ferramentas> Suplementos> Assistente de Esquema XML) ou por solicitações semelhantes a objetos diretamente do código do programa.



Como usamos o construtor sem especificar os parâmetros, ou seja, o nome do pacote para colocar as classes resultantes, elas terminaram no pacote de teste. Como você pode ver no segundo comando, eu dei o arquivo de esquema através do meu servidor web local em Python:

 python3 -m http.server 80 

Você pode usar qualquer outro servidor http que desejar. Ou faça o upload do arquivo para o servidor IRIS e indique o caminho direto para ele.

Como resultado, temos duas classes que refletem completamente a estrutura do nosso XML endereçável:

Test.AddressObjects
 ///            Class Test.AddressObjects Extends (%Persistent, %XML.Adaptor) [ ProcedureBlock ] { Parameter XMLNAME = "AddressObjects"; Parameter XMLSEQUENCE = 1; ///    Relationship Object As Test.Object(XMLNAME = "Object", XMLPROJECTION = "ELEMENT") [ Cardinality = many, Inverse = AddressObjects ]; } 

Test.object
 ///  : http://localhost:28869/AS_ADDROBJ_2_250_01_04_01_01.xsd Class Test.Object Extends (%Persistent, %XML.Adaptor) [ ProcedureBlock ] { Parameter XMLNAME = "Object"; Parameter XMLSEQUENCE = 1; ///      Property AOGUID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "AOGUID", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///   Property FORMALNAME As %String(MAXLEN = 120, MINLEN = 1, XMLNAME = "FORMALNAME", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///   Property REGIONCODE As %String(MAXLEN = 2, MINLEN = 2, XMLNAME = "REGIONCODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///   Property AUTOCODE As %String(MAXLEN = 1, MINLEN = 1, XMLNAME = "AUTOCODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///   Property AREACODE As %String(MAXLEN = 3, MINLEN = 3, XMLNAME = "AREACODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///   Property CITYCODE As %String(MAXLEN = 3, MINLEN = 3, XMLNAME = "CITYCODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///    Property CTARCODE As %String(MAXLEN = 3, MINLEN = 3, XMLNAME = "CTARCODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///    Property PLACECODE As %String(MAXLEN = 3, MINLEN = 3, XMLNAME = "PLACECODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///     Property PLANCODE As %String(MAXLEN = 4, MINLEN = 4, XMLNAME = "PLANCODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///   Property STREETCODE As %String(MAXLEN = 4, MINLEN = 4, XMLNAME = "STREETCODE", XMLPROJECTION = "ATTRIBUTE"); ///     Property EXTRCODE As %String(MAXLEN = 4, MINLEN = 4, XMLNAME = "EXTRCODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///      Property SEXTCODE As %String(MAXLEN = 3, MINLEN = 3, XMLNAME = "SEXTCODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///   Property OFFNAME As %String(MAXLEN = 120, MINLEN = 1, XMLNAME = "OFFNAME", XMLPROJECTION = "ATTRIBUTE"); ///   Property POSTALCODE As %String(MAXLEN = 6, MINLEN = 6, XMLNAME = "POSTALCODE", XMLPROJECTION = "ATTRIBUTE"); ///    Property IFNSFL As %String(MAXLEN = 4, MINLEN = 4, XMLNAME = "IFNSFL", XMLPROJECTION = "ATTRIBUTE"); ///      Property TERRIFNSFL As %String(MAXLEN = 4, MINLEN = 4, XMLNAME = "TERRIFNSFL", XMLPROJECTION = "ATTRIBUTE"); ///    Property IFNSUL As %String(MAXLEN = 4, MINLEN = 4, XMLNAME = "IFNSUL", XMLPROJECTION = "ATTRIBUTE"); ///      Property TERRIFNSUL As %String(MAXLEN = 4, MINLEN = 4, XMLNAME = "TERRIFNSUL", XMLPROJECTION = "ATTRIBUTE"); /// OKATO Property OKATO As %String(MAXLEN = 11, MINLEN = 11, XMLNAME = "OKATO", XMLPROJECTION = "ATTRIBUTE"); /// OKTMO Property OKTMO As %String(MAXLEN = 11, MINLEN = 8, XMLNAME = "OKTMO", XMLPROJECTION = "ATTRIBUTE"); ///    Property UPDATEDATE As %Date(XMLNAME = "UPDATEDATE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///     Property SHORTNAME As %String(MAXLEN = 10, MINLEN = 1, XMLNAME = "SHORTNAME", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///    Property AOLEVEL As %Integer(XMLNAME = "AOLEVEL", XMLPROJECTION = "ATTRIBUTE", XMLTotalDigits = 10) [ Required ]; ///     Property PARENTGUID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "PARENTGUID", XMLPROJECTION = "ATTRIBUTE"); ///   .  . Property AOID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "AOID", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///        Property PREVID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "PREVID", XMLPROJECTION = "ATTRIBUTE"); ///        Property NEXTID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "NEXTID", XMLPROJECTION = "ATTRIBUTE"); ///           4.0. Property CODE As %String(MAXLEN = 17, MINLEN = 0, XMLNAME = "CODE", XMLPROJECTION = "ATTRIBUTE"); ///      4.0      (  ) Property PLAINCODE As %String(MAXLEN = 15, MINLEN = 0, XMLNAME = "PLAINCODE", XMLPROJECTION = "ATTRIBUTE"); ///     .     .      . /// 0 –   /// 1 -  Property ACTSTATUS As %Integer(XMLNAME = "ACTSTATUS", XMLPROJECTION = "ATTRIBUTE", XMLTotalDigits = 10) [ Required ]; ///   Property CENTSTATUS As %Integer(XMLNAME = "CENTSTATUS", XMLPROJECTION = "ATTRIBUTE", XMLTotalDigits = 10) [ Required ]; ///     –    (.   OperationStatus): /// 01 – ; /// 10 – ; /// 20 – ; /// 21 –  ; /// 30 – ; /// 31 -     ; /// 40 –    (); /// 41 –     ; /// 42 -        ; /// 43 -         ; /// 50 – ; /// 51 –     ; /// 60 –    ; /// 61 –        Property OPERSTATUS As %Integer(XMLNAME = "OPERSTATUS", XMLPROJECTION = "ATTRIBUTE", XMLTotalDigits = 10) [ Required ]; ///    4 (    ) Property CURRSTATUS As %Integer(XMLNAME = "CURRSTATUS", XMLPROJECTION = "ATTRIBUTE", XMLTotalDigits = 10) [ Required ]; ///    Property STARTDATE As %Date(XMLNAME = "STARTDATE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///    Property ENDDATE As %Date(XMLNAME = "ENDDATE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///      Property NORMDOC As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "NORMDOC", XMLPROJECTION = "ATTRIBUTE"); ///     Property LIVESTATUS As %xsd.byte(VALUELIST = ",0,1", XMLNAME = "LIVESTATUS", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///  : /// 0 -   /// 1 - ; /// 2 - - Property DIVTYPE As %xsd.int(VALUELIST = ",0,1,2", XMLNAME = "DIVTYPE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; Relationship AddressObjects As Test.AddressObjects(XMLPROJECTION = "NONE") [ Cardinality = one, Inverse = Object ]; } 


De toda a lista de arquivos xml no FIAS, usaremos apenas um arquivo com os nomes de regiões, cidades e ruas. No momento da preparação da publicação, eu tinha o seguinte:
AS_ADDROBJ_20190106_90809714-fe22-45b2-929c-52bd950963e0.XML

O tamanho do arquivo não é grande nem pequeno, mas quase 3 GB. Você não o abrirá com ferramentas de texto comuns - elas não digerem esse tamanho.
A propósito, o comprimento máximo de uma string literal (tipo String) no InterSystems IRIS não passa de 3.641.144 caracteres. Ou seja, o download do arquivo diretamente ou a URL nele falhará. Outras restrições podem ser encontradas na documentação . Para trabalhar com grandes volumes de dados, você pode usar fluxos de dados (fluxos) que não possuem um limite de tamanho.
Vamos ver o que temos?

Cozinhar FIAS pimenta recheada. Esta é apenas uma preparação para um futuro maravilhoso. Primeiro, obtemos o conjunto mínimo inicial. Precisamos apenas destes ingredientes:

 Class FIAS.AddressObject Extends (%Persistent, %XML.Adaptor) [ ProcedureBlock ] { Parameter XMLNAME = "Object"; Parameter XMLSEQUENCE = 1; ///      Property AOGUID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "AOGUID", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///   Property OFFNAME As %String(MAXLEN = 120, MINLEN = 1, XMLNAME = "OFFNAME", XMLPROJECTION = "ATTRIBUTE"); ///   Property POSTALCODE As %String(MAXLEN = 6, MINLEN = 6, XMLNAME = "POSTALCODE", XMLPROJECTION = "ATTRIBUTE"); ///     Property SHORTNAME As %String(MAXLEN = 10, MINLEN = 1, XMLNAME = "SHORTNAME", XMLPROJECTION = "ATTRIBUTE") [ Required ]; ///    Property AOLEVEL As %Integer(XMLNAME = "AOLEVEL", XMLPROJECTION = "ATTRIBUTE", XMLTotalDigits = 10) [ Required ]; ///     Property PARENTGUID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "PARENTGUID", XMLPROJECTION = "ATTRIBUTE"); ///   .  . Property AOID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "AOID", XMLPROJECTION = "ATTRIBUTE") [ Required ]; 

Em seguida, escreva . Criamos um objeto que entende XML como nativo - usamos uma classe da biblioteca de sistema% XML.Reader:

 set reader = ##class(%XML.Reader).%New() 

E damos-lhe instruções a quem levar e ignoramos o resto. Tomaremos uma porção:

 do reader.Correlate("Object","FIAS.AddressObject") 

Depois, há variações sobre como obter o arquivo microbd original. Se for conveniente, você pode colocá-lo ao lado do repositório - localmente no sistema de arquivos do servidor IRIS. Ou, como no meu exemplo, peça para enviar via HTTP. Existe uma opção ainda mais universal, sobre a qual haverá algumas palavras abaixo.

 set url="http://localhost/AS_ADDROBJ_20190106_90809714-fe22-45b2-929c-52bd950963e0.XML" write reader.OpenUrl(url) 

Importante! Neste momento, a maioria que passará esse exemplo para si terá uma coisa terrível. O sistema retornará em vez do alegre "1" (tudo está em ordem), algo começando com "0 ¸ STORE ...". E não vai agradar. Ou seja, o arquivo com uma microbase aparentemente não é muito micro e não se encaixa no nosso objeto. A memória alocada para ele não era suficiente. Solúvel? Claro. A plataforma de dados IRIS permite criar objetos de até 4 TB na RAM. Então o que deu errado? Por padrão, as configurações do sistema são definidas para 256 MB por objeto. E precisaríamos de muito mais. E lembre-se, esses são requisitos de RAM. Existe estoque suficiente no seu computador / servidor?
Qual o tamanho da memória que precisamos para instalar este gigante que instalamos empiricamente - quase 10 GB. O que você precisa especificar nas configurações (Menu> Configurar memória> Memória máxima por processo (KB)) ou através da variável de sistema $ ZSTORAGE (em kilobytes):

 set $ZSTORAGE=10000000 

Lançou um novo processo com as configurações de memória necessárias? Então tudo é mais simples - lemos e salvamos.

Existe uma opção alternativa (e provavelmente preferida) - use a propriedade UsePPGHandler da classe% XML.Reader que permite que você não armazene XML na memória e trabalhe com configurações de memória padrão.

 set reader = ##class(%XML.Reader).%New() set reader.UsePPGHandler = 1 

mais ... Correlate / Read, etc. ...

 do reader.Next(.object) do object.%Save() 

E assim 3.722.548 vezes para cada operação :-)

Isso é cansativo. Portanto, complementamos nossa classe FIAS.AddressObject com um método de importação, com base nos comandos mostrados:

 ClassMethod Import() { //     XML Set reader = ##class(%XML.Reader).%New() //   XML   Set status = reader.OpenURL("http://localhost/AS_ADDROBJ_20190106_90809714-fe22-45b2-929c-52bd950963e0.XML") If $$$ISERR(status) {Do $System.Status.DisplayError(status)} //       Do reader.Correlate("Object","FIAS.AddressObject") //       While (reader.Next(.object,.status)) { Set status = object.%Save() If $$$ISERR(status) {do $System.Status.DisplayError(status)} } //      ,   If $$$ISERR(status) {Do $System.Status.DisplayError(status)} } 

Vamos usar o poder do exocórtex do computador - apenas um comando no terminal :

 do ##class(FIAS.AddressObject).Import() 



Peço a todos para a mesa. Havia MCD, e agora o prato pronto na forma de um global com os nomes verificados de cidades e pesos russos está pronto.



E, finalmente, algumas palavras sobre quando 4 TB não é suficiente. Nesse caso, seguimos os fluxos (ou fluxos, se desejar). A documentação é apresentada nas prateleiras. Você pode binário, você pode simbólico. Loja no global também não é proibida. A receita é a seguinte: pegamos uma corrente, cortamos em partes e entregamos aos objetos que precisamos para o consumo.

Além disso, sobre belos objetos ObjectScript endereçáveis ​​e APIs em Python não se encaixavam. Haverá uma história separada.
Agradável: o Gartner acaba de concluir a coleção anual de classificações e avaliações reais de usuários na categoria DBMS e, com base nisso, publicou sua classificação dos melhores DBMS de 2019. Os produtos InterSystems Caché e InterSystems IRIS Data Platform receberam a mais alta classificação de escolha do consumidor. De quem você escolheu e como avaliou, você mesmo pode dar uma olhada .
Melhor Software de Sistemas Operacionais de Gerenciamento de Banco de Dados de 2019, conforme revisto pelos clientes

Source: https://habr.com/ru/post/pt438062/


All Articles