Using Caché Objects
Working with Collections
[Home] [Back] [Next]
InterSystems: The power behind what matters   
Class Reference   
Search:    

Caché supports collections, which provide a way to work with a set of elements, all of the same type. The elements can be literal values or can be objects.

You can define collection properties in any object class. You can also define stand-alone collections for other purposes, such as for use as an method argument or return value. This chapter describes collections, especially collection properties. It discusses the following topics:
Also see the chapters Defining and Using Literal Properties,” Working with Streams,” Defining and Using Object-Valued Properties,” Defining and Using Relationships, and Using and Overriding Property Methods.”
When viewing this book online, use the preface of this book to quickly find other topics.
Introduction to Collections
A collection contains a set of individual elements, all of the same type. There are two kinds of collections: lists and arrays.
Each item in a collection is called an element and its position within the collection is called a key. For list collections, the system generates sequential integer keys. For arrays, keys can have arbitrary values, and you specify them for each element.
Caché uses a set of collection classes as an interface to collection properties; these are classes in the %Collection package. Caché provides a different set of collection classes for use when you need a stand-alone collection, for example, to pass as an argument to a method; these are classes in the %Library package.
Each set of classes provides methods and properties that you can use to add collection items, remove collection items, count collection items, and so on. This chapter focuses on %Collection classes, but the details are similar for the %Library classes.
Note that collection classes are object classes. Thus a collection is an object.
Defining Collection Properties
To define a list property, add a property as follows:
Property MyProp as List of Type;
Where MyProp is the property name, and Type is either a data type class or an object class.
Similarly, to define an array property, add a property as follows:
Property MyProp as Array of Type;
For example, the following property definition is a list of %String values:
Property Colors As List Of %String;
For another example, the following property definition is an array of Doctor values, where Doctor is the name of an object class.
Property Doctors As Array Of Doctor;
Internally, Caché uses classes in the %Collection package to represent such properties, as follows:
This means that you use methods of these classes to add collection items, remove collection items, and so on. Later parts of this chapter show how this is done.
Do not use the %Collection classes directly as the type of a property. For example, do not create a property definition like this:
Property MyProp as %Collection.ArrayOfDT;
Instead use the syntax shown earlier in this section.
Adding Items to a List Property
Given a list property (as described in the previous section), use the following procedure to specify a value for the property:
  1. If the list items are objects, create those objects as needed.
  2. Add list items to the list as needed. To add one list item, call the Insert() instance method of the list property. This method is as follows:
    method Insert(listitem) as %Status
    Or use other methods of the list property, such as InsertAt(). For an introduction, see Working with List Properties.”
    For details on the methods, see the class reference for %Collection.ListOfDT and %Collection.ListOfObj.
For example, suppose that obj is an OREF, and Colors is a list property of the associated object. In that case, we could add list items as follows:
 Do obj.Colors.Insert("Red")
 Do obj.Colors.Insert("Green")
 Do obj.Colors.Insert("Blue")
For another example, suppose that pat is an OREF, and Diagnoses is a list property of the associated object. This property is defined as follows, where PatientDiagnosis is the name of a class:
Property Diagnoses as list of PatientDiagnosis;
In this case, we could add a list item as follows:
 Set patdiag=##class(PatientDiagnosis).%New()
 Set patdiag.DiagnosisCode=code
 Set patdiag.DiagnosedBy=diagdoc
 Set status=pat.Diagnoses.Insert(patdiag)
Adding Items to an Array Property
Given an array property (as described earlier in this chapter), use the following procedure to specify a value for the property:
  1. If the array items are objects, create those objects as needed.
  2. Add array items to the array as needed. To add one list item, call the SetAt() instance method of the array property. This method is as follows:
    method SetAt(element, key As %String) as %Status
    Where element is the element to add, and key is the array key to associate with that element.
    Important:
    Do not include a sequential pair of vertical bars (||) within the value that you use as the array key. This restriction is imposed by the way in which the Caché SQL mechanism works.
    For details on this method, see the class reference for %Collection.ArrayOfDT and %Collection.ArrayOfObj. (You will notice that these classes define the same set of methods.)
    Or use the other methods described in Working with Array Properties.”
For example, to add a new color to an array of RGB values accessed by color name in a Palette object, use the following code:
 Do palette.Colors.SetAt("255,0,0","red")
where palette is the OREF containing the array, Colors is the name of the array property, and “red” is the key to access the value “255,0,0”.
Working with List Properties
When you create a list property as described earlier, the property itself is an object that provides the instance methods of one of the following classes, depending on the property definition:
These classes provide instance methods such as GetAt(), Find(), GetPrevious(), GetNext(), and Remove(). The following example shows how you might use these methods:
 set p=##class(Sample.Person).%OpenId(1)
 for i=1:1:p.FavoriteColors.Count() {
    write !, p.FavoriteColors.GetAt(i)
 }
 
Lists are ordered collections of information. Each list element is identified by its position (slot) in the list. You can set the value for a slot or insert data at a slot. If you set a new value for a slot, that value is stored in the list. If you set the value for an already existing slot, the new data overwrites the previous data and the slot assignments are not modified. If you insert data at an already existing slot, the new list item increments the slot number of all subsequent slots. (Inserting a new item in the second slot slides the data currently in the second slot to the third slot, the object currently in the third slot to the fourth slot, and so on.)
You can modify data at slot n using the following syntax:
 Do oref.PropertyName.SetAt(data,n)
where oref is an OREF, PropertyName is the name of a list property of that object, and data is the actual data. For example, suppose that person.FavoriteColors is a list of favorite colors and suppose that this list is initially “red”,“blue”, and “green.” To change the second color in the list (so that the list is “red”,“yellow”, and “green”), we can use the following code:
 Do person.FavoriteColors.SetAt("yellow",2)
For other methods, such as Find(), RemoveAt(), and others, see the class reference for %Collection.ListOfDT and %Collection.ListOfObj.
Working with Array Properties
When you create an array property as described earlier, the property itself is an object that provides the instance methods of one of the following classes, depending on the property definition:
These classes provide instance methods such as GetAt(), Find(), GetPrevious(), GetNext(), and Remove(). For details, see the class reference for these classes. Note that the details are not the same as for the list classes.
Copying Collection Data
To copy the items in one collection into another collection, set the recipient collection equal to the source collection. This copies the contents of the source into the recipient (not the OREF of the collection itself). Some examples of such a command are:
 Set person2.Colors = person1.Colors
 Set dealer7.Inventory = owner3.cars
where person2, person1, dealer7, and owner3 are all instances of classes and Colors, Inventory, and cars are all collection properties. The first line of code looks as it might for copying data between two instances of a single class and the second line of code as it might for copying data from an instance of one class to an instance of a different class.
If the recipient collection is a list and the source collection is an array, Caché copies only the data of the array (not its key values). If the recipient collection is an array and the source collection is a list, the Caché generates key values for the recipient array; these key values are integers based on the position of the item in the source list.
Note:
There is no way to copy the OREF from one collection to another. It is only possible to copy the data.
Controlling the SQL Projection of Collection Properties
As described earlier in this book, a persistent class is projected as an SQL table. This section describes how list and array properties are projected by default and how you can modify those SQL projections.
Default Projection of List Properties
By default, a list property is projected to SQL as a $LIST in serialized form. This means that when you obtain such a value, you should use functions suitable for $LIST in order to work with it. The following example obtains the value of a list property via embedded SQL and then uses suitable functions to work with the value:
 &sql(SELECT favoritecolors INTO :FavCol FROM Sample.Person WHERE id=1)
 write !, $LISTVALID(FavCol)
 for i=1:1:$LISTLENGTH(FavCol) {
    write !, $LIST(FavCol,i)
 }
 
If the list for a particular instance contains no elements, it is projected as an empty string (and not an SQL NULL value).
Default Projection of Array Properties
By default, an array property is projected as a child table, which is in the same package as the parent table. The name of this child table is as follows:
tablename_fieldname
Where
For example, a Person class with an array property called Siblings has a projection as a child table called “Person_Siblings”.
The child table contains the following three columns:
Continuing the example of the Person class with an array property called Siblings, the projection of Person includes a Person_Siblings child table with the following entries:
Sample Projection of an Array Property
Person (ID) element_key Siblings
10 C Claudia
10 T Tom
12 B Bobby
12 C Cindy
12 G Greg
12 M Marsha
12 P Peter
If an instance of the parent class holds an empty collection (one that contains no elements), the ID for that instance does not appear in the child table, such as the instance above where ID equals 11.
Notice that there is no Siblings column in the parent table.
For the column(s) containing the array members, the number and contents of the column(s) depend on the kind of array:
Together, the ID of each instance and the identifier of each array member comprise a unique index for the child table. Also, if a parent instance has no array associated with it, it has no associated entries in the child table.
Note:
A serial object property is projected to SQL in the same way, by default.
Important:
When a collection property is projected as an array, there are specific requirements for any index you might add to the property. See Indexing Collections in Caché SQL Optimization Guide. For an introduction to indices in Caché persistent classes, see the chapter Other Options for Persistent Classes.”
Alternative Projections of Collections
This section discusses the STORAGEDEFAULT, SQLTABLENAME, and SQLPROJECTION property parameters, which affect how collection properties are stored and projected to SQL.
STORAGEDEFAULT Parameter
You can store a list property as a child table, and you can store an array property as a $LIST. In both cases, you specify the STORAGEDEFAULT parameter of the property:
Important:
The STORAGEDEFAULT property parameter affects how the compiler generates storage for the class. If the class definition already includes a storage definition for the given property, the compiler ignores this property parameter.
SQLTABLENAME Parameter
If a collection property is projected as a child table, you can control the name of that table. To do so, specify the SQLTABLENAME parameter of the property. For example:
Property MyArray As array Of %String(SQLTABLENAME = "MyArrayTable");

Property MyList As list Of %Integer(SQLTABLENAME = "MyListTable", STORAGEDEFAULT = "array");
The SQLTABLENAME parameter has no effect unless the property is projected as a child table.
SQLPROJECTION Parameter
By default, if a collection property is stored as a child table, it is also projected as a child table, but it is not available in the parent table. To make such a property also available in the parent table, specify the SQLPROJECTION parameter of the property as "table/column"
For example, consider the following class definition:
Class Sample.Sample Extends %Persistent
{

Property Property1 As %String;

Property Property2 As array Of %String(SQLPROJECTION = "table/column");

}
The system generates two tables for this class: Sample.Sample and Sample.Sample_Property2
The table Sample.Sample_Property2 stores the data for the array property Property2, as in the default scenario. Unlike the default scenario, however, a query can refer to the Property2 field in the Sample.Sample table. For example:
SAMPLES>>SELECT Property2 FROM Sample.Sample where ID=7
13.     SELECT Property2 FROM Sample.Sample where ID=7
 
Property2
"1     value 12       value 23       value 3"
The SELECT * query, however, does not return the Property2 field:
SAMPLES>>SELECT * FROM Sample.Sample where ID=7
14.     SELECT * FROM Sample.Sample where ID=7
 
ID      Property1
7       abc
Note:
There are other possible values of the SQLPROJECTION property parameter, but those values have an effect only in MV-enabled classes.
Creating and Using Stand-Alone Collections
The following classes are meant for use as collections that are not class properties:
To create a stand-alone collection, call the %New() method of the suitable class to obtain an instance of that class. Then use methods of that instance to add elements and so on. For example:
 set mylist=##class(%ListOfDataTypes).%New()
 do mylist.Insert("red")
 do mylist.Insert("green")
 do mylist.Insert("blue")
 write mylist.Count()
 
These classes provide methods with many of the same names as the other collection classes. For details, see the class reference.