¿Cómo escribir la dirección de la casa correctamente?

Cómo Servicio de Impuestos, OpenStreetMap e InterSystems IRIS
podría ayudar a los desarrolladores a obtener direcciones limpias


imagen
Pieter Brueghel el Joven, pagando el impuesto (El recaudador de impuestos), 1640

En mi artículo anterior , acabamos de rozar la superficie de los objetos. Continuemos nuestro reconocimiento. El tema de hoy es difícil. No son GRANDES DATOS, pero aún así no es fácil trabajar con los datos: estamos hablando de cantidades bastante grandes de datos. No encajará todo en la memoria RAM a la vez, y algunos de ellos ni siquiera encajarán en la unidad (no por falta de espacio, sino porque hay mucha basura). El nombre de nuestro sujeto es FIAS DB : la base de datos del Sistema de Direcciones de Información Federal, las bases de datos de direcciones en Rusia. El archivo es de 5,5 GB. Y es un archivo XML comprimido. Después de la extracción, será un total de 53 GB (reserve 110 GB para la extracción). Y cuando comienzas a analizarlo y convertirlo, esos 110 GB no serán suficientes. Tampoco habrá suficiente RAM.

Eso estaría bien, pero puedes seguir cavando. Ya existe un proyecto internacional de código abierto para recopilar y sistematizar datos de direcciones: OpenAddresses . Sus bases de datos serán aún más grandes. Actualmente hay muchos puntos en blanco en sus mapas. Rusia, por ejemplo, está casi vacía. El tamaño de este archivo es de 10 GB.

Pero lo primero es lo primero, consideramos la base de datos del proyecto bastante conocido OpenStreetMaps . Está construido por voluntarios, siguiendo el ejemplo de Wikipedia. Es bastante completo y multilingüe. Y el proyecto acaba de recibir el premio The Free Software Foundation 2018 . En este momento, el tamaño de todo el archivo comprimido como archivo XML comprimido es de 74 GB.

Hablando de direcciones, he recibido noticias inesperadas de DuckDuckGo , el mejor motor de búsqueda seguro hasta la fecha anunció su traslado a Apple Maps. Más precisamente, a Apple MapKit JS. Lo más interesante de esto para nuestros propósitos es la "búsqueda mejorada de direcciones". ¿Apple es mejor que todos los demás para recopilar y proteger meticulosamente nuestros datos? Tendremos que estar atentos ...

Entonces, aquí está el desafío. ¿Cómo podemos poner todos estos datos de direcciones en un repositorio fácil de usar, hacer posible imaginar una API fácil (en Python, por supuesto) y evitar que nuestro querido hardware se derrumbe bajo esta carga masiva? Llamemos a esto MicroBigData, mD o µBD para abreviar.

Casi todos los desarrolladores han escuchado el siguiente comentario. Un directorio de direcciones es un directorio de nombres de lugares, que es algo muy útil. No sé exactamente cuántos datos de la dirección de sus proyectos están actualizados. Después de todo, hay tantas regiones, ciudades y calles. Pero parece que son necesarios para cualquier proyecto que involucre personas. Hay direcciones donde puede encontrar a alguien o enviarle un paquete. Luego está la información necesaria para los pasaportes y otros documentos. Y quizás haya una dirección de alguna oficina o punto de referencia que alguien le haya recomendado. Entonces, ¿qué deberías hacer? ¿Dónde deberías conseguirlo?

Sin tener en cuenta los errores y los duplicados, la solución más simple involucra objetos primitivos que contienen literales de cadena simples (o constantes de cadena). Permita que los usuarios hagan entradas adicionales. Y los objetos saben cómo salvarse, como hemos cubierto antes .

Tome los objetos descritos en la clase a continuación como ejemplo. Un caso de libro de texto , aunque en forma de una dirección de EE. UU., Pero con un ajuste para nuestro conjunto de datos ruso: código postal en lugar de ZIP. También cambiaría el código postal a un número, pero lo dejaré como una cadena para mantener las cosas uniformes. Cualquiera que haya reconocido el lenguaje (ObjectScript) de inmediato recibirá un "me gusta" complementario.

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

Por supuesto, muchas personas llorarán mal, diciendo que todos los literales sobresalen del objeto. ¿Quién ha oído hablar de un objeto que transmite públicamente sus campos? Dejémoslo así por ahora. Es un ejemplo bastante elocuente, y cualquier estudiante podría entenderlo.

En realidad, eso es todo lo que necesitamos. Completamos los campos. Los almacené. Se los entregó a otros objetos. Alguien más los heredará. Todo funciona ¡Y está almacenado!

Pero tengo que decir algunas palabras sobre por qué no debería hacerse de esta manera. ¿Cuál es nuestra dirección de objeto? ¿Por qué no puede ser solo un grupo de cadenas de texto? Las objeciones más obvias que surgen provienen del contexto: ¿quién está usando esta dirección, en qué forma la están usando y con qué propósito? Intente poner a un lado la lógica de su programador e imagine cómo piensa un "turista extranjero", "historiador", "recaudador de impuestos" o "abogado".

Supongo que inmediatamente surgió una serie de preguntas adicionales: qué idioma y codificación usar, qué período de tiempo considerar y qué tipo de documentos están involucrados en esta operación: ¿legal o postal? Y una ciudad: ¿es esa una localidad con nombre, o qué? Incluso una calle podría ser un bulevar, un carril, una avenida o algo más. ¿Cómo deben manejarse todos estos detalles importantes?

Veamos un ejemplo de la vida real. Google ahora está dirigido por Sundar Pichai. El es de la India. Nació en la ciudad de Chennai. ¿O es Madras? En 1996, los residentes decidieron que el nombre de la ciudad sonaba demasiado portugués y cambiaron el nombre de la capital del estado de Tamil Nadu de Madras a Chennai. Entonces, ¿qué deberían ingresar Sundar y sus 72 millones de compatriotas en sus documentos electrónicos?

De hecho, hay toda una ciencia que estudia esto: la toponimia aplicada .
Entonces, hay algunas preguntas de seguimiento. ¿Cómo deben manejarse la hora y la fecha ? ¿Qué pasa con lo obvio, el dinero ? O coordenadas geográficas? ¿Y cómo debe implementar esto en su código? ¿Y podrá transferirlo a su DBMS de elección sin reducir la capa de abstracción? ¿Cómo evita la espiral descendente hacia tipos atómicos de datos de máquinas y pensamientos constantes sobre su reconstrucción? En este caso, vale la pena buscar la fuente de una API primitiva o, por el contrario, de buena calidad. Piensa en esto a tu antojo.

En resumen, el contexto es lo más importante. Y el modelo de objetos nos permite utilizarlo directamente encapsulando "datos de máquina" e implementando un comportamiento "real" dependiente del contexto. No es que las tuplas de bajo nivel estén organizadas en tablas ;-)

Mientras tanto, volveremos a la implementación "primitiva" y haremos las cosas más difíciles para nosotros. Para comenzar, eliminaremos errores y duplicados. En otras palabras, buscaremos una forma de escribir direcciones correctamente la primera vez. Al mismo tiempo, ayudaremos a los desarrolladores de IU a proporcionar sugerencias para los usuarios cuando ingresen datos en los campos.
Cuando hay dos cosas en un solo lugar: textos y la plataforma de datos IRIS de InterSystems, un desarrollador tiene una oportunidad real de cambiar las cosas sin alejarse de la máquina. Mediante el uso de los componentes de objetos integrados iKnow e iFind , por ejemplo. Estos componentes están diseñados para trabajar con datos no estructurados y búsqueda de texto completo , respectivamente.
Intentemos encontrar el esquema de datos para OpenStreetMap. No es tan fácil como podría parecer al principio. No sé la razón exacta, pero no hay un esquema de datos para OSM . ¡Y nos ayudaría mucho, como se verá a continuación! Y eso no volvería a inventar la rueda, use un XSD adecuado , que encontré para usted. Y gracias, Oliver Schrenk. Aquí hay más fotos . Debo decir que para nuestros propósitos es adecuado y corresponde a la estructura interna de los archivos XML y las descargas de OSM. Por qué es importante, pero la primera línea del archivo XSD debe comenzar con "<? Xml ...".

Los elementos son los componentes básicos del modelo de datos conceptuales del mundo físico de OpenStreetMap. Consisten en

  • nodos - definiendo puntos en el espacio,
  • formas: definir características lineales y límites de área, y
  • relaciones, que a veces se utilizan para explicar cómo otros elementos trabajan juntos.

Todo lo anterior puede tener una o más etiquetas asociadas (que describen el significado de un elemento en particular). Una etiqueta consta de dos elementos, una clave y un valor. Las etiquetas describen características específicas de los elementos del mapa: nodos, formas o relaciones.

¿Dónde están las calles y las ciudades? Es un gran secreto! La geometría fue bien enseñada? Más sobre esto la próxima vez. :)

Además, utilizaremos el asistente de esquema XSD, creado gentilmente por los desarrolladores de IRIS, para la clase% XML apropiada. Campos del adaptador. El signo de porcentaje al principio solo significa que esta es una clase de la biblioteca del sistema. Se puede encontrar más información al respecto en la documentación . Realizaremos las operaciones en la terminal.

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

Puede obtener lo mismo de Atelier IDE (en el menú, vaya a Herramientas> Complementos> Asistente de esquema XML):

imagen

Como utilizamos el asistente indicando el nombre del paquete resultante y no los parámetros, terminaron en el paquete de prueba. Como puede ver en el segundo comando, pasé el archivo de esquema a mi servidor Python local:

 python3 -m http.server 80 

Puede usar cualquier otro servidor http que desee. O cargue el archivo en su servidor IRIS y apúntelo.

Como resultado, tenemos ocho clases que reflejan completamente la estructura de nuestra dirección XML. Esta es la clase 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 ]; } 

Y 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 puede ver, algunas de las opciones que ya he desactivado son innecesarias para nuestra solución.

El tamaño del archivo XML solo para Rusia es de aproximadamente 53 GB. No puede abrirlo con las herramientas habituales de procesamiento de texto: no pueden soportar archivos tan grandes. Puede tomar muestras más pequeñas para hacer ejercicio, por ejemplo, las direcciones de Rusia están disponibles para los territorios individuales . El pequeño volumen de la región de Kaliningrado en formato comprimido será de 18 MB, el archivo XML sin comprimir es de 203 MB.
Por cierto, la longitud máxima de un literal de cadena en InterSystems IRIS es 3.641.144 caracteres. En otras palabras, cargar un archivo o URL directamente en él no funcionará. Puede ver los otros límites en la documentación . Para trabajar con grandes cantidades de datos, puede usar flujos de datos que no tengan restricciones de longitud.
Veamos qué obtenemos para el conjunto de nodos.

A continuación, haremos las cosas según el libro . Crearemos un objeto que entienda XML como lenguaje nativo usando una clase de la biblioteca del sistema% XML.Reader:

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

Le daremos instrucciones sobre qué llevar e ignoraremos el resto. Tomaremos una sola clase:

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

Después de eso, hay varias formas de obtener el archivo mBD original. Si es conveniente, puede colocarlo junto al repositorio de almacenamiento, localmente en el sistema de archivos del servidor IRIS. O, como en mi ejemplo, solicite que se envíe a través de HTTP. También hay una opción más universal, de la que hablaré un poco más abajo.

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

Importante! En este punto, la mayoría de las personas que prueben este ejemplo por sí mismas encontrarán algo horrible. En lugar de un feliz "1" (todo está bien), el sistema devolverá algo que comienza con "0, STORE ..." Y eso será decepcionante. En otras palabras, el archivo con lo que parece ser mBD resultó no ser tan micro y no se ajusta a nuestro objeto. No había suficiente memoria asignada. ¿Se puede arreglar esto? Absolutamente La plataforma de datos IRIS le permite crear objetos de hasta 4 TB en RAM. Entonces, ¿qué salió mal? Por defecto, el tamaño de un objeto es de 256 MB en la configuración del sistema. Pero necesitamos mucho más que eso. Y recuerde, estos son requisitos de RAM. ¿Tiene suficiente espacio en su computadora / servidor?
Experimenté para determinar la cantidad de memoria que necesitaremos para alojar a este gigante: casi 170 GB. Esto deberá especificarse en la configuración (Menú> Configurar memoria> Capacidad máxima de memoria por proceso (KB)) o mediante la variable del sistema $ ZSTORAGE (en kilobytes):

 set $ZSTORAGE=170000000 

¿Ejecutó un nuevo proceso con la configuración de memoria correcta? Entonces la siguiente parte es fácil: solo leemos y guardamos.

También hay una opción alternativa (y probablemente preferible): use la propiedad UsePPG Handler de la clase% XML. Reader, que le permite no almacenar el XML en la memoria y funciona con la configuración de memoria estándar.

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

Siguiente ... Correlacionar / Leer, etc. ...

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

Y así sucesivamente, 1.180.849 veces por cada operación :-) Es tedioso. Es por eso que agregaremos nuestro método de clase OSM.map para importar, basado en los mismos 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() } } 

Utilizaremos el poder del exocortex de nuestra computadora con solo un comando en la terminal :

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

Y así, obtenemos los datos de la dirección de forma abierta y quizás no sea una fuente muy confiable. Es hora de pasar por las mismas etapas, pero en los datos en los que se puede confiar. Y también estandarizado, limpio, bien documentado y realizado por el organismo gubernamental adecuado: eso es cosa de leyendas. Para su crédito, el servicio de impuestos ruso está haciendo un buen trabajo con su producto digital. En la medida en que sea posible un buen trabajo. Sin duda, tiene sus defectos, y la limpieza de datos está en curso. En cuanto a cómo podemos resolver esto, dejemos que los líderes del gobierno reflexionen sobre eso. Están tomando decisiones por sí mismos que nos benefician a todos.

Y ahora vamos a lo más incomprensible: le enseñaremos a Address a leer los datos correctos de nuestra fuente. Afortunadamente, el conjunto de datos del servicio de impuestos federales tiene descripciones listas para la estructura del documento XML. De acuerdo con la descripción del sitio web de FIAS que acompaña a los datos, necesitaremos el conjunto de datos ADDROBJ que, en mi caso, corresponde al archivo AS_ADDROBJ_2_250_01_04_01_01.xsd

A continuación, usemos el asistente de esquema XSD. Realizaremos las operaciones en la terminal:

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

Como resultado, tenemos dos clases que reflejan completamente la estructura de nuestra dirección XML:

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 ]; } 

Fuera de la lista completa de archivos XML en FIAS, solo usaremos el archivo con los nombres de regiones, ciudades y calles. Cuando me estaba preparando para la publicación, tenía este:

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


Cocinemos un poco de pimienta rellena de FIAS. Esto es solo preparación para un gran futuro por delante. Primero, obtendremos el conjunto mínimo inicial. Estos son los únicos ingredientes que necesitaremos:

 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 ]; } 

Crearemos un objeto que entienda XML como lenguaje nativo usando una clase de la biblioteca del sistema% XML.Reader:

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

Le daremos instrucciones sobre a quién tomar y le diremos que ignore el resto. Tomaremos una sola porción para la prueba.

Siguiente ... Correlacionar / Leer, etc. ...

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

Entonces la siguiente parte es fácil: solo leemos y guardamos.

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

Y así sucesivamente, 3.722.548 veces por cada operación :-)

Es aún más agotador que antes. Es por eso que agregaremos nuestro método de clase FIAS.AddressObject para importar, basado en los mismos 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) } 

Utilizaremos el poder del exocortex de nuestra computadora con solo un comando en la terminal :

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

imagen

La cena está lista, todos. Era mBD, y ahora es un plato terminado, global con los nombres verificados de las ciudades rusas.

imagen

Y, finalmente, algunas palabras sobre qué hacer cuando 4 TB no son suficientes. En ese caso, usamos las transmisiones . Todo se presenta en la documentación. Puedes usar binarios o caracteres. Almacenar en un global también es posible. Aquí está la receta: tome la corriente, córtela en pedazos y asígnela a los objetos que necesitamos para el consumo.

Aquí no había suficiente espacio para obtener más información sobre los encantadores objetos ObjectScript de direcciones y la API de Python. Esa será otra historia.
Buenas noticias: Gartner acaba de completar su colección anual de valoraciones y comentarios de usuarios reales en la categoría de DBMS y utilizó esta información para publicar su clasificación de los mejores DBMS de 2019. InterSystems Caché e InterSystems IRIS Data Platform recibieron la calificación más alta para "Clientes "Elección". Puede consultar qué productos se consideraron y cómo se calificaron.
El mejor software de sistemas de gestión de bases de datos operacionales de 2019 según lo revisado por los clientes.

imagen

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


All Articles