ストリームを使用した作業
ストリームは、大量のデータ (文字列長の制限値より長い) を保存する方法を提供します。ストリーム・プロパティは、あらゆるオブジェクト・クラスで定義できます。また、メソッドの引数や返り値として使用するなど、他の用途にスタンドアロンのストリーム・オブジェクトを定義することもできます。ここでは、ストリームおよびストリーム・プロパティについて説明します。
ストリーム・クラスの概要
InterSystems IRIS® Data Platform には、以下のストリーム・クラスが用意されています。
-
%Stream.GlobalCharacterOpens in a new tab — グローバル・ノードに文字データを格納するために使用します。
-
%Stream.GlobalBinaryOpens in a new tab — グローバル・ノードにバイナリ・データを格納するために使用します。
-
%Stream.FileCharacterOpens in a new tab — 外部ファイルに文字データを格納するために使用します。
-
%Stream.FileBinaryOpens in a new tab — 外部ファイルにバイナリ・データを格納するために使用します。
-
%Stream.TmpCharacterOpens in a new tab — 文字データを保持するためのストリームが必要だが、そのデータを保存する必要はない場合に使用します。
-
%Stream.TmpBinaryOpens in a new tab — バイナリ・データを保持するためのストリームが必要だが、そのデータを保存する必要はない場合に使用します。
これらのクラスはすべて、共通のストリーム・インタフェースを定義する %Stream.ObjectOpens in a new tab を継承します。
%Library パッケージにもストリーム・クラスが含まれていますが、こちらは非推奨です。このクラス・ライブラリには追加のストリーム・クラスが含まれていますが、一般的な使用を目的としたものではありません。
ストリーム・クラスはオブジェクト・クラスです。したがって、ストリームはオブジェクトになります。
これらのクラスのメソッドの多くは、ステータス値を返します。すべての場合において、詳細はクラス・リファレンスを参照してください。メソッドがステータス値を返す場合、コードで、戻り値をチェックして適切に処理する必要があります。同様に、%Stream.FileCharacterOpens in a new tab および %Stream.FileBinaryOpens in a new tab に対して、Filename プロパティを設定した場合は、%objlasterror を調べてエラーをチェックする必要があります。
ストリーム・プロパティの定義
InterSystems IRIS は、バイナリ・ストリームと文字ストリームの両方をサポートします。バイナリ・ストリームは、%BinaryOpens in a new tab タイプと同種のデータを含み、写真などの非常に大規模なバイナリ・オブジェクトを想定しています。同様に、文字ストリーム は、%StringOpens in a new tab タイプと同種のデータを含み、大量のテキストの保存を目的としています。文字ストリームは、文字列と同様に、クライアント・アプリケーション内で Unicode 変換を受ける可能性があります。
ストリーム・データは、ストリーム・プロパティの定義に応じて、外部ファイルまたは InterSystems IRIS グローバルに保存できます (一切保存しないようにすることもできます)。
-
%Stream.FileCharacterOpens in a new tab クラスおよび %Stream.FileBinaryOpens in a new tab クラスは、外部ファイルとして保存されたストリームに対して使用されます。
-
%Stream.GlobalCharacterOpens in a new tab クラスおよび %Stream.GlobalBinaryOpens in a new tab クラスは、グローバルとして保存されたストリームに対して使用されます。
-
%Stream.TmpCharacterOpens in a new tab クラスおよび %Stream.TmpBinaryOpens in a new tab クラスは、保存する必要のないストリームに使用します。
最初の 4 つのクラスは、オプションで LOCATION パラメータを使用して既定のストレージ場所を指定できます。
以下の例では、JournalEntry クラスに 4 つのストリーム・プロパティ (最初の 4 つのストリーム・クラスにつき 1 つずつ) が含まれ、そのうち 2 つについて既定のストレージ場所を指定しています。
Class testPkg.JournalEntry Extends %Persistent
{
Property DailyText As %Stream.FileCharacter;
Property DailyImage As %Stream.FileBinary(LOCATION = "C:/Images");
Property Text As %Stream.GlobalCharacter(LOCATION = "^MyText");
Property Picture As %Stream.GlobalBinary;
}
この例では、DailyImage のデータは C:/Images ディレクトリのファイル (ファイル名は自動的に生成されます) に保存されますが、Text プロパティのデータは ^MyText という名前のグローバルに保存されます。
ストリーム・インタフェースの使用法
すべてのストリームは、ストリームに含まれるデータの操作に使用されるメソッドおよびプロパティのセットを継承します。次のセクションでは多用するメソッドとプロパティの一覧を示し、それらの具体的な使用例をそれ以降のセクションで示します。項目は以下のとおりです。
これらのクラスのメソッドの多くは、ステータス値を返します。すべての場合において、詳細はクラス・リファレンスを参照してください。メソッドがステータス値を返す場合、コードで、戻り値をチェックして適切に処理する必要があります。同様に、%Stream.FileCharacterOpens in a new tab および %Stream.FileBinaryOpens in a new tab に対して、Filename プロパティを設定した場合は、%objlasterror を調べてエラーをチェックする必要があります。
一般的に使用されるストリーム・メソッドおよびストリーム・プロパティ
一般的に使用されるメソッドには以下のものがあります。
-
Read() — ストリームの現在の位置から、指定された数の文字を読み取ります。
-
Write() — 現在の位置から、ストリームにデータを追加します。この位置がストリームの末尾に設定されない場合は、既存のデータを上書きします。
-
Rewind() — ストリームの先頭に移動します。
-
MoveTo() — ストリーム内の特定の位置に移動します。
-
MoveToEnd() — ストリームの末尾に移動します。
-
CopyFrom() — コピー元のストリームの内容を、このストリームにコピーします。
-
NewFileName() — %Stream.FileCharacterOpens in a new tab または %Stream.FileBinaryOpens in a new tab プロパティのファイル名を指定します。
一般的に使用されるプロパティには以下のものがあります。
-
AtEnd — Read がデータ・ソースの末尾に到達すると true に設定されます。
-
Id — %Location で指定したエクステントの範囲でストリームのインスタンスを特定する一意の識別子。
-
Size — ストリームの現在のサイズ (ストリームのタイプに応じて、バイト数または文字数で表されます)。
個々のストリーム・メソッドおよびプロパティの詳細は、このトピックの始めに示したクラスの "インターシステムズ・クラス・リファレンス" のエントリを参照してください。
ストリームのインスタンス化
ストリーム・クラスをオブジェクト・プロパティとして使用する場合、ストリームを含むオブジェクトをインスタンス化すると、ストリームが暗黙的にインスタンス化されます。
ストリーム・クラスをスタンドアロン・オブジェクトとして使用する場合は、%New() メソッドを使用してストリームをインスタンス化します。
ストリーム・データの読み取りと書き込み
ストリーム・インタフェースの中核は、メソッド Read()、Write()、Rewind() と、プロパティ AtEnd および Size です。
以下の例では、一度に 100 文字ずつ Person.Memo ストリームのデータを読み取り、それをコンソールに書き込みます。len の値は参照によって渡され、毎回 Read の前に 100 にリセットされます。Read メソッドは len で指定された数の文字を読み取ろうとし、次に len を実際に読み取った実際の文字数に設定します。
Do person.Memo.Rewind()
While (person.Memo.AtEnd = 0) {
Set len = 100
Write person.Memo.Read(.len)
}
同様に、ストリームにデータを書き込むことができます。
Do person.Memo.Write("This is some text. ")
Do person.Memo.Write("This is some more text.")
変換テーブルの指定
タイプ %Stream.FileCharacterOpens in a new tab のストリームをロケールのネイティブ文字セット以外の文字セットで読み取ったり書き込んだりする場合は、ストリームの TranslateTable プロパティを設定する必要があります。"変換テーブル" のリファレンス・ページを参照してください。
ストリーム間のデータのコピー
すべてのストリームは CopyFrom() メソッドを含み、1 つのストリームが別のストリームからのデータを取り入れることができます。これを使用して、例えばストリーム・プロパティにファイルのデータをコピーできます。その場合、一方では %Library.FileOpens in a new tab クラスを使用します。これはオペレーティング・システム・コマンドのラッパであり、このクラスを使用することでファイルをストリームとして開くことができます。その場合のコードは以下のようになります。
// open a text file using a %Library.File stream
Set file = ##class(%File).%New("\data\textfile.txt")
Do file.Open("RU") // same flags as the OPEN command
// Open a Person object containing a Memo stream
// and copy the file into Memo
Set person = ##class(Person).%New()
Do person.Memo.CopyFrom(file)
Do person.%Save() // save the person object
Set person = "" // close the person object
Set file = "" // close the file
以下のように、Set コマンドを使用してデータをストリームにコピーすることもできます。
Set person2.Memo = person1.Memo
ここで、Person クラスの Memo プロパティはストリームの OREF を保持します。このコマンドは person1.Memo の内容を person2.Memo にコピーします。
ストリーム・データの挿入
一時ストリーム・クラス (そのデータは保存できない) とは別に、ストリームには一時的なストレージ場所と永続的なストレージ場所の両方があります。すべての挿入は一時的な場所に格納され、ストリームを保存する場合にのみ永続的に格納されます。ストリームへの挿入を開始した後でその挿入操作を中止すると、永続的な場所に保存されているデータは変更されません。
ストリームを作成する場合、挿入から開始し、MoveToEnd() を呼び出し、一時的なストリーム・データへの付加を継続します。ストリームを保存すると、データは永続的なストレージ場所に移動します。
以下に例を示します。
Set test = ##class(Test).%OpenId(5)
Do test.text.MoveToEnd()
Do test.text.Write("append text")
Do test.%Save()
この場合、test オブジェクトが保存されると、ストリームが永続ストレージに保存されます。
ストリームでのリテラル値の検索
ストリーム・インタフェースには、FindAt() メソッドが含まれています。このメソッドを使用すると、特定のリテラル値の場所を検索できます。このメソッドには、以下の引数があります。
method FindAt(position As %Integer, target, ByRef tmpstr, caseinsensitive As %Boolean = 0) as %Integer
以下はその説明です。
-
position は、検索を開始する位置です。
-
target は、検索するリテラル値です。
-
tmpstr は参照によって渡され、FindAt() の次回呼び出しに使用できる情報を返します。これは、同じストリームを繰り返し (最後にターゲットが見つかった位置から開始して) 検索するときに使用します。このシナリオでは、position を -1 に指定し、呼び出しのたびに tmpstr を参照で渡します。その後の継続的な FindAt() の呼び出しは、前回の呼び出しの停止位置から開始されます。
-
caseinsensitive は、大文字と小文字を区別する検索を実行するかどうかを指定します。既定では、このメソッドは大文字と小文字を区別しません。
このメソッドは、ストリームの先頭から開始したときの、この一致の位置を返します。一致する値が見つからない場合は、-1 を返します。
ストリームの保存
ストリーム・クラスをオブジェクト・プロパティとして使用する場合、ストリーム・データを含むオブジェクトを保存すると、ストリーム・データが保存されます。
ストリーム・クラスをスタンドアロン・オブジェクトとして使用する場合は、%Save() メソッドを使用してストリーム・データを保存します (一時ストリーム・クラス (%Stream.TmpCharacterOpens in a new tab および %Stream.TmpBinaryOpens in a new tab) については、このメソッドはすぐに制御を返し、データを保存しません)。
オブジェクト・アプリケーションでストリームを使用する
ストリーム・プロパティは、そのストリーム・プロパティを所有するオブジェクトによって作成される一時的なオブジェクトを介して操作されます。ストリームは、リテラル値 (ストリームを大きな文字列と見なす) の役割を果たします。2 つのオブジェクト・インスタンスは同じストリームを参照できません。
以下のクラス定義では、Person クラスに、ストリーム・プロパティである Memo プロパティがあります。
Class testPkg.Person Extends %Persistent
{
Property Name As %String;
Property Memo As %Stream.GlobalCharacter;
}
以下の ObjectScript コードの断片は新しい Person オブジェクトを作成し、Memo ストリームを暗黙的にインスタンス化します。その後、いくつかのテキストをストリームに書き込みます。
// create object and stream
Set p = ##class(testPkg.Person).%New()
Set p.Name = "Mo"
Do p.Memo.Write("This is part one of a long memo. ")
Do p.Memo.Write("This is part two of a long memo. ")
Do p.Memo.Write("This is part three of a long memo. ")
Do p.Memo.Write("This is part four of a long memo. ")
Do p.%Save()
Set id = p.%Id() // remember ID for later
Set p = ""
以下のコードの断片は Person オブジェクトを開いた後、ストリームの内容を書き込みます。オブジェクトを開くと、ストリーム・プロパティの現在の位置がストリームの先頭に設定されます。このコードでは、例示を目的として Rewind() メソッドを使用しています。
// read object and stream
Set p = ##class(testPkg.Person).%OpenId(id)
Do p.Memo.Rewind() // not required first time
// write contents of stream to console, 100 characters at a time
While (p.Memo.AtEnd = 0) {
Set len = 100
Write p.Memo.Read(.len)
}
Set p = ""
ストリーム・プロパティの内容を置き換える場合は、ストリームを巻き戻してから (ストリームの現在の位置が先頭になっていない場合)、Write() メソッドを使用して新しいデータをストリームに書き込みます。set p.Memo = ##class(%Stream.GlobalCharacter).%New() のように、%New() メソッドを使用して新しいストリーム・オブジェクトをインスタンス化し、それをストリーム・プロパティに割り当てないでください。この場合、古いストリーム・オブジェクトが孤立した状態でデータベース内に残ります。
gzip ファイルに使用するストリーム・クラス
%StreamOpens in a new tab パッケージでは、特殊なストリーム・クラス %Stream.FileBinaryGzipOpens in a new tab と %Stream.FileCharacterGzipOpens in a new tab も定義されています。このクラスは、gzip ファイルを対象とする読み込みと書き込みに使用できます。これらは前述の、同じインタフェースを使用します。以下の点に注意してください。
-
これらのクラスについては、圧縮されていないサイズを Size プロパティが返します。Size プロパティにアクセスすると、InterSystems IRIS は、ファイルのサイズを計算するためにデータを読み込みます。これは、負荷の高い操作になることがあります。
-
Size プロパティにアクセスすると、InterSystems IRIS はストリームを先頭の位置に巻き戻します。
SQL および ODBC へのストリーム・プロパティのプロジェクション
永続クラスは、SQL テーブルとして投影されます。このようなクラスについては、文字列ストリーム・プロパティとバイナリ・ストリーム・プロパティが、BLOB (binary large objects) として SQL (および ODBC クライアント) に投影されます。
ストリーム・プロパティは、ODBC タイプの LONG VARCHAR (または LONG VARBINARY) で投影されます。ODBC ドライバ、または ODBC サーバは、BLOB を読み取りまたは書き込むための特別なプロトコルを使用します。通常は、手動で BLOB アプリケーションを記述する必要があります。標準レポート・ツールは、それらをサポートしません。
後続のサブセクションでは、SQL でストリーム・プロパティを使用する方法について説明します。ここでは、以下のトピックについて説明します。
ストリーム・フィールドは、SQL では以下のような制限があります。
-
WHERE 節の中では、いくつかの例外を除き、ストリーム値を使用できません。
-
ストリームを含む複数の行を UPDATE (更新)、または INSERT (挿入) できません。一行ごとに操作を行う必要があります。
-
追加できる唯一のインデックスは、SQL Search ビットマップ・インデックスです。"SQL Search のためのソースのインデックス作成" を参照してください。
SQL でのストリームの使用に関する追加の情報について、"InterSystems SQL リファレンス" の "ストリーム・データ型" も参照してください。
埋め込み SQL によるストリームの読み取り
埋め込み SQL を使用して、以下のようにストリームを読み取ることができます。
-
埋め込み SQL を使用して、ストリームの OID (オブジェクト ID) を選択します。
&sql(SELECT Memo INTO :memo FROM Person WHERE Person.ID = 12345)
これはストリームの OID を取得して、memo ホスト変数に代入します。
-
その後、ストリームを開き、通常どおりに処理します。
埋め込み SQL によるストリームの書き出し
埋め込み SQL を使用してストリームを書き込むには、複数のオプションがあります。挿入する値については、ストリームのオブジェクト参照 (OREF)、ストリームの OREF の文字列バージョン、または文字列リテラルを使用できます。
以下の例で、これらすべての方法を示します。これらの例では、ストリーム値が予期される、Prop1 という名前の列を持つ Test.ClassWStream という名前のテーブルがあるものとします。
以下の例では、オブジェクト参照を使用します。
///use an OREF
ClassMethod Insert1()
{
set oref=##class(%Stream.GlobalCharacter).%New()
do oref.Write("Technique 1")
//do the insert; this time use an actual OREF
&sql(INSERT INTO Test.ClassWStreams (Prop1) VALUES (:oref))
}
次の例では、オブジェクト参照の文字列バージョンを使用します。
///use a string version of an OREF
ClassMethod Insert2()
{
set oref=##class(%Stream.GlobalCharacter).%New()
do oref.Write("Technique 2")
//next line converts OREF to a string OREF
set string=oref_""
//do the insert
&sql(INSERT INTO Test.ClassWStreams (Prop1) VALUES (:string))
}
最後の例では、文字列リテラルを Prop1 ストリームに挿入します。
///insert a string literal into the stream column
ClassMethod Insert3()
{
set literal="Technique 3"
//do the insert; use a string
&sql(INSERT INTO Test.ClassWStreams (Prop1) VALUES (:literal))
}
文字列リテラルの最初の文字は数字にすることはできません。数字の場合、SQL は、これを OREF として解釈し、OREF として保存しようとします。このストリームは OREF ではないため、これは SQL -415 エラーになります。
ストリームの圧縮
InterSystems IRIS では、グローバルに格納したストリーム・データを圧縮して、データベースでの占有容量とジャーナル・ファイルのサイズを削減できます。
圧縮は、クラス・パラメータ COMPRESS によってストリーム・クラスで制御されます。
%Stream.TmpCharacterOpens in a new tab クラスと %Stream.TmpBinaryOpens in a new tab クラスでは、COMPRESS を 0 に設定するとストリーム・データが圧縮されません。
%Stream.GlobalCharacterOpens in a new tab クラスと %Stream.GlobalBinaryOpens in a new tab クラスでは、COMPRESS を 1 に設定すると、データが圧縮に適していない以下の状況を除き、新しいストリーム・データが自動的に圧縮されます。
-
ストリームが 1,024 文字未満の単一のチャンクに格納できる。
-
ストリームが既に圧縮されている。つまり、データの最初のチャンクが、JPEG、MP3、ZIP などの一般的な圧縮ファイル形式である。
-
ストリームの最初の 4KB を 20% 以上圧縮できない。