#Modelo de datos de objetos

0 Seguidores · 35 Publicaciones

Un modelo de datos orientado a objetos es aquel en que los datos o el código se componen por módulos, los cuales combinan datos y procedimientos que funcionan con dichos datos.

Obtener más información.

Artículo Jose-Tomas Salvador · sep 11, 2025 1m read

Una ventaja de usar Doxygenerate es que Doxygen hace más que generar salida en HTML. Ajustad el Doxyfile que le indica a Doxygen qué hacer y podréis crear fácilmente un PDF. Nuestro ejemplo de la aplicación MARINA produjo un PDF de 524 páginas. Así se ve la página 94:

0
0 27
Artículo Jose-Tomas Salvador · ene 7, 2025 4m read

Recientemente me encontré con un problema muy antiguo relacionado con el mantenimiento de código:

Tenéis acceso a vuestro servidor IRIS solo a través de ODBC/JDBC:

  • Sin acceso a VSCode
  • Sin acceso a Studio
  • Sin acceso a (Web-)Terminal

¡Pero necesitáis revisar Clases, Rutinas o Globals!

De cualquier forma, SQL es vuestro aliado.

  • Primero, necesitáis una tabla sencilla como almacén temporal de texto.
CREATEGLOBALTEMPORARYTABLE  arcc.txt (line VARCHAR(32000))
  • A continuación, exportad vuestro código a un archivo local e importadlo a vuestra tabla.
0
0 69
Artículo Iryna Mykhailova · jun 1, 2024 3m read

​El almacenamiento en columnas es una de las nuevas ofertas de InterSystems IRIS. A diferencia del almacenamiento tradicional basado en filas, optimiza el procesamiento de consultas al almacenar datos en columnas en lugar de filas, lo que permite un acceso y una recuperación más rápidos de información relevante.

Se han escrito un par de artículos sobre cuándo se debe utilizar para darle el mayor impulso a un sistema, y cómo crear tablas así usando SQL.

CREATETABLE tabla (columna1 tipo1, columna2 tipo2, columna3 tipo3) WITH STORAGETYPE = COLUMNAR -- ex 1CREATETABLE tabla (columna1 tipo1, columna2 tipo2, columna3 tipo3 WITH STORAGETYPE = COLUMNAR) -- ex 2

e incluso las pruebas de rendimiento.

Como todos sabemos, InterSystems IRIS es un DBMS multimodelo y brinda acceso perfecto a los mismos datos mediante acceso relacional y de objetos. Lo primero se trata en otros artículos, pero ¿qué pasa con lo segundo?

0
0 175
Artículo Luis Angel Pérez Ramos · dic 5, 2023 3m read

Preguntas frecuentes de InterSystems

Las definiciones de clases creadas por los usuarios se almacenan en clases de definición de clases. Estas se pueden utilizar para obtener una lista de definiciones de clases desde un programa.

Nota: Las clases de definición de clase se refieren a todas las clases contenidas en el paquete %Dictionary.

En el siguiente código de muestra, se obtiene una lista de definiciones de clases utilizando la consulta Resumen de la clase %Dictionary.ClassDefinitionQuery.

0
0 102
Artículo Ricardo Paiva · nov 8, 2023 2m read

Preguntas frecuentes de InterSystems

La información sobre las propiedades definidas en una clase se puede obtener usando las siguientes clases del sistema:

%Dictionary.ClassDefinetion

%Dictionary.PropertyDefinition

El ejemplo de descripción del código es el siguiente.

0
0 134
Artículo Luis Angel Pérez Ramos · sep 8, 2023 1m read

InterSystems FAQ

Puedes evitar este error especificando un objeto de tipo stream como argumento de la función %ToJSON usando durante la generación del string JSON desde el objeto dinámico.

A continuación podéis ver un ejemplo.

USER>set temp=##class(%Stream.TmpCharacter).%New()

USER>set jsonobj={}

USER>set jsonobj.pro1=["a","b","c","d"]

USER>set jsonobj.pro2=["あ","い","う","え"]

USER>do jsonobj.%ToJSON(temp)

USER>write temp.Size
51
USER>write temp.Read()
{"pro1":["a","b","c","d"],"pro2":["あ","い","う","え"]}

Podéis consultar la documentación si queréis más detalles al respecto.

0
0 137
Artículo Ricardo Paiva · sep 5, 2023 1m read

Preguntas frecuentes de InterSystems

De cara a la reconstrucción de los índices de una clase persistente/tabla, hay que utilizar el método %BuildIndices(), proporcionado por dicha clase. Para ello hay que especificar los valores de inicio y fin de los ID para los cuales se quieren reconstruir índices en los argumentos del método. 

Por ejemplo, para reconstruir el índice NameIDX y el índice ZipCode en la clase Sample.Person sólo para ID=10 a 20, ejecutad el siguiente código (el rango de ID se especifica en los argumentos quinto y sexto).

0
0 107
Artículo Ricardo Paiva · ago 16, 2023 3m read

InterSystems IRIS actualmente limita sus clases a 999 propiedades.

Pero, ¿qué hacer si necesita almacenar más datos por objeto?

Este artículo respondería a esta pregunta (con el apunte adicional de Community Python Gateway y cómo transferir conjuntos de datos amplios a Python).

En realidad, la respuesta es muy simple: InterSystems IRIS actualmente limita las clases a 999 propiedades, pero no a 999 primitivas. La propiedad en InterSystems IRIS puede ser un objeto con 999 propiedades y así sucesivamente; el límite se puede ignorar fácilmente.

0
0 116
Artículo Ricardo Paiva · jul 25, 2023 9m read

Antes de empezar a hablar de bases de datos y de los distintos modelos de datos que existen, primero explicaré qué es una base de datos y cómo se utiliza.

Una base de datos es una colección organizada de datos, almacenados y accesibles de forma electrónica. Se utiliza para almacenar y recuperar datos estructurados, semiestructurados o sin procesar, que normalmente están relacionados con un tema o una actividad.

En el corazón de toda base de datos hay al menos un modelo utilizado para describir sus datos. Y según el modelo que utilice, una base de datos puede tener características ligeramente diferentes y almacenar distintos tipos de datos.

Para escribir, recuperar, modificar, ordenar, transformar o imprimir la información de la base de datos, se utiliza un software llamado Sistema de Gestión de Bases de Datos (DBMS, por sus siglas en inglés).

El tamaño, la capacidad y el rendimiento de las bases de datos y sus respectivos DBMS ha aumentado de forma significativa. Esto ha sido posible gracias a los avances tecnológicos en varios ámbitos, como los procesadores, la memoria y almacenamiento de los ordenadores y las redes informáticas. En general, el desarrollo de la tecnología de bases de datos puede dividirse en cuatro generaciones basadas en los modelos o la estructura de los datos: navegacional, relacional, de objetos y post-relacionales.

A diferencia de las tres primeras generaciones, que se caracterizan por un modelo de datos específico, la cuarta generación incluye muchas bases de datos diferentes basadas en distintos modelos, como columnas, gráficos, documentos, componentes, multidimensiones, clave-valor, almacenamiento en memoria, etc. Todas estas bases de datos están unidas por un único nombre NoSQL (NoSQL o ahora es más preciso decir No sólo SQL).

Además, ahora aparece una nueva clase, que se llama NewSQL. Se trata de bases de datos relacionales modernas que tienen como objetivo ofrecer el mismo rendimiento escalable que los sistemas NoSQL para cargas de trabajo de procesamiento de transacciones en línea (lectura-escritura), a la vez que utilizan SQL y mantienen ACID.

Por casualidad, entre estas bases de datos de cuarta generación se encuentran las que admiten los modelos de datos múltiples mencionados anteriormente. Se llaman bases de datos multimodelo. Un buen ejemplo de este tipo de base de datos es InterSystems IRIS. Por ello, la utilizaré para dar ejemplos de distintos tipos de modelos, según vayan presentándose.

<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" src="https://www.youtube.com/embed/EAOfw09xTrM" title="YouTube video player" width="560" height="315" frameborder="0"></iframe>

La primera generación de bases de datos utilizaba modelos jerárquicos o modelos en red. El núcleo de los primeros es una estructura en forma de árbol en la que cada registro tiene un solo propietario. Podéis ver cómo funciona utilizando el ejemplo de InterSystems IRIS, porque su modelo principal es jerárquico y todos los datos se almacenan en globals (que son árboles B). Podéis leer más sobre los globals aquí.

Podemos crear este árbol en IRIS:

Set^a("+7926X", "city") = "Moscow"Set^a("+7926X", "city", "street") = "Req Square"Set^a("+7926X", "age") = 25Set^a("+7916Y", "city") = "London"Set^a("+7916Y", "city", "street") = "Baker Street"Set^a("+7916Y", "age") = 36

Y verlo en la base de datos:

Después, Edgar F. Codd propuso su álgebra relacional y su teoría del almacenamiento de datos, basada en principios relacionales, en 1969. Y tras ello se crearon las bases de datos relacionales. El uso de relaciones (tablas), atributos (columnas), tuplas (filas) y, lo que es más importante, transacciones y requisitos ACID, hizo que estas bases de datos fueran muy populares y lo siguen siendo ahora.

Por ejemplo, tenemos el siguiente esquema:

Podemos crear y completar tablas:

Y si escribimos la consulta:

select p.ID, p.Name, a.Country, A.City
  from My.Person p left join My.Address a
    on p.Address = a.ID

recibiremos la respuesta:

A pesar de las significativas ventajas de las bases de datos relacionales, con la difusión de los lenguajes de objetos se hizo necesario almacenar datos orientados a objetos en bases de datos. Por eso, en los años 90 aparecieron las primeras bases de datos orientadas a objetos y objeto-relacionales. Estas últimas se crearon a partir de bases de datos relacionales y añadiéndole complementos para simular el trabajo con objetos. Las primeras se desarrollaron desde cero a partir de las recomendaciones del consorcio OMG (Object Management Group) y después del ODMG (Object Data Management Group).

Las ideas clave de estas bases de datos orientadas a objetos son las siguientes.

El almacén de datos único es accesible usando:

  • object definition language - schema definition, permite definir clases, sus atributos, relaciones y métodos
  • object-query language - declarative, es un lenguaje similar a SQL, que permite obtener objetos de la base de datos
  • object manipulation language - permite modificar y guardar datos en la base de datos, admite transacciones y la invocación de métodos.

Este modelo permite obtener datos de bases de datos mediante lenguajes orientados a objetos.

Si tomamos la misma estructura que en el ejemplo anterior, pero en la forma orientada a objetos, tendremos las siguientes clases:

Class My.Person Extends%Persistent
{
Property Name As%Name;Property Address As My.Address;
}
Class My.Address Extends%Persistent
{
Property Country;Property City;
}

Y podemos crear los objetos utilizando el lenguaje orientado a objetos:

 set address = ##class(My.Address).%New()
 set address.Country = "France"
 set address.City = "Marseille"
 do address.%Save()
 set person = ##class(My.Person).%New()
 set person.Address = address
 set person.Name = "Quouinaut, Martin"
 do person.%Save()

Desafortunadamente, las bases de datos de objetos no lograron competir con las bases de datos relacionales a pesar de su posición dominante, y como resultado, surgieron muchos ORM.

En cualquier caso, con la expansión de Internet en la década de los 2000 y la aparición de nuevos requisitos para el almacenamiento de datos, empezaron a surgir otros modelos de datos y DBMS. Dos de estos modelos que se utilizan en IRIS son los de documentos y columnas.

Las bases de datos orientadas a documentos se utilizan para gestionar datos semiestructurados. Se trata de datos que no siguen una estructura fija y llevan la estructura dentro. Cada unidad de información en una base de datos de este tipo es un par simple: una clave y un documento específico. Este documento normalmente tiene un formato JSON y contiene la información. Como la base de datos no requiere un esquema particular, también es posible integrar distintos tipos de documentos en el mismo almacén.

Si tomamos el ejemplo anterior, podemos tener documentos como estos:

{
   "Name":"Quouinaut, Martin",
   "Address":{
      "Country":"France",
      "City":"Paris"
   }
}
{
   "Name":"Merlingue, Luke",
   "Address":{
      "Country":"France",
      "City":"Nancy"
   },
   "Age":26
}

Estos dos documentos con un distinto número de campos se almacenan en la base de datos de IRIS sin ningún problema.

Y el último ejemplo de un modelo que estará disponible en la versión 2022.2 es el modelo de columnas. En este caso, el DBMS almacena las tablas de datos por columnas y no por filas.

La orientación por columnas permite un acceso más eficiente a los datos para consultar un subconjunto de columnas (eliminando la necesidad de leer columnas que no son relevantes), y más opciones para comprimir datos. Asimismo, comprimir por columnas es más eficaz cuando los datos de la columna son similares. No obstante, generalmente son menos eficaces para insertar nuevos datos.

Podéis crear esta tabla:

Create Table My.Address (
  city varchar(50),
  zip varchar(5),
  country varchar(15)
) WITH STORAGETYPE = COLUMNAR

En este caso, la clase es la siguiente:

 
Spoiler
 
Class My.Address Extends%Persistent [ ClassType = persistent, DdlAllowed, Final, Owner = {UnknownUser}, ProcedureBlock, SqlRowIdPrivate, SqlTableName = Address ]
{
Property city As%Library.String(COLLATION = "EXACT", MAXLEN = 50) [ SqlColumnNumber = 2 ];Property zip As%Library.String(COLLATION = "EXACT", MAXLEN = 5) [ SqlColumnNumber = 3 ];Property country As%Library.String(COLLATION = "EXACT", MAXLEN = 15) [ SqlColumnNumber = 4 ];Parameter STORAGEDEFAULT = "columnar";Parameter USEEXTENTSET = 1;/// Bitmap Extent Index auto-generated by DDL CREATE TABLE statement.  Do not edit the SqlName of this index.
Index DDLBEIndex [ Extent, SqlName = "%%DDLBEIndex", Type = bitmap ];
Storage Default
{
<Data name="_CDM_city">
<Attribute>city</Attribute>
<ColumnarGlobal>^q3AW.DZLd.1.V1</ColumnarGlobal>
<Structure>vector</Structure>
</Data>
<Data name="_CDM_country">
<Attribute>country</Attribute>
<ColumnarGlobal>^q3AW.DZLd.1.V2</ColumnarGlobal>
<Structure>vector</Structure>
</Data>
<Data name="_CDM_zip">
<Attribute>zip</Attribute>
<ColumnarGlobal>^q3AW.DZLd.1.V3</ColumnarGlobal>
<Structure>vector</Structure>
</Data>
<DataLocation>^q3AW.DZLd.1</DataLocation>
<ExtentLocation>^q3AW.DZLd</ExtentLocation>
<ExtentSize>3</ExtentSize>
<IdFunction>sequence</IdFunction>
<IdLocation>^q3AW.DZLd.1</IdLocation>
<Index name="DDLBEIndex">
<Location>^q3AW.DZLd.2</Location>
</Index>
<Index name="IDKEY">
<Location>^q3AW.DZLd.1</Location>
</Index>
<IndexLocation>^q3AW.DZLd.I</IndexLocation>
<Property name="%%ID">
<AverageFieldSize>3</AverageFieldSize>
<Selectivity>1</Selectivity>
</Property>
<Property name="city">
<AverageFieldSize>7</AverageFieldSize>
<Selectivity>33.3333%</Selectivity>
</Property>
<Property name="country">
<AverageFieldSize>7</AverageFieldSize>
<Selectivity>33.3333%</Selectivity>
</Property>
<Property name="zip">
<AverageFieldSize>7</AverageFieldSize>
<Selectivity>33.3333%</Selectivity>
</Property>
<SQLMap name="%%DDLBEIndex">
<BlockCount>-4</BlockCount>
</SQLMap>
<SQLMap name="IDKEY">
<BlockCount>-4</BlockCount>
</SQLMap>
<SQLMap name="_CDM_city">
<BlockCount>-4</BlockCount>
</SQLMap>
<SQLMap name="_CDM_country">
<BlockCount>-4</BlockCount>
</SQLMap>
<SQLMap name="_CDM_zip">
<BlockCount>-4</BlockCount>
</SQLMap>
<StreamLocation>^q3AW.DZLd.S</StreamLocation>
<Type>%Storage.Persistent</Type>
}
}
 

Después insertamos los datos:

insert into My.Address values ('London', '47000', 'UK')
insert into My.Address values ('Paris', '54000', 'France')
insert into My.Address values ('Kyiv', '03000', 'Ukraine')

En los globals podemos ver:

Si abrimos el global con los nombres de las ciudades, podremos ver:

Y si escribimos una consulta:

select City
  from My.Address

recibimos los datos:

En este caso, el DBMS se limita únicamente a leer un global para obtener el resultado completo. Además, ahorra tiempo y recursos en la lectura.

Así pues, hemos hablado de 5 modelos de datos diferentes compatibles con la base de datos InterSystems IRIS: los modelos jerárquicos, relacionales, de objetos, de documentos y de columnas.

Espero que este artículo os resulte útil para saber qué modelos están disponibles. Si tenéis alguna pregunta, podéis escribirla en los comentarios.

2
0 427
Artículo Kurro Lopez · jun 16, 2020 9m read

En mi anterior artículo, revisamos los posibles casos de uso para macros, así que pasemos ahora a un ejemplo más completo de usabilidad de macros. En este artículo diseñaremos y crearemos un sistema de registro.

Sistema de registro

El sistema de registro es una herramienta útil para monitorear el trabajo de una aplicación que ahorra mucho tiempo durante la depuración y el monitoreo. Nuestro sistema constaría de dos partes:

  • Clase de almacenamiento (para registros de anotaciones)
  • Conjunto de macros que agregan automáticamente un nuevo registro al registro

Clase de almacenamiento

Vamos a crear una tabla de lo que necesitamos almacenar y especificar cuándo se pueden obtener estos datos, durante la compilación o en tiempo de ejecución. Esto será necesario cuando trabaje en la segunda parte del sistema: macros, donde buscaremos tener tantos detalles registrables como sea posible durante la compilación:

InformaciónObtenido durante
Tipo de eventoCompilación
Nombre de claseCompilación
Nombre del métodoCompilación
Argumentos pasados ​​a un métodoCompilación
Número de línea en el código fuente de clsRuntime
Número de línea en el código int generadoRuntime
Nombre de usuarioRuntime
Date/TimeRuntime
MensajeRuntime
direccion IPRuntime

Vamos a crear una clase App.Log que contenga las propiedades de la tabla anterior. Cuando se crea un objeto App.Log, las propiedades de UserName, TimeStamp y ClientIPAddress se completan automáticamente.

App.Log class:

Class App.Log Extends %Persistent
{

/// Type of event Property EventType As %String(MAXLEN = 10, VALUELIST = ",NONE,FATAL,ERROR,WARN,INFO,STAT,DEBUG,RAW") [ InitialExpression = "INFO" ];

/// Name of class, where event happened Property ClassName As %String(MAXLEN = 256);

/// Name of method, where event happened Property MethodName As %String(MAXLEN = 128);

/// Line of int code Property Source As %String(MAXLEN = 2000);

/// Line of cls code Property SourceCLS As %String(MAXLEN = 2000);

/// Cache user Property UserName As %String(MAXLEN = 128) [ InitialExpression = {$username} ];

/// Arguments' values passed to method Property Arguments As %String(MAXLEN = 32000, TRUNCATE = 1);

/// Date and time Property TimeStamp As %TimeStamp [ InitialExpression = {$zdt($h, 3, 1)} ];

/// User message Property Message As %String(MAXLEN = 32000, TRUNCATE = 1);

/// User IP address Property ClientIPAddress As %String(MAXLEN = 32) [ InitialExpression = {..GetClientAddress()} ];

/// Determine user IP address ClassMethod GetClientAddress() { // %CSP.Session source is preferable #dim %request As %CSP.Request If ($d(%request)) { Return %request.CgiEnvs("REMOTE_ADDR") } Return $system.Process.ClientIPAddress() } }

 

Macros de registro

Por lo general, las macros se almacenan en archivos * .inc separados que contienen sus definiciones. Los archivos necesarios se pueden incluir en clases usando el comando Include MacroFileName, que en este caso se verá de la siguiente manera: Include App.LogMacro.
 
Para comenzar, definamos la macro principal que el usuario agregará al código de su aplicación:

#define LogEvent(%type, %message) Do ##class(App.Log).AddRecord($$$CurrentClass, $$$CurrentMethod, $$$StackPlace, %type, $$$MethodArguments, %message)

Esta macro acepta dos argumentos de entrada: Tipo de evento y Mensaje. El usuario define el argumento Mensaje, pero el parámetro Tipo de evento requerirá macros adicionales con diferentes nombres que identificarán automáticamente el tipo de evento:

#define LogNone(%message)         $$$LogEvent("NONE", %message)
#define LogError(%message)        $$$LogEvent("ERROR", %message)
#define LogFatal(%message)        $$$LogEvent("FATAL", %message)
#define LogWarn(%message)         $$$LogEvent("WARN", %message)
#define LogInfo(%message)         $$$LogEvent("INFO", %message)
#define LogStat(%message)         $$$LogEvent("STAT", %message)
#define LogDebug(%message)        $$$LogEvent("DEBUG", %message)
#define LogRaw(%message)          $$$LogEvent("RAW", %message)

Por lo tanto, para realizar el registro, el usuario solo necesita colocar la macro $$$LogError("Additional message") en el código de la aplicación.
Todo lo que necesitamos hacer ahora es definir las macros $$$CurrentClass, $$$CurrentMethod, $$$StackPlace, $$$MethodArguments.Comencemos con las tres primeras:

#define CurrentClass     ##Expression($$$quote(%classname))
#define CurrentMethod    ##Expression($$$quote(%methodname))
#define StackPlace       $st($st(-1),"PLACE")

%classname, %methodname las variables se describen en la documentacion. La función $stack devuelve el número de línea del código INT. Para convertirlo en un número de línea CLS, podemos usar este código.

Usemos el paquete %Dictionary para obtener una lista de argumentos de métodos y sus valores. Contiene toda la información sobre las clases, incluidas las descripciones de los métodos. Estamos particularmente interesados ​​en la clase %Dictionary.CompiledMethod y su propiedad FormalSpecParsed, que es una lista:

$lb($lb("Name","Classs","Type(Output/ByRef)","Default value "),...)

correspondiente a la firma del método. Por ejemplo:

ClassMethod Test(a As %Integer = 1, ByRef b = 2, Output c)

tendrá el siguiente valor FormalSpecParsed:

$lb(
$lb("a","%Library.Integer","","1"),
$lb("b","%Library.String","&","2"),
$lb("c","%Library.String","*",""))

Necesitamos que la macro $$$MethodArguments se expanda en el siguiente código (para el método Test):

"a="_$g(a,"Null")_"; b="_$g(b,"Null")_"; c="_$g(c,"Null")_";"

Para lograr esto, tenemos que hacer lo siguiente durante la compilación:

  1. Obtenga un nombre de clase y un nombre de método
  2. Abra una instancia correspondiente de la clase %Dictionary.CompiledMethod y obtenga su propiedad FormalSpec
  3. Conviértalo en una línea de código fuente

Agreguemos los métodos correspondientes a la clase App.Log:

ClassMethod GetMethodArguments(ClassName As %String, MethodName As %String) As %String
{
    Set list = ..GetMethodArgumentsList(ClassName,MethodName)
    Set string = ..ArgumentsListToString(list)
    Return string
}

ClassMethod GetMethodArgumentsList(ClassName As %String, MethodName As %String) As %List { Set result = "" Set def = ##class(%Dictionary.CompiledMethod).%OpenId(ClassName _ "||" _ MethodName) If ($IsObject(def)) { Set result = def.FormalSpecParsed } Return result }

ClassMethod ArgumentsListToString(List As %List) As %String { Set result = "" For i=1:1:$ll(List) { Set result = result _ $$$quote($s(i>1=0:"",1:"; ") _ $lg($lg(List,i))"=") _ "$g(" _ $lg($lg(List,i)) _ ","$$$quote(..#Null)")_" _$s(i=$ll(List)=0:"",1:$$$quote(";")) } Return result }


Ahora definamos la macro $$$MethodArguments como:

#define MethodArguments ##Expression(##class(App.Log).GetMethodArguments(%classname,%methodname))

Caso de uso

A continuación, creemos una clase App.Use con un método de prueba para demostrar las capacidades del sistema de registro:

Include App.LogMacro
Class App.Use [ CompileAfter = App.Log ]
{
/// Do ##class(App.Use).Test()
ClassMethod Test(a As %Integer = 1, ByRef b = 2)
{
    $$$LogWarn("Text")
}
}

Como resultado, la macro $$$LogWarn("Text") en el código int se convierte en la siguiente línea:

Do ##class(App.Log).AddRecord("App.Use","Test",$st($st(-1),"PLACE"),"WARN","a="_$g(a,"Null")_"; b="_$g(b,"Null")_";", "Text")

La ejecución de este código creará un nuevo registro de App.Log:

Mejoras

Después de haber creado un sistema de registro, aquí hay algunas ideas de mejora:

  • En primer lugar, existe la posibilidad de procesar argumentos de tipo objeto ya que nuestra implementación actual solo registra objetos oref.
  • Segundo, una llamada para restaurar el contexto de un método a partir de valores de argumentos almacenados.

Procesamiento de argumentos de tipo objeto

La línea que pone un valor de argumento en el registro se genera en el método ArgumentsListToString y tiene este aspecto:

"_$g(" _ $lg($lg(List,i)) _ ","_$$$quote(..#Null)_")_"

Realicemos una refactorización y muévala a un método GetArgumentValue separado que acepte un nombre y clase de variable (todo lo que sabemos de FormalSpecParsed) y genere un código que convertirá la variable en una línea. Usaremos el código existente para los tipos de datos, y los objetos se convertirán en JSON con la ayuda de los métodos SerializeObject (para llamar desde el código de usuario) y WriteJSONFromObject (para convertir un objeto en JSON):

ClassMethod GetArgumentValue(Name As %String, ClassName As %Dictionary.CacheClassname) As %String
{
    If $ClassMethod(ClassName, "%Extends", "%RegisteredObject") {
        // it's an object
        Return "_##class(App.Log).SerializeObject("_Name _ ")_"
    } Else {
        // it's a datatype
        Return "_$g(" _ Name _ ","_$$$quote(..#Null)_")_"
    }
}

ClassMethod SerializeObject(Object) As %String { Return:'$IsObject(Object) Object Return ..WriteJSONFromObject(Object) }

ClassMethod WriteJSONFromObject(Object) As %String [ ProcedureBlock = 0 ] { Set OldIORedirected = ##class(%Device).ReDirectIO() Set OldMnemonic = ##class(%Device).GetMnemonicRoutine() Set OldIO = $io Try { Set Str=""

    //Redirect IO to the current routine - makes use of the labels defined below
    Use $io::("^"_$ZNAME)

    //Enable redirection
    Do ##class(%Device).ReDirectIO(1)

    Do ##class(%ZEN.Auxiliary.jsonProvider).%ObjectToJSON(Object)
} Catch Ex {
    Set Str = ""
}

//Return to original redirection/mnemonic routine settings
If (OldMnemonic '= "") {
    Use OldIO::("^"_OldMnemonic)
} Else {
    Use OldIO
}
Do ##class(%Device).ReDirectIO(OldIORedirected)

Quit Str

// Labels that allow for IO redirection
// Read Character - we don't care about reading

rchr(c) Quit // Read a string - we don't care about reading rstr(sz,to) Quit // Write a character - call the output label wchr(s) Do output($char(s)) Quit // Write a form feed - call the output label wff() Do output($char(12)) Quit // Write a newline - call the output label wnl() Do output($char(13,10)) Quit // Write a string - call the output label wstr(s) Do output(s) Quit // Write a tab - call the output label wtab(s) Do output($char(9)) Quit // Output label - this is where you would handle what you actually want to do. // in our case, we want to write to Str output(s) Set Str = Str_s Quit }

Una entrada de registro con un argumento de tipo objeto se ve así:

Restaurando el contexto

La idea de este método es hacer que todos los argumentos estén disponibles en el contexto actual (principalmente en la terminal, para la depuración). Para este fin, podemos usar el parámetro del método ProcedureBlock. Cuando se establece en 0, todas las variables declaradas dentro de dicho método permanecerán disponibles al salir del método. Nuestro método abrirá un objeto de la clase App.Log y deserializará la propiedad Argumentos.

ClassMethod LoadContext(Id) As %Status [ ProcedureBlock = 0 ]
{
    Return:'..%ExistsId(Id) $$$OK
    Set Obj = ..%OpenId(Id)
    Set Arguments = Obj.Arguments
    Set List = ..GetMethodArgumentsList(Obj.ClassName,Obj.MethodName)
    For i=1:1:$Length(Arguments,";")-1 {
        Set Argument = $Piece(Arguments,";",i)
        Set @$lg($lg(List,i)) = ..DeserializeObject($Piece(Argument,"=",2),$lg($lg(List,i),2))
    }
    Kill Obj,Arguments,Argument,i,Id,List
}

ClassMethod DeserializeObject(String, ClassName) As %String { If $ClassMethod(ClassName, "%Extends", "%RegisteredObject") { // it's an object Set st = ##class(%ZEN.Auxiliary.jsonProvider).%ConvertJSONToObject(String,,.obj) Return:$$$ISOK(st) obj } Return String }

Así es como se ve en la terminal:

>zw
>do ##class(App.Log).LoadContext(2)
>zw

a=1 b=<OBJECT REFERENCE>[2@%ZEN.proxyObject]

>zw b b=<OBJECT REFERENCE>[2@%ZEN.proxyObject] +----------------- general information --------------- | oref value: 2 | class name: %ZEN.proxyObject | reference count: 2 +----------------- attribute values ------------------ | %changed = 1 | %data("prop1") = 123 | %data("prop2") = "abc" | %index = ""

¿Que sigue?

La mejora potencial clave es agregar otro argumento a la clase de registro con una lista arbitraria de variables creadas dentro del método.

Conclusiones

Las macros pueden ser bastante útiles para el desarrollo de aplicaciones.

Preguntas

¿Hay alguna manera de obtener el número de línea durante la compilación?

Links

1
0 242
Anuncio Luis Angel Pérez Ramos · oct 21, 2022

Quiero anunciar el lanzamiento de algo muy interesante - y revolucionario, de hecho. Puede sonar exagerado, pero no creo que hayáis visto nada como esto, ¡ni si quiera imaginar que sería posible!

Hemos sacado un nuevo módulo JavaScript/Node.js llamado glsdb del que podéis leer todo aquí:

https://github.com/robtweed/glsdb

No obstante, para el propósito de este anuncio, me voy a centrar en una parte de glsdb: sus APIs que abstraen las Clases de IRIS (o Cache) como Objetos JavaScript equivalentes.

Con esto quiero decir que los Objetos de JavaScript serán en realidad ¡Objetos IRIS persistidos en la base de datos! 

0
0 99
Artículo Dmitrii Kuznetsov · sep 19, 2022 17m read

Una sesión simultánea en IRIS: SQL, Objects, REST y GraphQL  

Kazimir Malevich, "Deportistas" (1932)

"¡Pues claro que no lo entiende! ¿Cómo puede una persona que siempre ha viajado en un carruaje tirado por caballos entender los sentimientos e impresiones del viajero expres o del piloto de aviones?"
Kazimir Malevich (1916)

Introducción

Ya hemos abordado el tema de por qué la representación de objetos/clases es superior a la de SQL para implementar modelos de áreas temáticas. Y esas conclusiones y hechos son tan ciertos ahora como lo han sido siempre. Entonces, ¿por qué deberíamos dar un paso atrás y hablar sobre las tecnologías que arrastran las abstracciones de vuelta al nivel global, donde habían estado en la era pre-objetos y pre-clases? ¿Por qué debemos fomentar el uso de "código espagueti", que provoca errores que son difíciles de rastrear y se basa en las habilidades de desarrolladores virtuosos? 

Hay varios argumentos a favor de la transmisión de datos por medio de APIs basadas en SQL/REST/GraphQL en lugar de representarlos como clases/objetos:

0
0 2512
Artículo Alberto Fuentes · jul 29, 2019 4m read

¡Hola a tod@s!

Hoy os traigo un artículo de Kyle Baxter sobre búsquedas de texto libre que vale la pena guardar como referencia :)

¿Os gustaría buscar de forma eficiente campos de texto libres almacenados en vuestra aplicación? ¿Lo habéis intentado alguna vez pero no habéis encontrado una manera que os ofrezca un buen rendimiento? Hay un truco especial que resuelve el problema :)

Como es habitual, si preferís la versión TL;DR (Demasiado largo, no lo he leído), podéis ir directamente al final del artículo, pero preferiríamos que leyeseis el artículo entero para evitar herir sentimientos.

1
0 331
Artículo Alberto Fuentes · ene 27, 2022 6m read

@Ming Zhou hizo una buena pregunta: ¿Cómo conseguir todas las propiedades definidas en una clase desde %Dictionary.ClassDefinition? y la respuesta resume exactamente por qué ObjectScript es mi lenguaje favorito.

Cuando describo por primera vez ObjectScript o IRIS a alguien, siempre explico que puedes escribir una clase, compilarla, obtener una tabla y trabajar con tus datos desde una perspectiva orientada a objetos o relacional - la que resulte más natural. En cualquier caso, es sólo un fino envoltorio alrededor de los Globals, las estructuras de datos super rápidas existentes por debajo, y que también puedes usar cuando realmente necesitas ese acelerón extra.

0
0 183
Artículo Ricardo Paiva · dic 10, 2021 4m read

Me gustaría compartir algunas funciones de almacenamiento que también existen en Caché y que son prácticamente desconocidas y en su mayoría no se utilizan. Por supuesto, están disponibles en IRIS y son más relevantes con arquitecturas de almacenamiento extensas y distribuidas.

0
0 183
Artículo Ricardo Paiva · dic 3, 2021 2m read

Si defines una tabla/clase persistente, el compilador de clases genera una definición de almacenamiento adecuada. Otra opción es definir un mapeo SQL para un almacenamiento global que ya existe.  Esto ya se explicó estupendamente en otra serie de artículos:  El arte del mapeo de globales para Clases 1 de 3

Después de definir el diagrama de almacenamiento, el compilador de la clase lo podrá ampliar, pero los parámetros fundamentales de almacenamiento no cambiarán.
Esto no significa que no puedas cambiarlo manualmente.

Mi artículo El mapa de bits adoptado incluye un caso como este. Y en combinación con algunas Condiciones WHERE estáticas, como se describió anteriormente, permite que esté disponible también para SQL.

La definición habitual de tus globals de almacenamiento se parecerá a este ejemplo:

<DataLocation>^User.PersonD</DataLocation>
<IdLocation>^User.PersonD</IdLocation>
<IndexLocation>^User.PersonI</IndexLocation>
<StreamLocation>^User.PersonI</StreamLocation
>

Ahora cambiaremos esta definición por una dinámica

<DataLocation>@%MyData</DataLocation>
<IdLocation>@%MyId</IdLocation>
<IndexLocation>@%MyIndex</IndexLocation>
<StreamLocation>@%MyStream</StreamLocation>

y añadimos algo de comodidad

Parameter MANAGEDEXTENT As INTEGER = 0;
ClassMethod SetStorage(ref As %String) As %Integer [ SqlName = SetStorage, SqlProc ]
{
    set %MyData=ref_"D"
      , %MyId=%MyData
      , %MyIndex=ref_"I"
      , %MyStream=ref_"S"
    quit $$$OK
}

Para acceder a los objetos, dirigimos nuestro almacenamiento y lo llenamos

write ##class(Person).SetStorage("^mtemp.Person")
write ##class(Person).Populate(5)

y en SQL:

SELECT  from Person where SetStorage('^mtemp.Person')=1

Funciona con PPG  (^||Person)
mediante el namespace (^|"USER"|Person)
también corresponde al subíndice que se utilizó en El mapa de bits adoptado  con otra variante de ClassMethod SetStorage() 

Para acceso a objetos (sin SQL), funciona incluso para variables locales.
No es el uso deseado, pero demuestra la flexibilidad de esta función. 

Pero ten cuidado. NO funcionará con sharding. Pero eso no es una sorpresa.

0
0 222
Artículo David Reche · jul 23, 2019 5m read

¡Hola a tod@s!

Cuando hablo con alguien de perfil técnico por primera vez acerca de InterSystems IRIS, siempre comienzo hablando de que en el centro de todo InterSystems IRIS es una Base de Datos Multimodelo. En mi opinión, esta es la mayor ventaja (desde la visión de Sistemas de Bases de Datos), ya que:

  • ¿Quieres obtener un resumen o partes específicas de tus datos? Usa SQL!
  • ¿Necesitas trabajar de forma intensiva con un registro? Usa Objetos!
  • ¿Quieres establecer un valor y conoces la clave? Piensalo de nuevo. Usa globals!

Y en todos los casos, el dato está almacenado de forma única. ¡Tú eliges la manera en la que quieres acceder al mismo!!

De un primer vistazo es una bonita historia - corta, concisa y con un mensaje; pero cuando se empieza a trabajar con InterSystems IRIS, comienzan a surgir  preguntas: ¿Cómo están relacionados las clases, las tablas y los globals? ¿Qué son cada uno para el otro? ¿Cómo se almacenan realmente los datos?

En este artículo voy a tratar de responder estas preguntas y explicar qué está pasando realmente.

3
0 618
Artículo Mathew Lambert · nov 25, 2020 17m read

Este artículo fue creado como efecto secundario de las preparaciones para un conjunto más extendido de artículos sobre la simple, pero útil, implementación de MapReduce en Caché. Estaba buscando una forma relativamente fácil de pasar argumentos a (potencialmente) múltiples objetivos mediante mecanismos de invocación remota. Y tras varios intentos, me di cuenta de que en el ObjectScript de Caché contamos con mecanismos muy potentes que podrían ser de utilidad para esto: dynamix dispatching para métodos y propiedades.

0
1 153
Artículo Kurro Lopez · jul 30, 2020 9m read

Este es el primer artículo de una serie que se sumerge en herramientas de visualización y análisis de datos de series temporales. Obviamente, estamos más interesados en analizar los datos relacionados con el rendimiento que podemos recopilar de la familia de productos Caché. Sin embargo, como veremos más adelante, no estamos limitados a eso. Por ahora estamos explorando Python y las bibliotecas/herramientas disponibles dentro de ese ecosistema.

La serie está estrechamente vinculada a la excelente serie de Murray sobre el rendimiento y la supervisión de Caché. (ver aquí) y mas especificamente este artículo.

Descargo de responsabilidad I: Si bien hablaré de pasada sobre la interpretación de los datos que estamos viendo, hablar de eso en detalle distraería demasiado del objetivo real. Recomiendo encarecidamente la serie de Murray para comenzar, para obtener una comprensión básica del tema.

Descargo de responsabilidad II: Existen miles de millones de herramientas que le permiten visualizar los datos recopilados. Muchos de ellos trabajan directamente con los datos que obtiene de mgstat y sus amigos, o solo necesitan un ajuste mínimo. Esta no es una publicación de 'esta solución es la mejor'. Es solo una de las formas en que he encontrado útil y eficiente trabajar con los datos.

Descargo de responsabilidad III: La visualización y el análisis de datos es un campo altamente adictivo, emocionante y divertido para sumergirse. Puede perder algo de tiempo libre por esto. ¡Usted ha sido advertido!

Entonces, sin más preámbulos, profundicemos en ello.

Prerequisitos

Para comenzar, necesitará algunas herramientas y bibliotecas:

  • Jupyter notebooks
  • Python (3)
  • varias bibliotecas de Python que usaremos en el futuro

Python (3) Necesitará Python en su máquina. Hay numerosas formas de instalarlo en varias arquitecturas. Yo uso homebrew en mi mac, lo que lo hizo fácil:

brew install python3

Solicite a google instrucciones para su plataforma favorita.

Jupyter notebooks: Si bien no es técnicamente necesario, el Jupyter notebooks hace que trabajar en scripts de python sea muy fácil. Le permite ejecutar interactivamente y mostrar scripts de Python desde una ventana del navegador. También permite trabajar en colaboración en scripts. Como hace que sea muy fácil experimentar y jugar con código, es muy recomendable.

pip3 install jupyter

(de nuevo, habla con $search-engine ;)

Librerías Python Mencioné las diferentes bibliotecas de Python mientras las usamos en el futuro. Si obtiene un error en una declaración import, una buena primera aproximación es siempre asegurarse de tener instalada la biblioteca:

pip3 install matplotlib

Empezando

Suponiendo que tiene todo instalado en su máquina, debería poder ejecutar

jupyter notebook

de un directorio. Esto debería abrir automáticamente una ventana del navegador con una interfaz de usuario simple. emtpy-notebook

Continuaremos y crearemos un nuevo cuaderno a través del menú y agregaremos un par de declaraciones de importación a nuestra primera celda de código (New -> Notebooks -> Python3):

notebook-imports

import math
import pandas as pd
import mpl_toolkits.axisartist as AA
from mpl_toolkits.axes_grid1 import host_subplot
import matplotlib.pyplot as plt
from datetime import datetime
from matplotlib.dates import DateFormatter

En cuanto a las bibliotecas que estamos importando, solo quiero mencionar algunas:

  • Pandas "es una biblioteca de código abierto con licencia BSD que proporciona estructuras de datos y herramientas de análisis de datos de alto rendimiento y fáciles de usar para el lenguaje de programación Python". Lo que permite trabajar eficientemente con grandes conjuntos de datos. Si bien los conjuntos de datos que obtenemos de pButtons, de ninguna manera son 'big data'. Sin embargo, nos consolaremos con el hecho de que podríamos ver muchos datos a la vez. Imagine que ha estado recolectando pButtons con muestreo de 24 h/2 segundos durante los últimos 20 años en su sistema. Podríamos graficar eso.
  • Matplotlib "matplotlib es una biblioteca de trazado 2D de python que produce cifras de calidad de publicación en una variedad de formatos impresos y entornos interactivos en todas las plataformas". Este será el principal motor de gráficos que vamos a utilizar (por ahora).

Si recibe un error al ejecutar la celda de código actual (shortcut: Ctrl+Enter) (lista de atajos), asegúrese de verificar que los tenga instalados.

También notará que cambié el nombre del cuaderno Sin título, para hacerlo, simplemente puede hacer clic en el título.

Cargando algunos datos

Ahora que pusimos un poco de terreno, es hora de obtener algunos datos. Por suerte, Pandas proporciona una manera fácil de cargar datos CSV. Ya que tenemos un conjunto de datos de mgstat por ahí in csv-format, we'll just use that.

mgstatfile = '/Users/kazamatzuri/work/proj/vis-articles/part1/mgstat.txt'
data = pd.read_csv(
    mgstatfile, 
    header=1,
    parse_dates=[[0,1]]
   )

Nosotras estamos utilizando el read_csv comando para leer directamente los datos de mgstat en un DataFrame. Consulte la documentación completa para una descripción completa de las opciones. En resumen: simplemente estamos pasando el archivo para leerlo y decirle que la segunda línea (¡basada en 0!) Contiene los nombres de los encabezados. Dado que mgstat divide los campos de fecha y hora en dos campos, también necesitamos combinarlos con el parámetro parse_dates.

data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 25635 entries, 0 to 25634
Data columns (total 37 columns):
Date_       Time        25635 non-null datetime64[ns]
  Glorefs               25635 non-null int64
 RemGrefs               25635 non-null int64
 GRratio                25635 non-null int64
  PhyRds                25635 non-null int64
 Rdratio                25635 non-null float64
 Gloupds                25635 non-null int64
 RemGupds               25635 non-null int64
 Rourefs                25635 non-null int64
 RemRrefs               25635 non-null int64
  RouLaS                25635 non-null int64
 RemRLaS                25635 non-null int64
  PhyWrs                25635 non-null int64
   WDQsz                25635 non-null int64
  WDtmpq                25635 non-null int64
 WDphase                25635 non-null int64
  WIJwri                25635 non-null int64
  RouCMs                25635 non-null int64
 Jrnwrts                25635 non-null int64
   GblSz                25635 non-null int64
 pGblNsz                25635 non-null int64
 pGblAsz                25635 non-null float64
   ObjSz                25635 non-null int64
 pObjNsz                25635 non-null int64
 pObjAsz                25635 non-null int64
   BDBSz                25635 non-null int64
 pBDBNsz                25635 non-null int64
 pBDBAsz                25635 non-null float64
  ActECP                25635 non-null int64
  Addblk                25635 non-null int64
 PrgBufL                25635 non-null int64
 PrgSrvR                25635 non-null int64
  BytSnt                25635 non-null int64
  BytRcd                25635 non-null int64
  WDpass                25635 non-null int64
  IJUcnt                25635 non-null int64
 IJULock                25635 non-null int64
dtypes: datetime64[ns](1), float64(3), int64(33)
memory usage: 7.2 MB

nos da una buena visión general del DataFrame recopilado.

Trabajando con los datos

Dado que algunos nombres de campo contienen espacios y guión bajo ("Date_ Time") es bastante difícil de manejar, seguiremos adelante y eiminamos espacios y cambiaremos el nombre de la primera columna:

data.columns=data.columns.str.strip()
data=data.rename(columns={'Date_       Time':'DateTime'})

El Marco de datos predeterminado es un RangeIndex. Esto no es muy útil para ver nuestros datos. Como tenemos disponible una columna DateTime bastante práctica, seguiremos adelante y la configuraremos como índice:

data.index=data.DateTime

Ahora estamos listos para crear una versión inicial de nuestra trama. Como esta es siempre una de las primeras cosas a tener en cuenta, usemos Glorefs para esto:

plt.figure(num=None, figsize=(16,5), dpi=80, facecolor='w', edgecolor='k')
plt.xticks(rotation=70)
plt.plot(data.DateTime,data.Glorefs)
plt.show()

Primero le decimos a la biblioteca en qué tamaño queremos el gráfico. También queremos que las etiquetas del eje x giren un poco, para que no se superpongan. Finalmente graficamos DateTime vs Glorefs y mostramos el gráfico. Esto nos da algo como el siguiente gráfico.

Glorefs

Podemos reemplazar fácilmente Glorefs con cualquiera de las otras columnas para tener una idea general de lo que está sucediendo.

Combinando gráficos

En algún momento es bastante útil mirar múltiples gráficos a la vez. Entonces, la idea de dibujar varias parcelas en una sola gráfica parece natural. Si bien es muy sencillo hacerlo solo con matplotlib:

plt.plot(data.DateTime,data.Glorefs)
plt.plot(data.DateTime,data.PhyRds)
plt.show()

Esto nos dará más o menos la misma gráfica que antes. El problema, por supuesto, es la escala y. Dado que Glorefs sube a millones, mientras que los PhyRds generalmente están en los 100 (a miles), no los vemos.

Para resolver esto, necesitaremos usar el kit de herramientas axisartist previamente importado.

plt.gcf()
plt.figure(num=None, figsize=(16,5), dpi=80, facecolor='w', edgecolor='k')
host = host_subplot(111, axes_class=AA.Axes)
plt.subplots_adjust(right=0.75)

par1 = host.twinx()
par2 = host.twinx()
offset = 60
new_fixed_axis = par2.get_grid_helper().new_fixed_axis
par2.axis["right"] = new_fixed_axis(loc="right",axes=par2,offset=(offset, 0))
par2.axis["right"].toggle(all=True)

host.set_xlabel("time")
host.set_ylabel("Glorefs")
par1.set_ylabel("Rdratio")
par2.set_ylabel("PhyRds")

p1,=host.plot(data.Glorefs,label="Glorefs")
p2,=par1.plot(data.Rdratio,label="Rdratio")
p3,=par2.plot(data.PhyRds,label="PhyRds")

host.legend()

host.axis["left"].label.set_color(p1.get_color())
par1.axis["right"].label.set_color(p2.get_color())
par2.axis["right"].label.set_color(p3.get_color())

plt.draw()
plt.show()

El breve resumen es: agregaremos dos ejes y al diagrama, que tendrán su propia escala. Si bien utilizamos implícitamente la subtrama en nuestro primer ejemplo, en este caso necesitamos acceder a ella directamente para poder agregar el eje y las etiquetas. Establecemos un par de etiquetas y los colores. Después de agregar una leyenda y conectar los colores a las diferentes parcelas, terminamos con una imagen como esta:

combinado

Comentarios finales

Esto ya nos da un par de herramientas muy poderosas para trazar nuestros datos. Exploramos cómo cargar datos de mgstat y crear algunos gráficos básicos. En la siguiente parte, jugaremos con diferentes formatos de salida para nuestros gráficos y obtendremos más datos.

Comentarios y preguntas son alentados! ¡Comparte tus experiencias!

-Fab

ps. el cuaderno para esto está disponible aquí

0
0 323
Artículo Nancy Martínez · jul 22, 2020 8m read

Ejemplos de Mapeos

Obviamente, si tienes un cuatro artículo en la trilogía, debes apostar por las ganancias económicas y escribir el quinto, ¡así que aquí está!

Nota: Hace muchos años, Dan Shusman me dijo que el mapeo de globals es una forma de arte. No existe una manera correcta o incorrecta de hacerlo. El modo en que interpretes los datos te llevará al tipo de mapeo que realizas. Como siempre, existe más de una forma de llegar a la respuesta final. Según vayas revisando mis ejemplos, verás que hay algunos en los que se mapean el mismo tipo de datos, pero de distintas maneras.

0
0 190
Artículo Mathew Lambert · jun 25, 2020 3m read

El propósito de este artículo es reforzar el perfil de un procedimiento muy potente que ha estado disponible desde hace mucho tiempo para nosotros, y abrir un debate sobre las maneras en que puede utilizarse (o explotarse).

Puedes leer más información sobre el el mecanismo aquí. En resumen, cuando definas una clase puedes utilizar la palabra clave Projection para referirte a una o más clases de proyección. Una clase de proyección permite implementar métodos que se llaman desde puntos clave en el ciclo de vida de tu clase.

0
0 507
Artículo Nancy Martínez · mar 13, 2020 9m read

Introducción

Un requisito frecuente en muchas aplicaciones es registrar en una base de datos los cambios que se realizan en los datos- qué datos se modificaron, quién los modificó y cuándo (control de cambios). Hay muchos artículos relacionados con el tema y existen diferentes métodos sobre cómo hacer esto en Caché.

1
0 399
Artículo Ricardo Paiva · mayo 22, 2020 9m read

¡Hola desarrollador!

En este articulo repasaremos una publicación original de Maks Atygaev sobre la Implementación de una API de MongoDB, basada en InterSystems Caché - MonCaché.

https://habrastorage.org/files/26f/995/320/26f995320d4a4d5fbe44c8ba3b3fe24d.png

Descargo de responsabilidad: En este artículo se muestra la opinión personal del autor y no tiene ninguna relación con opinión oficial de InterSystems.

Idea

La idea del proyecto es implementar las características básicas de la API en MongoDB (v2.4.9) , con la finalidad de buscar, guardar, actualizar y eliminar documentos de una manera que permita el uso de InterSystems Caché, en lugar de MongoDB, sin cambiar el código del lado del cliente.

0
0 430
Artículo Estevan Martinez · feb 11, 2020 7m read

Esta publicación es el resultado directo de trabajar con un cliente de InterSystems que acudió a mí con el siguiente problema:

SELECT COUNT(*) FROM MyCustomTable

Esto tarda 0.005 segundos, con 2300 filas en total.  Sin embargo:

SELECT * FROM MyCustomTable
0
0 184
Artículo Alberto Fuentes · oct 14, 2019 6m read

¡Hola a tod@s!

Me gustaría comentar con vosotros algunas de las mejoras en procesamiento JSON que incorpora IRIS desde la versión 2019.1. Utilizar JSON como formato de serialización es muy común a la hora de construir aplicaciones hoy en día, especialmente si desarrollamos o interactuamos con servicios REST.

Dar formato a cadenas a JSON

0
0 337
Artículo Jose-Tomas Salvador · ago 14, 2019 5m read

La funcionalidad de Sincronización de Objetos no es nueva, estaba presente en Caché, pero quería explorar un poco más en profundidad cómo funciona. Siempre he pensado que la sincronización automática de una base de datos es compleja en sí misma pero, para algunos escenarios muy particulares quizá no sea tan difícil. Así que he considerado un caso de uso muy simple (OK, quizá el caso típico, no descubro nada... pero si es común y funciona, es bueno  ). Puedes bajar el código de GitHub y compilarlo en tu sistema, generar automáticamente datos de ejemplo y jugar un poco con ello. Está hecho para

0
1 180
Artículo Alberto Fuentes · ago 8, 2019 2m read

¡Hola a todos!

En ocasiones, mientras depuramos  código COS podemos obtener mensajes de error como: <UNDEFINED>test^routine *test

El error (al que podemos acceder con la variable especial $zerror) nos indica la rutina y la línea que ha fallado.
Para revisar el código desde la línea de comandos, escribimos:

ZLOAD routine 

ZPRINT test

Posiblemente también nos sea útil  echar un vistazo a las líneas que se encuentran antes de la línea donde se produjo el error, ya que estas le proporcionan contexto. Utilizando ZPRINT podemos escribir ZPRINT +1:test pero esto podría mostrarnos cientos de líneas.

0
0 159