Chapter 4 SQL and Object Usage for Multidimensional Storage (1)

This chapter describes how InterSystems IRIS® objects and SQL engines leverage multidimensional storage (global variables) to store persistent objects, relational tables, and indexes.

Although the InterSystems IRIS objects and SQL engine automatically provide and manage the data store structure, it is useful to know the details of how it works.

The object and relational views of data use the same storage structure. For simplicity, this chapter introduces storage only from an object perspective.

data

Each Persistent class that uses the % storage.Persistent Storage class (the default) can use one or more nodes of multidimensional Storage (global variables) to store its own instance in the InterSystems IRIS database.

Each persistent class has a storage definition that defines how its properties are stored in the global variable node. This storage definition (called the “default structure”) is automatically managed by the class compiler.

The default structure

The default structure for storing persistent objects is very simple:

  • The data is stored in global variables whose names start with the full class name, including the package name. additional"D"To form the name of the global data while attaching"I"As a global index.
  • The data for each instance is stored in a single node of global data, with all non-transient attributes placed$listIn the structure.
  • Each node in a data global variable is an objectIDValue as a subscript. By default, objectsIDThe value is obtained by calling the counter node stored at the global variable data root (without subscripts)$IncrementThe integer provided by the function.

For example, suppose we define a simple persistence class myapp.person with two text attributes:

Class MyApp.Person Extends %Persistent
{
Property Name As %String;
Property Age As %Integer;
}
Copy the code

If we create and save two instances of this class, the resulting global variable will look like this:

 ^MyApp.PersonD = 2  // counter node
 ^MyApp.PersonD(1) = $LB("".530."Abraham")
 ^MyApp.PersonD(2) = $LB("".680."Philip")
Copy the code

Note that it is stored in each node$ListThe first part of the structure is empty; This is reserved for class names. If you definePersonClass, the slot contains the name of the subclass. When multiple objects are stored in the same extent,%OpenIdMethod (by%PersistentClass provided) use this information to polymorphically open the correct object type. This slot is shown in the class storage definition asThe CLASSNAME "% %"Properties.

IDKEY

The IDKEY mechanism allows explicit definition of the value used as an object ID. To do this, simply add the IDKEY index definition to the class and specify one or more attributes that will provide an ID value. Note that once an object is saved, its object ID value cannot be changed. This means that once you save an object using the IDKEY mechanism, you cannot modify any of the properties on which the object ID is based.

Class MyApp.Person Extends %Persistent
{
Index IDKEY On Name [ Idkey ];

Property Name As %String;
Property Age As %Integer;
}
Copy the code

If we create and save two instances of the Person class, the resulting global variable now looks like this:

 ^MyApp.PersonD("Abraham") = $LB("".530."Abraham")
 ^MyApp.PersonD("Philip") = $LB("".680."Philip")
Copy the code

Notice that no counter nodes are defined anymore. Also note that by basing the object ID on the Name attribute, we have implied that the value of Name must be unique for each object.

If the IDKEY index is based on multiple attributes, the master data node has multiple subscripts. Such as:

Class MyApp.Person Extends %Persistent
{
Index IDKEY On (Name,Age) [ Idkey ];

Property Name As %String;
Property Age As %Integer;
}
Copy the code

In this case, the generated global variable now looks like:

 ^MyApp.PersonD("Abraham".530) = $LB("".530."Abraham")
 ^MyApp.PersonD("Philip".680) = $LB("".680."Philip")
Copy the code

Important note:IDKEYThe value of any attribute used by the index cannot have a consecutive pair of vertical bars (||), unless the attribute is a valid reference to a persistent class instance. This limitation is imposed by the way the InterSystems SQL mechanism works. inIDKeyProperty.||Can lead to unpredictable behavior.

Subclasses

By default, any fields introduced by a subclass of a persistent object are stored in the attached node. The name of the subclass is used as the additional subscript value.

For example, suppose we define a simple persistent myapp.person class with two text attributes:

Class MyApp.Person Extends %Persistent
{
Property Name As %String;

Property Age As %Integer;
}
Copy the code

Now we define a persistent subclass myapp.Students, which introduces two additional text attributes:

Class MyApp.Student Extends Person
{
Property Major As %String;

Property GPA As %Double;
}
Copy the code

If we created and saved two instances of this myApp. Student class, the global result would look like this:

^MyApp.PersonD = 2  // counter node
^MyApp.PersonD(1) = $LB("Student".19."Jack")
^MyApp.PersonD(1."Student") = $LB(3.2."Physics")

^MyApp.PersonD(2) = $LB("Student".20."Jill")
^MyApp.PersonD(2."Student") = $LB(3.8."Chemistry")
Copy the code

Properties inherited from the Person class are stored in the primary node, while properties introduced by the Student class are stored in another child node. This structure ensures that student data can be used interchangeably with staff data. For example, an SQL query that lists the names of all Person objects correctly retrieves the Person and Student data. This structure also makes it easier for the class compiler to maintain data compatibility when attributes are added to a superclass or subclass.

Notice that the first part of the primary node contains the string “Student” – which identifies the node that contains the Student data.

Father and son

In parent-child relationships, instances of child objects are stored as child nodes of the parent object to which they belong. This structure ensures that child instance data and parent data are physically clustered.

/// An Invoice class
Class MyApp.Invoice Extends %Persistent
{
Property CustomerName As %String;

/// an Invoice has CHILDREN that are LineItems
Relationship Items As LineItem  [inverse = TheInvoice, cardinality = CHILDREN];
}
Copy the code

And LineItem:

/// A LineItem class
Class MyApp.LineItem Extends %Persistent
{
Property Product As %String;
Property Quantity As %Integer;

/// a LineItem has a PARENT that is an Invoice
Relationship TheInvoice As Invoice [inverse = Items, cardinality = PARENT];
}
Copy the code

If we store multiple instances of Invoice objects, each with an associated LineItem object, the result of the global variable would look something like this:

^MyApp.InvoiceD = 2  // invoice counter node
^MyApp.InvoiceD(1) = $LB(""."Wiley Coyote")
^MyApp.InvoiceD(1."Items".1) = $LB(""."Rocket Roller Skates".2)
^MyApp.InvoiceD(1."Items".2) = $LB(""."Acme Magnet".1)

^MyApp.InvoiceD(2) = $LB(""."Road Runner")
^MyApp.InvoiceD(2."Items".1) = $LB(""."Birdseed".30)
Copy the code

Embedded objects

Embedded objects are stored by first converting them to a serialized state (by default, a $List structure containing object attributes) and then storing this serial state in the same way as any other attribute.

For example, suppose we define a simple serial (embeddable) class with two literal attributes:

Class MyApp.MyAddress Extends %SerialObject
{
Property City As %String;
Property State As %String;
}
Copy the code

Now, let’s modify the previous example to add the embedded Home Address attribute:

Class MyApp.MyClass Extends %Persistent
{
Property Name As %String;
Property Age As %Integer;
Property Home As MyAddress;
}
Copy the code

If we create and save two instances of this class, the generated global variable is equivalent to:

 ^MyApp.MyClassD = 2  // counter node
 ^MyApp.MyClassD(1) = $LB(530."Abraham",$LB("UR"."Mesopotamia"))
 ^MyApp.MyClassD(2) = $LB(680."Philip",$LB("Bethsaida"."Israel"))
Copy the code

flow

By splitting the global stream’s data into a series of chunks (each less than32KByte) and write these blocks to a series of sequential nodes in which the global stream is stored. File streams are stored in external files.