microBIGDATA o FIAS en tu bolsillo
Peter Brueghel el Joven, pago de impuestos , 1640La última entrada en la afeitadora
en los objetos fue. Continuaremos el reconocimiento en la batalla. Hoy hablaremos de lo difícil. Todavía no se trata de GRANDES DATOS, pero ya es un inconveniente trabajar: grandes cantidades de datos. No todos caben en toda la RAM, pero algunos ni siquiera caben en el disco (no hay suficiente espacio, pero mucha basura). El nombre de nuestra
base de datos FIAS de confianza es la base de datos del sistema de información de direcciones federales. Archivo de 5,5 GB. Y está comprimido en un archivo XML. Después de desempacar, habrá 53 GB completos (almacena 110 GB para desempacar). Y cuando comience a analizarlo y convertirlo, entonces 110 GB no serán suficientes. Acerca de la cantidad requerida de RAM también será.
Todo estaría bien, pero puedes cavar más. Existe un proyecto internacional de sistematización y recopilación de datos de código abierto:
OpenAddresses . Entonces habrá más bases de datos. La cobertura actual del planeta tiene muchos puntos blancos, por ejemplo, Rusia está casi ausente. El tamaño del archivo es de 10 GB.
O una base de datos de un
proyecto OpenStreetMaps bastante conocido. Está construido por voluntarios sobre la base de Wikipedia. Bastante detallado y multilingüe. Ahora un archivo completo de XML comprimido con un tamaño de 74 GB.
Si comenzaron a hablar de direcciones, llegaron noticias inesperadas a tiempo de DuckDuckGo , el mejor motor de búsqueda seguro para hoy, sobre su transición a las tarjetas de Apple. Más precisamente en Apple MapKit JS. La característica más interesante en nuestro contexto es la "búsqueda mejorada de direcciones". ¿Es Apple el mejor que recopila y protege minuciosamente nuestros datos? Será necesario rastrear ...
Entonces el desafío. Cómo poner toda esta riqueza de direcciones en un repositorio agradable para su uso, para permitirle soñar con una API gratuita (en Python, por supuesto) y no dejar que su querido hierro se atragante con una carga no leída. Llamémoslo MicroBigData - mcBD o μBG en inglés :-)
En la economía de cada segundo (o incluso el primero) desarrollador, esto es un directorio de direcciones, también es un directorio de topónimos, algo muy necesario. Y cuando también es normativo, preparado, limpio y bien documentado por el cuerpo correcto, es solo un cuento de hadas. Debemos rendir homenaje, el servicio fiscal ruso está haciendo bien su producción digital. En la medida de lo posible. Probablemente hay algunos defectos en el interior y la limpieza de datos continúa. Cómo resolver este problema, deje que los jefes de estado piensen. Ellos deciden por nosotros mismos y para el beneficio de todos nosotros. Por cierto, se encontró un error tipográfico de FIAS en el siguiente ejemplo. El resultado no se ve afectado. No lo arreglé. Vas a encontrar?
No sé cuán específicos son relevantes los datos de la dirección en sus proyectos: todas estas regiones, ciudades, calles. Pero parece que ni un solo proyecto para personas puede prescindir de ellos. Esa es la dirección donde buscar a una persona o dónde enviarle paquetes. Se deben guardar los detalles del pasaporte o cualquier otro documento. O tal vez es la dirección de la oficina de trabajo o las atracciones que se recomiendan visitar. Y que hacer Donde conseguir
La solución más simple, sin tener en cuenta los errores y los duplicados, son los objetos primitivos que contienen literales de cadena simples (son constantes de cadena, también son cadenas). Permita que los usuarios ingresen las siguientes entradas recibidas en ellos. Y los objetos saben cómo salvarse a sí mismos; ya hemos
pasado esto .
Tales objetos son, por ejemplo, como se describe en la clase a continuación.
Directamente del libro de texto , aunque estadounidense, pero ajustado a nuestra realidad rusa; en lugar de su código postal, estará nuestro código postal. También reemplazaría el código postal con un número, pero en aras de la monotonía, dejo una cadena. Cualquiera que reconozca inmediatamente un lenguaje, y este es ObjectScript, tiene derecho a un me gusta alentador.
Class Soviet.Address Extends %Persistent { Property streetName As %String; Property cityName As %String; Property areaName As %String; Property postalCode As %String; }
Por supuesto, muchos se indignarán, dicen que todo sobresale de los bolsillos del objeto (literales). ¿Dónde se ha visto, para que el objeto brille sus campos públicamente? Dejémoslo así, duele también un ejemplo elocuente y es comprensible para cualquier estudiante.
Esto es en realidad todo lo que se necesita. Lleno en los campos. Poner en almacenamiento. Transferido a otros objetos para el trabajo. Heredado más allá de alguien. Todo funciona Y almacenado!
Pero hay que decir algunas palabras de por qué no vale la pena hacerlo. ¿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 vienen a la mente provienen del contexto: ¿quién usa esta dirección, de qué forma y con qué propósito? Trate de dejar a un lado su pensamiento de programación e imagine cómo piensa un "turista extranjero", "historiador", "inspector de impuestos", "abogado", etc.
Creo que de inmediato surgen una serie de preguntas y aclaraciones adicionales: ¿qué idioma usar, en qué codificación almacenar y dar, en qué época clasificar, qué documentos se aplican, legales o postales? ¿Es una ciudad un asentamiento con nombre o qué? Incluso una calle puede convertirse en un bulevar, una calle lateral, una avenida u otra cosa. ¿Qué hacer con todos estos detalles importantes de implementación?
Toma un ejemplo vivo. Google ahora está dirigido por Sundar Pichai. Él mismo es de la India. Nacido en la ciudad de Chennai (también conocido como Chennai). ¿O en Madras? En 1996, los indios decidieron que el nombre de la ciudad era muy portugués y cambiaron el nombre de la capital de Tamil Nadu de Madras a Chennai. ¿Y qué deberían escribir Sundar y 72 millones de sus compatriotas en sus documentos electrónicos?
En general, toda la ciencia se ocupa de esto: la toponimia aplicada .
Entonces las preguntas ruegan. ¿Cómo lidiar con la
hora y la fecha ? ¿Es tan
obvio el dinero ? ¿Las coordenadas geográficas son tan simples? ¿Y cómo se implementa esto en su código? ¿Se puede transferir al DBMS seleccionado sin disminuir el nivel de abstracción? ¿Cómo no deslizarse en tipos atómicos de datos de máquinas y pensar constantemente en su reconstrucción? Aquí vale la pena buscar la fuente de una API primitiva o, por el contrario, sólida. Piénsalo a tu gusto.
En resumen, el contexto es lo más importante. Y el modelo de objetos nos permite usarlo directamente a través de la encapsulación de "datos de la máquina" y la implementación de un comportamiento "en vivo" sensible al contexto. Para nada las tuplas de bajo nivel presentadas en las tablas ;-)
Mientras tanto, volvamos a la implementación "primitiva" y compliquemos nuestras vidas. Para comenzar, elimine errores y duplicados. Es decir, buscaremos una forma de escribir direcciones de inmediato. Al mismo tiempo, ayudaremos a los desarrolladores de la interfaz de usuario a organizar sugerencias para los usuarios al completar los campos de entrada de datos.
Cuando dos personas se reúnen en un solo lugar: mensajes de texto y la plataforma de datos IRIS de InterSystems, el desarrollador tiene una oportunidad real de desplegarse al máximo sin salir de la máquina. Por ejemplo, usando los componentes de objetos integrados iKnow e iFind . Estos son componentes para trabajar con datos no estructurados y búsqueda de texto completo , respectivamente. El ruso es compatible "fuera de la caja".
Lo primero es lo primero, le enseñaremos a la Dirección a leer los datos necesarios de la fuente original. Afortunadamente, el conjunto de datos del servicio de impuestos federales tiene descripciones preparadas de la estructura de los documentos XML. De acuerdo con la
descripción adjunta a los datos
del sitio web de FIAS , necesitamos el conjunto de datos ADDROBJ, que, en mi caso, corresponde al archivo AS_ADDROBJ_2_250_01_04_01_01.xsd
A continuación, utilizaremos el convertidor de sistema de la plantilla XSD a la estructura de campo correspondiente de la clase% XML.Adaptor, amablemente preparada por los desarrolladores de IRIS. El signo de porcentaje al principio solo significa que esta es una clase de la biblioteca del sistema.
Los detalles de uso se encuentran en la documentación . Realizaremos operaciones en la terminal.
set xmlScheme = ##class(%XML.Utils.SchemaReader).%New() do xmlScheme.Process("http://localhost/AS_ADDROBJ_2_250_01_04_01_01.xsd")
Lo mismo se puede obtener en el
Atelier IDE (en el menú Herramientas> Complementos> Asistente de esquema XML) o mediante solicitudes similares a objetos directamente desde el código del programa.

Como utilizamos el constructor sin especificar los parámetros, es decir, el nombre del paquete para colocar las clases resultantes, terminaron en el paquete Prueba. Como puede ver en el segundo comando, le di el archivo de esquema a través de mi servidor web local en Python:
python3 -m http.server 80
Puede usar cualquier otro servidor http que desee. O suba el archivo a su servidor IRIS e indique la ruta directa al mismo.
Como resultado, tenemos dos clases que reflejan completamente la estructura de nuestro XML direccionable:
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 la lista completa de archivos xml en FIAS, utilizaremos solo un archivo con los nombres de regiones, ciudades y calles. Al momento de preparar la publicación, tenía esto:
AS_ADDROBJ_20190106_90809714-fe22-45b2-929c-52bd950963e0.XML
El tamaño del archivo no es grande ni pequeño, sino casi 3 GB. No lo abrirá con herramientas de texto normales: no resumen este tamaño.
Por cierto, la longitud máxima de un literal de cadena (tipo de cadena) en InterSystems IRIS no es más de 3,641,144 caracteres. Es decir, descargar el archivo directamente o la URL en él fallará. Otras restricciones se pueden encontrar en la documentación . Para trabajar con grandes volúmenes de datos, puede usar flujos de datos (flujos) que no tengan dicho límite de longitud.
¿A ver qué tenemos?
Cocinar pimientos rellenos FIAS. Esto es solo una preparación para un futuro maravilloso. Primero obtenemos el conjunto mínimo inicial. Solo necesitamos estos 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 ];
Luego,
escribe . Creamos un objeto que entiende XML como nativo: utilizamos una clase de la biblioteca del sistema% XML.Reader:
set reader = ##class(%XML.Reader).%New()
Y le damos instrucciones, a quién tomar, e ignoramos el resto. Tomaremos una porción:
do reader.Correlate("Object","FIAS.AddressObject")
Luego hay variaciones sobre cómo obtener el archivo microbd original. Si es conveniente, puede colocarlo junto al repositorio, localmente en el sistema de archivos del servidor IRIS. O, como en mi ejemplo, pida enviar a través de HTTP. Hay una opción aún más universal, sobre la cual habrá un par de palabras a continuación.
set url="http://localhost/AS_ADDROBJ_20190106_90809714-fe22-45b2-929c-52bd950963e0.XML" write reader.OpenUrl(url)
Importante! En este momento, la mayoría que pasará este ejemplo sobre sí mismos tendrá algo terrible. El sistema regresará en lugar del alegre "1" (todo está en orden), algo que comienza con "0 ¸ STORE ...". Y no lo hará por favor. Es decir, el archivo con una microbase aparentemente no es del todo micro y no cabe en nuestro objeto. La memoria asignada para él no fue suficiente. Soluble? Por supuesto La plataforma de datos IRIS le permite crear objetos de hasta 4 TB en RAM. Entonces, ¿qué salió mal? De manera predeterminada, la configuración del sistema está establecida en 256 MB por objeto. Y necesitaríamos mucho más. Y recuerde, estos son requisitos de RAM. ¿Hay suficiente stock en su computadora / servidor?
¿Qué tamaño de memoria necesitamos para instalar este gigante que hemos instalado empíricamente? Casi 10 GB. Lo que necesita especificar en la configuración (Menú> Configurar memoria> Memoria máxima por proceso (KB)) o mediante la
variable del sistema $ ZSTORAGE (en kilobytes):
set $ZSTORAGE=10000000
¿Lanzó un nuevo proceso con la configuración de memoria necesaria? Entonces todo es más simple: leemos y guardamos.
Hay una opción alternativa (y probablemente preferida):
use la propiedad UsePPGHandler de la clase% XML.Reader que le permite no almacenar XML en la memoria y funciona con configuraciones de memoria estándar.
set reader = ##class(%XML.Reader).%New() set reader.UsePPGHandler = 1
más ... Correlacionar / Leer, etc. ...
do reader.Next(.object) do object.%Save()
Y así 3.722.548 veces por cada operación :-)
Esto es agotador Por lo tanto, complementamos nuestra clase FIAS.AddressObject con un método para importar, basado en los comandos que se acaban de mostrar:
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)} }
Usemos el poder de nuestra computadora exocortex, solo un comando
en la terminal :
do ##class(FIAS.AddressObject).Import()

Pido a todos a la mesa. Hubo MCD, y ahora el plato terminado en forma de global con los nombres verificados de ciudades y pesos rusos está listo.

Y finalmente un par de palabras sobre cuándo 4 TB no es suficiente. En este caso,
seguimos las transmisiones (o transmisiones si lo desea). La documentación se presenta en estanterías. Puedes binario, puedes simbólico. La tienda en el mundo tampoco está prohibida. La receta es esta: tomamos una corriente, la cortamos en partes y se la damos a los objetos que necesitamos para el consumo.
Además, los hermosos objetos ObjectScript y API direccionables en Python no encajaban. Habrá una historia separada.
Agradable: Gartner acaba de completar la colección anual de valoraciones y reseñas de usuarios reales en la categoría DBMS y, sobre esta base, ha publicado su calificación del mejor DBMS de 2019. Los productos InterSystems Caché e InterSystems IRIS Data Platform han recibido la calificación más alta de elección del consumidor. De quién eligió y cómo calificó, puede echar un vistazo usted mismo .
El mejor software de sistemas operativos de gestión de bases de datos de 2019 según lo revisado por los clientes
