コレクションを使用した作業
Caché は、コレクションをサポートしています。コレクションにより、同一タイプの要素のセットを使用した作業が行えるようになります。この要素はリテラル値にしたりオブジェクトにできます。
コレクション・プロパティは、あらゆるオブジェクト・クラスで定義できます。スタンドアロンのコレクションは、メソッドの引数や返り値として使用するなど、別の用途に定義することもできます。この章では、コレクション・プロパティに重点を置いてコレクションについて説明します。以下のトピックについて説明します。
“リテラル・プロパティの定義と使用” の章、“ストリームを使用した作業” の章、“オブジェクト値プロパティの定義と使用” の章、“リレーションシップの定義と使用” の章、および “プロパティ・メソッドの使用とオーバーライド” の章も参照してください。
このドキュメントをオンラインで表示している場合は、このドキュメントの "序文" を使用すると、他のトピックをすばやく見つけることができます。
コレクションの概要
コレクションには、すべて同一タイプの個別要素のセットを格納します。リストと配列という 2 種類のコレクションがあります。
コレクション内の各項目を要素と呼びます。また、コレクション内の要素の位置をキーと呼びます。リスト・コレクションの場合は、連続する整数のキーがシステムによって生成されます。配列の場合は、キーが任意の値を保持できます。その値は要素ごとに指定できます。
Caché では、コレクション・プロパティへのインタフェースとしてコレクション・クラスのセットを使用します。これらは、%Collection パッケージに含まれるクラスです。Caché では、スタンドアロンのコレクションが必要なとき (例えば、引数としてメソッドに渡すため) に使用する、別のコレクション・クラスのセットが用意されています。これらは、%Library パッケージに含まれるクラスです。
クラスの各セットには、コレクションに項目を追加したり、コレクションの項目を削除したり、コレクションの項目を数えたりするために使用できる、メソッドとプロパティが用意されています。この章は、%Collection クラスに焦点を合わせていますが、詳細は %Library クラスの場合とほとんど同じです。
コレクション・クラスは、オブジェクト・クラスである点に注意してください。したがって、コレクションはオブジェクトになります。
コレクション・プロパティの定義
リスト・プロパティを定義するには、以下のようにプロパティを追加します。
Property MyProp as List of Type;
MyProp はプロパティ名です。Type は、データ型クラスまたはオブジェクト・クラスのどちらかです。
同様に、配列プロパティを定義するには、以下のようにプロパティを追加します。
Property MyProp as Array of Type;
例えば、以下のプロパティ定義は、%StringOpens in a new tab 値のリストです。
Property Colors As List Of %String;
もう 1 つ例を挙げると、以下のプロパティ定義は、Doctor 値の配列です。Doctor はオブジェクト・クラスの名前です。
Property Doctors As Array Of Doctor;
内部的に、Caché は %Collection パッケージに含まれるクラスを使用して、以下に示すようなプロパティを表します。
-
%Collection.ListOfDTOpens in a new tab (リスト要素がデータ型クラスの場合)
-
%Collection.ListOfObjOpens in a new tab (リスト要素がオブジェクト・クラスの場合)
-
%Collection.ArrayOfDTOpens in a new tab (配列要素がデータ型クラスの場合)
-
%Collection.ArrayOfObjOpens in a new tab (配列要素がオブジェクト・クラスの場合)
つまり、コレクション項目の追加やコレクション項目の削除などには、これらのクラスのメソッドを使用するということです。この章の後半でこの方法を示します。
プロパティのタイプとして、%Collection クラスを直接使用しないでください。例えば、以下のようなプロパティ定義を作成してはいけません。
Property MyProp as %Collection.ArrayOfDT;
代わりに、このセクションの前半に示した構文を使用してください。
リスト・プロパティへの項目の追加
リスト・プロパティ (前のセクションを参照) がある場合は、以下の手順を使用してプロパティに値を指定します。
-
リスト項目がオブジェクトの場合は、そのオブジェクトを必要に応じて作成します。
-
リスト項目を、必要に応じてリストに追加します。1 つのリスト項目を追加する場合は、リスト・プロパティの Insert() インスタンス・メソッドを呼び出します。このメソッドは、以下のとおりです。
method Insert(listitem) as %Status
または、リスト・プロパティの別のメソッド (InsertAt() など) を使用します。概要については、“リスト・プロパティを使用した作業” を参照してください。
メソッドの詳細は、%Collection.ListOfDTOpens in a new tab および %Collection.ListOfObjOpens in a new tab のクラスリファレンスを参照してください。
例えば、obj が OREF であり、Colors が関連オブジェクトのリスト・プロパティだとします。この場合、以下のようにして、リスト項目を追加できます。
Do obj.Colors.Insert("Red")
Do obj.Colors.Insert("Green")
Do obj.Colors.Insert("Blue")
もう 1 つ例を挙げると、pat が OREF であり、Diagnoses が関連オブジェクトのリスト・プロパティだとします。このプロパティは、以下のように定義されています (PatientDiagnosis はクラスの名前です)。
Property Diagnoses as list of PatientDiagnosis;
この場合、以下のようにして、リスト項目を追加できます。
Set patdiag=##class(PatientDiagnosis).%New()
Set patdiag.DiagnosisCode=code
Set patdiag.DiagnosedBy=diagdoc
Set status=pat.Diagnoses.Insert(patdiag)
配列プロパティへの項目の追加
配列プロパティ (この章の前述を参照) がある場合、以下の手順を使用してプロパティに値を指定します。
-
配列項目がオブジェクトの場合は、そのオブジェクトを必要に応じて作成します。
-
配列項目を、必要に応じて配列に追加します。1 つのリスト項目を追加する場合は、配列プロパティの SetAt() インスタンス・メソッドを呼び出します。このメソッドは、以下のとおりです。
method SetAt(element, key As %String) as %Status
element は追加する要素です。key は、その要素に関連付ける配列キーです。
Important:配列キーとして使用する値には、連続する 1 対の垂直バー (||) を含めないでください。この制限は、Caché SQL のメカニズムが動作する方法によるものです。
このメソッドの詳細は、%Collection.ArrayOfDTOpens in a new tab および %Collection.ArrayOfObjOpens in a new tab のクラス・リファレンスを参照してください。(これらのクラスが同じメソッドのセットを定義していることがわかります。)
または、別のメソッドを使用します (“配列プロパティを使用した作業” を参照)。
例えば、Palette オブジェクトで、色の名前でアクセスされる RGB 値の配列に新しい色を追加する場合、以下のコードを使用します。
Do palette.Colors.SetAt("255,0,0","red")
palette は配列を含む OREF、Colors は配列プロパティの名前、“red” は値 “255,0,0” にアクセスするキーです。
リスト・プロパティを使用した作業
前述したようにリスト・プロパティを作成すると、プロパティ自体がオブジェクトになり、プロパティ定義に応じて、以下のいずれかクラスのインスタンス・メソッドを提供します。
-
%Collection.ListOfDTOpens in a new tab (リスト要素がデータ型クラスの場合)
-
%Collection.ListOfObjOpens in a new tab (リスト要素がオブジェクト・クラスの場合)
これらのクラスは、GetAt()、Find()、GetPrevious()、GetNext()、Remove() などのインスタンス・メソッドを提供します。以下の例では、これらのメソッドを使用する方法を示しています。
set p=##class(Sample.Person).%OpenId(1)
for i=1:1:p.FavoriteColors.Count() {
write !, p.FavoriteColors.GetAt(i)
}
リストは整列した情報のコレクションです。各リスト要素は、リスト内のその位置 (スロット) によって識別されます。スロットに値を設定したり、スロットにデータを挿入することができます。スロットに新規の値を設定すると、その値はリスト内に保存されます。既存のスロットに値を設定する場合、新規データは前のデータを上書きし、スロットの割り当てを変更しません。既存のスロットにデータを挿入する場合、新しいリスト項目により、それ以降のすべてのスロットのスロット番号がインクリメントされます。(2 番目のスロットに新しい項目を挿入すると、現在、2 番目のスロットに入っているデータは 3 番目のスロットに、3 番目のデータは 4 番目のスロットにというようにスライドされます。)
以下の構文を使用してスロット n のデータを変更できます。
Do oref.PropertyName.SetAt(data,n)
oref は OREF です。PropertyName は、そのオブジェクトのリスト・プロパティの名前です。また、data は実際のデータです。例えば、person.FavoriteColors は好きな色のリストであり、このリストの初期状態が “red”、“blue”、および “green” だとします。このリストの 2 番目の色を変更する (リストを “red”、“yellow”、および “green” にする) には、以下のコードを使用できます。
Do person.FavoriteColors.SetAt("yellow",2)
その他のメソッド、Find() や RemoveAt() などについては、%Collection.ListOfDTOpens in a new tab および %Collection.ListOfObjOpens in a new tab のクラス・リファレンスを参照してください。
配列プロパティを使用した作業
前述したように配列プロパティを作成すると、プロパティ自体がオブジェクトになり、プロパティ定義に応じて、以下のいずれかクラスのインスタンス・メソッドを提供します。
-
%Collection.ArrayOfDTOpens in a new tab (配列要素がデータ型クラスの場合)
-
%Collection.ArrayOfObjOpens in a new tab (配列要素がオブジェクト・クラスの場合)
これらのクラスは、GetAt()、Find()、GetPrevious()、GetNext()、Remove() などのインスタンス・メソッドを提供します。詳細は、これらのクラスのクラスリファレンスを参照してください。リスト・クラスの場合とは、詳細が同じではない点に注意してください。
コレクション・データのコピー
一方のコレクションにある項目を他方のコレクションにコピーするには、コピー先のコレクションの設定を、コピー元のコレクションの設定と同じものにします。 これにより、コピー元の内容が、コレクション自身の OREF ではなく、コピー先にコピーされます。 以下は、このコマンドの例です。
Set person2.Colors = person1.Colors
Set dealer7.Inventory = owner3.cars
ここで、person2、person1、dealer7、および owner3 は、すべてクラスのインスタンスで、Colors、Inventory、および cars は、すべてコレクションのプロパティです。このコードの 1 行目は、同じクラスにある 2 つのインスタンス間でデータをコピーし、2 行目は、互いに異なるクラスにあるインスタンスの間でデータをコピーします。
コピー先のコレクションがリストで、コピー元のコレクションが配列の場合、Caché では、配列のデータのみがコピーされます (キー値はコピーされません)。 コピー先のコレクションが配列で、コピー元のコレクションがリストの場合、Caché では、コピー先の配列にキー値が生成されます。このキー値は、コピー元リストの項目の位置に基づいた整数になります。
コレクションの間で OREF をコピーする方法はありません。データのコピーのみが可能です。
コレクション・プロパティの SQL プロジェクションの制御
このドキュメントで前述したように、永続クラスは SQL テーブルとして投影されます。このセクションでは、リスト・プロパティと配列プロパティが既定で投影される方法と、そのような SQL プロジェクションを変更する方法について説明します。
リスト・プロパティの既定のプロジェクション
既定では、リスト・プロパティは、シリアル化形式の $LIST として SQL に投影されます。つまり、そのような値を取得して作業するには、$LIST に適した関数を使用する必要があるということです。以下の例では、リスト・プロパティの値を埋め込み SQL で取得し、その値の操作に適した関数を使用しています。
&sql(SELECT favoritecolors INTO :FavCol FROM Sample.Person WHERE id=1)
write !, $LISTVALID(FavCol)
for i=1:1:$LISTLENGTH(FavCol) {
write !, $LIST(FavCol,i)
}
特定のインスタンスのリストが何も要素を含まない場合、これは空文字列として投影されます (SQL NULL 文字列ではありません)。
配列プロパティの既定のプロジェクション
既定では、配列プロパティは子テーブルとして投影されます。この子テーブルは親テーブルと同じパッケージ内にあります。この子テーブルの名前は、以下のようになります。
tablename_fieldname
以下はその説明です。
-
tablename は親クラスの SqlTableName です (指定されている場合)。SqlTableName が指定されていない場合、親クラスの短い名前になります。
-
fieldname は、配列プロパティの SqlFieldName です (指定されている場合)。SqlTableName が指定されていない場合、配列プロパティの名前になります。
例えば、Siblings と呼ばれる配列プロパティを持つ Person クラスでは、“Person_Siblings” と呼ばれる子テーブルとして投影されます。
子テーブルには、以下の 3 列が含まれます。
-
親クラスの対応するインスタンスの ID を含む列。この列の名前は、配列を含むクラスの名前となります (例では Person)。
-
各配列メンバの識別子を含む列。名前は常に element_key です。
-
クラスの全インスタンスの配列メンバを含む列。名前は、配列プロパティの名前です (例では Siblings)。
Siblings という配列プロパティを持つ Person クラスの例では、Person のプロジェクションには以下のようなエントリを持つ子テーブル Person_Siblings が組み込まれます。
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 |
上の例で ID = 11 のインスタンスのように、親クラスのインスタンスが、空のコレクション (何も要素を含まないコレクション) を保持する場合、そのインスタンスの ID は子テーブルに表示されません。
親テーブルには、Siblings 列は含まれません。
配列メンバを含む列の数とコンテンツは、配列の種類によって異なります。
-
データ型プロパティの配列は、1 列のデータとして投影されます。
-
参照プロパティの配列は、1 列のオブジェクト参照として投影されます。
-
埋め込みオブジェクトの配列は、子テーブル内の複数列として投影されます。これらの列の構造については、“埋め込みオブジェクト・プロパティ” を参照してください。
また、各インスタンスの ID と各配列メンバの識別子は、子テーブルの一意のインデックスを構成します。親インスタンスが関連する配列を持っていない場合、子テーブルには関連するエントリがありません。
既定では、シリアル・オブジェクト・プロパティは、同じ方法で SQL に投影されます。
コレクション・プロパティが配列として投影される際には、プロパティに追加する可能性がある任意のインデックスに対して固有の要件があります。"Caché SQL 最適化ガイド" の “コレクションのインデックス作成” を参照してください。Caché 永続クラスのインデックスの概要は、“永続クラスのその他のオプション” の章を参照してください。
コレクションの代替のプロジェクション
このセクションでは、STORAGEDEFAULT、SQLTABLENAME、および SQLPROJECTION の各プロパティ・パラメータを説明します。これらはコレクション・プロパティの格納方法と SQL への投影方法に影響を与えます。
STORAGEDEFAULT パラメータ
リスト・プロパティは子テーブルとして格納できます。また、配列プロパティは $LIST として格納できます。どちらの場合も、そのプロパティの STORAGEDEFAULT パラメータを指定します。
-
リスト・プロパティの場合、既定では、STORAGEDEFAULT が "list" になります。STORAGEDEFAULT に "array" を指定すると、そのプロパティは格納され、子テーブルとして投影されます。以下に例を示します。
Property MyList as list of %String (STORAGEDEFAULT="array");
結果のプロジェクションの詳細は、“配列プロパティの既定のプロジェクション” を参照してください。
-
配列プロパティの場合、既定では、STORAGEDEFAULT が "array" になります。STORAGEDEFAULT に "list" を指定すると、そのプロパティは格納され、$LIST として投影されます。以下に例を示します。
Property MyArray as array of %String (STORAGEDEFAULT="list");
結果のプロジェクションの詳細は、“リスト・プロパティの既定のプロジェクション” を参照してください。
STORAGEDEFAULT プロパティ・パラメータは、コンパイラがクラスのストレージを生成する方法に影響します。クラス定義に指定のプロパティのストレージ定義が含まれていた場合、このプロパティ・パラメータはコンパイラによって無視されます。
SQLTABLENAME パラメータ
コレクション・プロパティが子テーブルとして投影される場合は、そのテーブルの名前を制御できます。そのためには、そのプロパティの SQLTABLENAME パラメータを指定します。以下に例を示します。
Property MyArray As array Of %String(SQLTABLENAME = "MyArrayTable");
Property MyList As list Of %Integer(SQLTABLENAME = "MyListTable", STORAGEDEFAULT = "array");
SQLTABLENAME パラメータは、プロパティが子テーブルとして投影されていない場合には効果がありません。
SQLPROJECTION パラメータ
既定では、コレクション・プロパティは子テーブルとして格納され、子テーブルとして投影もされますが、親テーブルでは使用できません。そのプロパティを親テーブルでも使用できるようにするには、プロパティの SQLPROJECTION パラメータを "table/column" と指定します。
例えば、以下のクラス定義について考えてみます。
Class Sample.Sample Extends %Persistent
{
Property Property1 As %String;
Property Property2 As array Of %String(SQLPROJECTION = "table/column");
}
システムは、クラスで 2 つのテーブル (Sample.Sample および Sample.Sample_Property2) を生成します。
テーブル Sample.Sample_Property2 は、既定のシナリオどおり、配列プロパティ Property2 のデータを格納します。ただし既定のシナリオとは異なり、クエリは Sample.Sample テーブル内の Property2 フィールドを参照できます。例えば、以下のようになります。
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"
ただし、SELECT * クエリは、Property2 フィールドを返しません。
SAMPLES>>SELECT * FROM Sample.Sample where ID=7
14. SELECT * FROM Sample.Sample where ID=7
ID Property1
7 abc
スタンドアロン・コレクションの作成と使用
以下のクラスは、クラス・プロパティではないコレクションとして使用することを目的としています。
-
%ListOfDataTypesOpens in a new tab (リスト要素がデータ型クラスの場合)
-
%ListOfObjectsOpens in a new tab (リスト要素がオブジェクト・クラスの場合)
-
%ArrayOfDataTypesOpens in a new tab (配列要素がデータ型クラスの場合)
-
%ArrayOfObjectsOpens in a new tab (配列要素がオブジェクト・クラスの場合)
スタンドアロン・コレクションを作成するには、適切なクラスの %New() メソッドを呼び出し、そのクラスのインスタンスを取得します。そのインスタンスのメソッドを使用して、要素の追加などを行います。以下に例を示します。
set mylist=##class(%ListOfDataTypes).%New()
do mylist.Insert("red")
do mylist.Insert("green")
do mylist.Insert("blue")
write mylist.Count()
これらのクラスは、その多くが他方のコレクション・クラスと同じ名前を持つメソッドを提供します。詳細は、クラスリファレンスを参照してください。