Como escrever o endereço residencial certo?

Como o IRS, OpenStreetMap e InterSystems IRIS
poderia ajudar os desenvolvedores a obter endereços limpos


imagem
Pieter Brueghel, o Jovem, Pagando o Imposto (O Coletor de Impostos), 1640

No meu artigo anterior , nós apenas deslizamos a superfície dos objetos. Vamos continuar nosso reconhecimento. O tópico de hoje é difícil. Não são dados GRANDES, mas ainda não é fácil trabalhar com os dados: estamos falando de quantidades bastante grandes de dados. Nem tudo se encaixa na memória RAM de uma só vez, e algumas delas nem se encaixam na unidade (não devido à falta de espaço, mas porque há muito lixo). O nome do nosso assunto é FIAS DB : o banco de dados do Federal Information Address System - os bancos de dados de endereços na Rússia. O arquivo tem 5,5 GB. E é um arquivo XML compactado. Após a extração, serão 53 GB completos (reserve 110 GB para extração). E quando você começa a analisar e convertê-lo, esses 110 GB não serão suficientes. Também não haverá RAM suficiente.

Tudo ficaria bem, mas você poderia continuar cavando. Já existe um projeto internacional de código aberto para coletar e sistematizar dados de endereço: OpenAddresses . Seus bancos de dados serão ainda maiores. Atualmente, existem muitos espaços em branco em seus mapas. A Rússia, por exemplo, está quase vazia. O tamanho deste arquivo é de 10 GB.

Mas, antes de tudo, consideramos o banco de dados do projeto bastante conhecido OpenStreetMaps . É construído por voluntários, seguindo o exemplo da Wikipedia. É bem completo e multilíngue. E o projeto acaba de receber o prêmio 2018 The Free Software Foundation . No momento, o tamanho de todo o arquivo morto como um arquivo XML compactado é de 74 GB.

Por falar em endereços, recebi notícias inesperadas do DuckDuckGo , o melhor mecanismo de pesquisa seguro até o momento, que anunciou sua mudança para o Apple Maps. Mais precisamente, para o Apple MapKit JS. O mais interessante sobre isso para nossos propósitos é a "pesquisa aprimorada de endereços". A Apple é melhor do que todos os demais na coleta e proteção meticulosas de nossos dados? Teremos que ficar de olho ...

Então, aqui está o desafio. Como podemos colocar todos esses dados de endereço em um repositório fácil de usar, possibilitar a criação de uma API fácil (em Python, é claro) e impedir que nosso amado hardware desmorone sob esse fardo massivo? Vamos chamar isso de MicroBigData, mD ou µBD, para abreviar.

Quase todos os desenvolvedores ouviram a seguinte piada. Um diretório de endereço é um diretório de nomes de lugares, o que é uma coisa muito útil. Não sei exatamente quanto dos dados de endereço em seus projetos estão atualizados. Afinal, existem muitas regiões, cidades e ruas. Mas parece que eles são necessários para qualquer projeto que envolva pessoas. Existem endereços onde você pode encontrar alguém ou enviar um pacote. Depois, há as informações necessárias para passaportes e outros documentos. E talvez haja o endereço de algum escritório ou ponto de referência que alguém lhe recomendou. Então o que você deve fazer? Onde você deve conseguir isso?

Sem considerar erros e duplicatas, a solução mais simples envolve objetos primitivos que contêm literais simples de seqüência de caracteres (ou constantes de seqüência de caracteres). Permita que os usuários façam entradas adicionais. E os objetos sabem como se salvar, como abordamos anteriormente .

Tome os objetos descritos na classe abaixo como exemplo. Um caso de livro didático , embora na forma de um endereço nos EUA, mas com um ajuste para o nosso conjunto de dados russo: postalCode em vez de ZIP. Eu também mudaria o código postal para um número, mas deixarei como uma string para manter as coisas uniformes. Qualquer pessoa que reconheça a linguagem (ObjectScript) imediatamente receberá um "curtir" de cortesia.

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

Claro, muitas pessoas vão chorar, dizendo que todos os literais estão saindo do objeto. Quem já ouviu falar de um objeto publicamente divulgando seus campos? Vamos deixar assim por enquanto. É um exemplo bastante eloquente, e qualquer aluno pode entender.

Na verdade, é tudo o que precisamos. Nós preenchemos os campos. Armazenou-os. Entregou-os a outros objetos. Alguém os herdará. Tudo funciona. E está armazenado!

Mas tenho que dizer algumas palavras sobre o motivo de não ser assim. 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 surgem vêm do contexto: quem está usando este endereço, de que forma o está usando e com que finalidade? Tente colocar a lógica do programador de lado e imagine como um "turista estrangeiro", "historiador", "cobrador de impostos" ou "advogado" pensa.

Suponho que você tenha encontrado imediatamente um monte de perguntas adicionais: qual idioma e codificação usar, que período de tempo considerar e que tipo de documentos estão envolvidos nessa operação: legal ou postal? E uma cidade: é uma localidade nomeada ou o quê? Até uma rua pode ser uma avenida, faixa, avenida ou outra coisa. Como todos esses detalhes importantes devem ser tratados?

Vejamos um exemplo da vida real. O Google agora é gerenciado por Sundar Pichai. Ele é da Índia. Ele nasceu na cidade de Chennai. Ou é Madras? Em 1996, os moradores decidiram que o nome da cidade parecia muito português e renomearam a capital do estado de Tamil Nadu, de Madras para Chennai. Então, o que Sundar e seus 72 milhões de compatriotas devem inserir em seus documentos eletrônicos?

De fato, existe toda uma ciência que estuda isso: toponímia aplicada .
Portanto, existem algumas perguntas de acompanhamento. Como a hora e a data devem ser tratadas? E o óbvio, dinheiro ? Ou coordenadas geográficas? E como você deve implementar isso no seu código? E você poderá transferi-lo para o DBMS de sua escolha sem diminuir a camada de abstração? Como você evita a espiral descendente em tipos atômicos de dados de máquinas e pensamentos constantes sobre sua reconstrução? Nesse caso, vale a pena procurar a fonte de uma API primitiva ou, inversamente, de boa qualidade. Pense nisso à sua vontade.

Em resumo, o contexto é a coisa mais importante. E o modelo de objeto nos permite utilizá-lo diretamente, encapsulando "dados da máquina" e implementando o comportamento "da vida real" dependente do contexto. Não é que tuplas de baixo nível estejam organizadas em tabelas ;-)

Enquanto isso, retornaremos à implementação "primitiva" e tornaremos as coisas mais difíceis para nós mesmos. Para começar, eliminaremos erros e duplicatas. Em outras palavras, procuraremos uma maneira de escrever endereços corretamente na primeira vez. Ao mesmo tempo, ajudaremos os desenvolvedores da interface do usuário a fornecer dicas para os usuários quando eles estiverem inserindo dados nos campos.
Quando existem duas coisas em um só lugar - textos e a plataforma de dados InterSystems IRIS - um desenvolvedor tem uma oportunidade real de realmente mudar as coisas sem se afastar da máquina. Usando os componentes do objeto incorporado iKnow e iFind , por exemplo. Esses componentes destinam-se ao trabalho com dados não estruturados e pesquisa de texto completo , respectivamente.
Vamos tentar encontrar o esquema de dados para o OpenStreetMap. Não é tão fácil quanto pode parecer à primeira vista. Não sei o motivo exato, mas não há esquema de dados para o OSM . E isso nos ajudaria muito, como será visto abaixo! E isso não reinventaria a roda, use um XSD adequado , que eu achei para você. E obrigado, Oliver Schrenk. Aqui estão mais fotos . Devo dizer que, para nossos propósitos, é adequado e corresponde à estrutura interna dos arquivos XML e downloads do OSM. Por que é importante, mas a primeira linha no arquivo XSD deve começar com "<? Xml ...".

Elementos são os componentes básicos do modelo de dados conceituais do OpenStreetMap do mundo físico. Eles consistem em

  • nós - definindo pontos no espaço,
  • maneiras - definindo características lineares e limites de área, e
  • relações - que às vezes são usadas para explicar como outros elementos funcionam juntos.

Todas as opções acima podem ter uma ou mais tags associadas (que descrevem o significado de um elemento específico). Uma tag consiste em dois itens, uma chave e um valor. As tags descrevem recursos específicos dos elementos do mapa: nós, maneiras ou relações.

Onde estão as ruas e cidades? É um grande segredo! A geometria foi bem ensinada? Mais sobre isso na próxima vez. :)

Além disso, usaremos o assistente de esquema XSD, graciosamente criado para nós pelos desenvolvedores do IRIS, para a classe apropriada% XML. Campos do adaptador. O sinal de porcentagem no início significa apenas que essa é uma classe da biblioteca do sistema. Mais informações podem ser encontradas na documentação . Vamos realizar as operações no terminal.

 set xmlSchema = ##class(%XML.Utils.SchemaReader).%New() do xmlSchema.Process("/path/to/OSMSchema.xsd") 

Você pode obter o mesmo no Atelier IDE (no menu, vá em Ferramentas> Suplementos> Assistente de esquema XML):

imagem

Como usamos o assistente indicando o nome do pacote resultante e não os parâmetros, eles terminaram no pacote de teste. Como você pode ver no segundo comando, passei o arquivo de esquema para meu servidor Python local:

 python3 -m http.server 80 

Você pode usar qualquer outro servidor http que desejar. Ou carregue o arquivo no servidor IRIS e aponte para ele.

Como resultado, temos oito classes que refletem completamente a estrutura do nosso endereço XML. Esta é a classe principal OSM.osm:

 Class OSM.osm Extends (%Persistent, %XML.Adaptor) [ ProcedureBlock ] { Parameter XMLNAME = "osm"; Parameter XMLSEQUENCE = 1; Property bounds As OSM.bounds(XMLNAME = "bounds", XMLREF = 1) [ Required ]; Relationship node As OSM.node(XMLNAME = "node", XMLPROJECTION = "ELEMENT", XMLREF = 1) [ Cardinality = many, Inverse = osm ]; Relationship way As OSM.way(XMLNAME = "way", XMLPROJECTION = "ELEMENT", XMLREF = 1) [ Cardinality = many, Inverse = osm1 ]; Relationship relation As OSM.relation(XMLNAME = "relation", XMLPROJECTION = "ELEMENT", XMLREF = 1) [ Cardinality = many, Inverse = osm2 ]; Property version As %xsd.float(XMLNAME = "version", XMLPROJECTION = "ATTRIBUTE") [ InitialExpression = ".6", ReadOnly ]; Property generator As %String(MAXLEN = "", XMLNAME = "generator", XMLPROJECTION = "ATTRIBUTE") [ InitialExpression = "CGImap 0.0.2", ReadOnly ]; } 

E OSM.node:

 Class OSM.node Extends (%Persistent, %XML.Adaptor) [ ProcedureBlock ] { Parameter XMLNAME = "node"; Parameter XMLSEQUENCE = 1; Relationship tag As OSM.tag(XMLNAME = "tag", XMLPROJECTION = "ELEMENT", XMLREF = 1) [ Cardinality = many, Inverse = node ]; Property id As %xsd.unsignedLong(XMLNAME = "id", XMLPROJECTION = "ATTRIBUTE"); Property lat As %xsd.double(XMLNAME = "lat", XMLPROJECTION = "ATTRIBUTE"); Property lon As %xsd.double(XMLNAME = "lon", XMLPROJECTION = "ATTRIBUTE"); Property user As %String(MAXLEN = "", XMLNAME = "user", XMLPROJECTION = "ATTRIBUTE") [ SqlFieldName = _user ]; Property uid As %xsd.unsignedLong(XMLNAME = "uid", XMLPROJECTION = "ATTRIBUTE"); Property visible As %Boolean(XMLNAME = "visible", XMLPROJECTION = "ATTRIBUTE"); Property version As %xsd.unsignedLong(XMLNAME = "version", XMLPROJECTION = "ATTRIBUTE"); Property changeset As %xsd.unsignedLong(XMLNAME = "changeset", XMLPROJECTION = "ATTRIBUTE"); Property timestamp As %TimeStamp(XMLNAME = "timestamp", XMLPROJECTION = "ATTRIBUTE"); Relationship osm As OSM.osm(XMLPROJECTION = "NONE") [ Cardinality = one, Inverse = node ]; } 

Como você pode ver, algumas das opções que eu já desativei como desnecessárias para nossa solução.

O XML do tamanho do arquivo apenas para a Rússia é de aproximadamente 53 GB. Você não pode abri-lo com as ferramentas usuais de processamento de texto: elas não podem suportar arquivos tão grandes. Você pode coletar amostras menores para exercitar, por exemplo, endereços da Rússia estão disponíveis para os territórios individuais . O pequeno volume da região de Kaliningrado no formato compactado será 18 MB, o arquivo XML não compactado será 203 MB.
A propósito, o comprimento máximo de uma literal de cadeia de caracteres no InterSystems IRIS é de 3.641.144 caracteres. Em outras palavras, carregar um arquivo ou URL diretamente nele não funcionará. Você pode ver os outros limites na documentação . Para trabalhar com grandes quantidades de dados, você pode usar fluxos de dados que não têm restrições de comprimento.
Vamos ver o que obtemos para o conjunto de nós.

Em seguida, faremos as coisas de acordo com o livro . Criaremos um objeto que entende XML como idioma nativo usando uma classe da biblioteca de sistema% XML.Reader:

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

Vamos dar instruções sobre o que levar e ignorar o resto. Vamos dar uma aula única:

 do reader.Correlate("node","OSM.node") 

Depois disso, existem várias maneiras de obter o arquivo mBD original. Se conveniente, você pode colocá-lo próximo ao repositório de armazenamento, localmente no sistema de arquivos do servidor IRIS. Ou, como no meu exemplo, solicite que seja enviado via HTTP. Há também uma opção mais universal, da qual falarei um pouco abaixo.

 set url="http://localhost/kaliningrad-latest.osm" write reader.OpenUrl(url) 

Importante! Nesse ponto, a maioria das pessoas que tenta esse exemplo por conta própria encontrará algo horrível. Em vez de um feliz "1" (está tudo bem), o sistema retornará algo começando com "0, ARMAZENAR ..." E isso será decepcionante. Em outras palavras, o arquivo com o que parece ser mBD acabou não sendo tão micro e não se encaixa no nosso objeto. Não havia memória suficiente alocada para ele. Isso pode ser corrigido? Absolutamente. A plataforma de dados IRIS permite criar objetos de até 4 TB na RAM. Então, o que deu errado? Por padrão, o tamanho de um objeto é de 256 MB nas configurações do sistema. Mas precisamos de muito mais que isso. E lembre-se, esses são requisitos de RAM. Você tem espaço suficiente no seu computador / servidor?
Eu experimentei para determinar a quantidade de memória que precisamos para acomodar esse gigante: quase 170 GB. Isso deve ser especificado nas configurações (Menu> Configurar memória> Capacidade máxima de memória por processo (KB)) ou através da variável de sistema $ ZSTORAGE (em kilobytes):

 set $ZSTORAGE=170000000 

Você executou um novo processo com as configurações corretas de memória? Então a próxima parte é fácil: nós apenas lemos e salvamos.

Há também uma opção alternativa (e provavelmente preferível): use a propriedade UsePPG Handler da classe% XML. Reader, que permite que você não armazene o XML na memória e trabalhe com as configurações de memória padrão.

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

Próximo ... Correlacionar / Ler, etc. ...

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

E assim por diante, 1.180.849 vezes para cada operação :-) É entediante. É por isso que adicionaremos nosso método de classe OSM.map para importação, com base nos mesmos comandos:

 ClassMethod Import(url) { Set reader = ##class(%XML.Reader).%New() Set reader.UsePPGHandler = 1 Set status = reader.OpenURL(url) Do reader.Correlate("node","OSM.node") While (reader.Next(.object)) { Do object.%Save() } //back to top of XML file Do reader.Rewind() Do reader.Correlate("way","OSM.way") While (reader.Next(.object)) { Do object.%Save() } Do reader.Rewind() Do reader.Correlate("relation","OSM.relation") While (reader.Next(.object)) { Do object.%Save() } } 

Usaremos o poder do exocórtex do computador com apenas um comando no terminal :

 do ##class(OSM.osm).Import("http://localhost/kaliningrad-latest.osm") 

E assim, obtemos os dados de endereço da fonte aberta e talvez não muito confiável. É hora de passar pelos mesmos estágios, mas com dados confiáveis. E também padronizados, limpos, bem documentados e elaborados pelo órgão governamental certo: isso é lenda. Para seu crédito, o serviço fiscal russo está fazendo um bom trabalho com seu produto digital. Na medida em que um bom trabalho é possível. Para ter certeza, ele tem suas deficiências e a limpeza de dados está em andamento. Quanto a como podemos resolver isso, que os líderes do governo ponderem isso. Eles estão tomando decisões que beneficiam a todos nós.

E agora vamos ao mais incompreensível - ensinaremos o Address a ler os dados corretos da nossa fonte. Felizmente, o conjunto de dados do serviço de imposto federal possui descrições prontas para a estrutura de documentos XML. De acordo com a descrição do site do FIAS que acompanha os dados, precisaremos do conjunto de dados ADDROBJ que, no meu caso, corresponde ao arquivo AS_ADDROBJ_2_250_01_04_01_01.xsd

Em seguida, vamos usar o assistente de esquema XSD. Realizaremos as operações no terminal:

 set xmlScheme = ##class(%XML.Utils.SchemaReader).%New() do xmlScheme.Process("/path/to/AS_ADDROBJ_2_250_01_04_01_01.xsd") 

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

Test.AddressObjects

 /// Composition and structure of the file with classifier information for FIAS DB elements in address form Class Test.AddressObjects Extends (%Persistent, %XML.Adaptor) [ ProcedureBlock ] { Parameter XMLNAME = "AddressObjects"; Parameter XMLSEQUENCE = 1; /// Classifier for elements in address form Relationship Object As Test.Object(XMLNAME = "Object", XMLPROJECTION = "ELEMENT") [ Cardinality = many, Inverse = AddressObjects ]; } 

Test.object

 /// Created from: 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; /// Global unique identifier of the address object Property AOGUID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "AOGUID", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Formal name Property FORMALNAME As %String(MAXLEN = 120, MINLEN = 1, XMLNAME = "FORMALNAME", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Region code Property REGIONCODE As %String(MAXLEN = 2, MINLEN = 2, XMLNAME = "REGIONCODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Autonomy code Property AUTOCODE As %String(MAXLEN = 1, MINLEN = 1, XMLNAME = "AUTOCODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Area code Property AREACODE As %String(MAXLEN = 3, MINLEN = 3, XMLNAME = "AREACODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// City code Property CITYCODE As %String(MAXLEN = 3, MINLEN = 3, XMLNAME = "CITYCODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Code of area within city Property CTARCODE As %String(MAXLEN = 3, MINLEN = 3, XMLNAME = "CTARCODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Locality code Property PLACECODE As %String(MAXLEN = 3, MINLEN = 3, XMLNAME = "PLACECODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Planning structure element code Property PLANCODE As %String(MAXLEN = 4, MINLEN = 4, XMLNAME = "PLANCODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Street code Property STREETCODE As %String(MAXLEN = 4, MINLEN = 4, XMLNAME = "STREETCODE", XMLPROJECTION = "ATTRIBUTE"); /// Code of additional element in address form Property EXTRCODE As %String(MAXLEN = 4, MINLEN = 4, XMLNAME = "EXTRCODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Code of subordinate additional element in address form Property SEXTCODE As %String(MAXLEN = 3, MINLEN = 3, XMLNAME = "SEXTCODE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Official name Property OFFNAME As %String(MAXLEN = 120, MINLEN = 1, XMLNAME = "OFFNAME", XMLPROJECTION = "ATTRIBUTE"); /// Postal code Property POSTALCODE As %String(MAXLEN = 6, MINLEN = 6, XMLNAME = "POSTALCODE", XMLPROJECTION = "ATTRIBUTE"); /// Federal Tax Service - Private Individual code Property IFNSFL As %String(MAXLEN = 4, MINLEN = 4, XMLNAME = "IFNSFL", XMLPROJECTION = "ATTRIBUTE"); /// Federal Tax Service - Private Individual territorial district code Property TERRIFNSFL As %String(MAXLEN = 4, MINLEN = 4, XMLNAME = "TERRIFNSFL", XMLPROJECTION = "ATTRIBUTE"); /// Federal Tax Service - Legal Entity code Property IFNSUL As %String(MAXLEN = 4, MINLEN = 4, XMLNAME = "IFNSUL", XMLPROJECTION = "ATTRIBUTE"); /// Federal Tax Service - Legal Entity territorial district code Property TERRIFNSUL As %String(MAXLEN = 4, MINLEN = 4, XMLNAME = "TERRIFNSUL", XMLPROJECTION = "ATTRIBUTE"); /// Russian Classification on Objects of Administrative Division Property OKATO As %String(MAXLEN = 11, MINLEN = 11, XMLNAME = "OKATO", XMLPROJECTION = "ATTRIBUTE"); /// Russian Classification of Territories of Municipal Formations Property OKTMO As %String(MAXLEN = 11, MINLEN = 8, XMLNAME = "OKTMO", XMLPROJECTION = "ATTRIBUTE"); /// Date of record entry Property UPDATEDATE As %Date(XMLNAME = "UPDATEDATE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Short name of object type Property SHORTNAME As %String(MAXLEN = 10, MINLEN = 1, XMLNAME = "SHORTNAME", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Address object level Property AOLEVEL As %Integer(XMLNAME = "AOLEVEL", XMLPROJECTION = "ATTRIBUTE", XMLTotalDigits = 10) [ Required ]; /// Object identifier of the parent object Property PARENTGUID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "PARENTGUID", XMLPROJECTION = "ATTRIBUTE"); /// Unique record identifier. Key field. Property AOID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "AOID", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Record identifier associated with previous historical record Property PREVID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "PREVID", XMLPROJECTION = "ATTRIBUTE"); /// Record identifier associated with next historical record Property NEXTID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "NEXTID", XMLPROJECTION = "ATTRIBUTE"); /// Address object code in one string with validity indicator from Russian Classifier of Addresses (KLADR) 4.0. Property CODE As %String(MAXLEN = 17, MINLEN = 0, XMLNAME = "CODE", XMLPROJECTION = "ATTRIBUTE"); /// Address object code from KLADR 4.0 in one string without validity indicator (last two digits) Property PLAINCODE As %String(MAXLEN = 15, MINLEN = 0, XMLNAME = "PLAINCODE", XMLPROJECTION = "ATTRIBUTE"); /// Validity status of FIAS address object. Current address as of today's date. Usually the last entry about the address object. /// 0 - Not current /// 1 - Current Property ACTSTATUS As %Integer(XMLNAME = "ACTSTATUS", XMLPROJECTION = "ATTRIBUTE", XMLTotalDigits = 10) [ Required ]; /// Center status Property CENTSTATUS As %Integer(XMLNAME = "CENTSTATUS", XMLPROJECTION = "ATTRIBUTE", XMLTotalDigits = 10) [ Required ]; /// Operation status on record - reason for record's appearance (see description of OperationStatus table): /// 01 – Activation; /// 10 – Addition; /// 20 – Change; /// 21 – Group change; /// 30 – Deletion; /// 31 - Deletion due to the deletion of the parent object; /// 40 – Attachment of the address object (merger); /// 41 – Reassignment due to the merger of the parent object; /// 42 - Termination due to the attachment to another address object; /// 43 - Creation of a new address object due to a merger of address objects; /// 50 – Reassignment; /// 51 – Reassignment due to the reassignment of the parent object; /// 60 – Termination due to segmentation; /// 61 – Creation of a new address object due to segmentation Property OPERSTATUS As %Integer(XMLNAME = "OPERSTATUS", XMLPROJECTION = "ATTRIBUTE", XMLTotalDigits = 10) [ Required ]; /// KLADR 4 validity status (last two digits in the code) Property CURRSTATUS As %Integer(XMLNAME = "CURRSTATUS", XMLPROJECTION = "ATTRIBUTE", XMLTotalDigits = 10) [ Required ]; /// Start of record operation Property STARTDATE As %Date(XMLNAME = "STARTDATE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// End of record operation Property ENDDATE As %Date(XMLNAME = "ENDDATE", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Foreign key to requirements document Property NORMDOC As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "NORMDOC", XMLPROJECTION = "ATTRIBUTE"); /// Current address object indicator Property LIVESTATUS As %xsd.byte(VALUELIST = ",0,1", XMLNAME = "LIVESTATUS", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Address type: /// 0 - not defined /// 1 - municipal; /// 2 - administrative/territorial 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 ]; } 

Da lista inteira de arquivos XML no FIAS, usaremos o arquivo apenas com os nomes de regiões, cidades e ruas. Quando eu estava me preparando para a publicação, tive este:

 AS_ADDROBJ_20190106_90809714-fe22-45b2-929c-52bd950963e0.XML 


Vamos preparar um pouco de pimenta recheada da FIAS. Esta é apenas a preparação para um grande futuro pela frente. Primeiro, obteremos o conjunto mínimo inicial. Estes são os únicos ingredientes que precisaremos:

 Class FIAS.AddressObject Extends (%Persistent, %XML.Adaptor) [ ProcedureBlock ] { Parameter XMLNAME = "Object"; Parameter XMLSEQUENCE = 1; /// Global unique identifier of the address object Property AOGUID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "AOGUID", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Official name Property OFFNAME As %String(MAXLEN = 120, MINLEN = 1, XMLNAME = "OFFNAME", XMLPROJECTION = "ATTRIBUTE"); /// Postal code Property POSTALCODE As %String(MAXLEN = 6, MINLEN = 6, XMLNAME = "POSTALCODE", XMLPROJECTION = "ATTRIBUTE"); /// Short name of object type Property SHORTNAME As %String(MAXLEN = 10, MINLEN = 1, XMLNAME = "SHORTNAME", XMLPROJECTION = "ATTRIBUTE") [ Required ]; /// Address object level Property AOLEVEL As %Integer(XMLNAME = "AOLEVEL", XMLPROJECTION = "ATTRIBUTE", XMLTotalDigits = 10) [ Required ]; /// Object identifier of the parent object Property PARENTGUID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "PARENTGUID", XMLPROJECTION = "ATTRIBUTE"); /// Unique record identifier. Key field. Property AOID As %String(MAXLEN = 36, MINLEN = 36, XMLNAME = "AOID", XMLPROJECTION = "ATTRIBUTE") [ Required ]; } 

Criaremos um objeto que entende XML como idioma nativo usando uma classe da biblioteca de sistema% XML.Reader:

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

Vamos dar instruções sobre quem tomar e dizer para ignorar o resto. Tomaremos uma única porção para teste.

Próximo ... Correlacionar / Ler, etc. ...

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

Então a próxima parte é fácil: nós apenas lemos e salvamos.

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

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

É ainda mais cansativo do que antes. É por isso que adicionaremos nosso método de classe FIAS.AddressObject para importação, com base nos mesmos comandos:

 ClassMethod Import() { // Create object to read XML Set reader = ##class(%XML.Reader).%New() // Get source XML for parsing Set status = reader.OpenURL("http://localhost/AS_ADDROBJ_20190106_90809714-fe22-45b2-929c-52bd950963e0.XML") If $$$ISERR(status) {Do $System.Status.DisplayError(status)} // Join object with the right sample structure Do reader.Correlate("Object","FIAS.AddressObject") // Read and save the object in storage While (reader.Next(.object,.status)) { Set status = object.%Save() If $$$ISERR(status) {do $System.Status.DisplayError(status)} } // If an error occurs during parsing, display a message If $$$ISERR(status) {Do $System.Status.DisplayError(status) } 

Usaremos o poder do exocórtex do computador com apenas um comando no terminal :

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

imagem

O jantar está pronto, pessoal. Era mBD, e agora é um prato pronto, um global com os nomes verificados das cidades russas.

imagem

E, finalmente, algumas palavras sobre o que fazer quando 4 TB não é suficiente. Nesse caso, usamos os fluxos . Tudo está descrito na documentação. Você pode usar binário ou caracteres. Armazenar em um global também é possível. Aqui está a receita: pegue o fluxo, corte-o em pedaços e atribua-o aos objetos de que precisamos para consumo.

Não havia espaço suficiente aqui para saber mais sobre os encantadores objetos ObjectScript de endereço e a API Python. Essa será outra história.
Boas notícias: o Gartner acaba de concluir sua coleção anual de avaliações e comentários reais de usuários na categoria DBMS e usou essas informações para publicar sua classificação dos melhores DBMSs de 2019. O InterSystems Caché e o InterSystems IRIS Data Platform receberam a classificação mais alta por "Clientes 'Escolha. " Você pode verificar quais produtos foram considerados e como foram classificados.
Melhor software de sistemas operacionais de gerenciamento de banco de dados de 2019, conforme revisto pelos clientes.

imagem

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


All Articles