検索

クリアフィルター
記事
Megumi Kakechi · 2022年8月30日

各アクセス方法(ダイレクト/SQL/オブジェクトアクセス)による速度の違いについて

これは InterSystems FAQ サイトの記事です。InterSystems IRIS Data Platform(以下IRISと表記)ではマルチモデルのサポートにより、データに対して様々なアクセス手法を使用することができます。 主だったアクセス手法としてダイレクトアクセス、SQLアクセス、オブジェクトアクセスがあります。 ダイレクトアクセス は、IRISのネイティブ構造であるグローバルと呼ばれるキーバリュー型のデータに直接アクセスする方法です。 SQLアクセス は、リレーショナルデータベースシステムにアクセスするための標準言語であるSQLを使用してデータにアクセスする方法です。 オブジェクトアクセス は、オブジェクト指向言語でオブジェクトを操作するための表記法として幅広く利用されるドット記法を使用してデータにアクセスする方法です。 ダイレクトアクセスとSQLアクセスおよびオブジェクトアクセスでは、処理の抽象度が異なります。 抽象度が高くなるに伴い、内部的な処理のオーバヘッドが増加するため、単純な1スレッド単位でのアクセススピードの速さについては、ダイレクトアクセスが、SQLアクセスとオブジェクトアクセスに比較して速い場合が多いです。 しかしながら、今後SQLアクセスに関してより高速に処理できるよう様々な開発が進行中です。 その点を考慮すると段々とその性能差が縮小していくことが期待できます。 一方で、IRISではマルチコア、マルチCPU環境またはシャーディング機能を利用することでSQLクエリーを並列実行する機能があるので、並列実行が効力を発揮するケースでは、ダイレクトアクセスで処理するより、圧倒的に高速に処理できる(集計処理など)場合があります。 オブジェクトアクセスとSQLアクセスでは、条件次第で、どちらが速いとは一概に言えませんが、一般的にはSQLで処理するほうが高速です。  クエリー処理に関しては、オブジェクトアクセスではなくSQLで実装することが一般的です。 またアクセススピードの違いには、使用するプログラミング言語の違いによる影響もあります。 全てのアクセス手法においてネイティブなプログラム言語であるObjectScriptでコードを記述するのが最速であるケースが多いです。 JDBCやODBC等を使用したSQL、オブジェクトアクセスの場合には、ObjectScriptでデータベースに直接アクセスするのに比しネットワーク層を介する分オーバーヘッドが大きくなり、大量にデータをアクセスする際には処理時間に大きな差が発生します。 しかしながら他のプログラム言語でのアクセスもObjectScriptでのアクセスに比して遜色のない性能が出るように製品の改良を続けていきますので、その差は少なくなっていくことが期待できます。 速度を比較する際、異なるアクセス手法に対して、条件を同じにすることは、非常に難しいことです。 あるいは、単純な速度比較は無意味なケースもあります。 アクセススピードを考える時には、以下の視点が必要です。 例えば、データの入力処理を考えてみると、データ登録前に入力データの妥当性チェックが必要となります。 オブジェクトアクセスとSQLアクセスでは、データ保存処理時、システムによる型に基づく妥当性チェックを自動的に行います。 一方、ダイレクトアクセスでは、開発者が自分で、その処理用コードを追加する必要があります。 また、オブジェクト間に複雑な関連がある場合、オブジェクトアクセスの場合は、システムがその複雑さを理解しながら、適切に処理してくれるのに対して、SQLアクセスの場合は、処理単位が行単位のため、関連は開発者が自分で処理しなければならないケースもでてきます。 これらの事と、処理スピードとは、基本的に逆の相関関係にあり、何らかのトレードオフがつきものです。 以上のことを踏まえ、どちらを優先するか考えながら適切なアクセス手法を選択することをお勧めします。(繰り返しになりますが、マルチコア、マルチCPU、シャーディング環境では、クエリー処理に関してSQLで処理することで高速に処理できる可能性が高まります。) さらに、InterSystems製品のメリットは、各種アクセス手法を適宜使い分けることにより、アプリケーション開発を最適化できる点にあることを、ご理解いただけたら幸いです。
記事
Hiroshi Sato · 2020年11月16日

ネームスペースやデータベースを作成するコマンドラインインタフェースとAPI

これはInterSystems FAQ サイトの記事です。 Config.Configurationクラス、SYS.Databaseクラスのメソッドを使用して、ネームスペース・データベースの作成及び登録をターミナルから実行することができます。 以下はデータベースファル/CacheDB/AAA/cache.datを作成し、構成ファイル(cache.cpf)にデータベース AAA、及び、ネームスペースAAAの登録を行う一連の実行例です。*実行は、%SYSネームスペースで行って下さい。* Set Directory="/CacheDB/AAA/"Set x=$ZF(-100, "/shell", "mkdir", Directory)Set db=##Class(SYS.Database).%New()Set db.Directory=DirectorySet status=db.%Save()Set DBName="AAA"Set status=##class(Config.Configuration).AddDatabase(DBName,Directory)Set NSName=DBNameSet status=##class(Config.Configuration).AddNamespace(NSName,DBName) リモートデータベースからネームスペースを作成する場合は、以下のデータベース定義で、第三パラメータにリモートのサーバ名を指定します。 Set status=##class(Config.Configuration).AddDatabase(DBName,Directory,Server) ※クラスの詳細については、それぞれのクラスリファレンスをご参照下さい。 また、データベースの作成・登録については、^DATABASEルーチンを使用することも可能です。 このルーチンを実行すると、下記のようにオプションが表示されますので、目的によって選択します。 以下は、データベース作成の実行例です。*実行は、%SYSネームスペースで行って下さい。* %SYS>do ^DATABASE 1) Create a database2) Edit a database3) List databases4) Delete a database5) Mount a database6) Dismount a database7) Compact a database8) Show free space for a database9) Show details for a database10) Recreate a database11) Manage database encryptionOption? 1Database directory? D:\200820DS\Mgr\TEST2Directory does not exist, create it? No => yChange default database properties? No =>Dataset name of this database in the configuration: TEST2Mount TEST2 Required At Startup? No => yConfirm creation of database in d:\200820ds\mgr\test2\? Yes => yFormatting...Database in d:\200820ds\mgr\test2\ createdDataset TEST2 added to the current configuration.Database directory? ※^DATABASEルーチンの各オプションについては、以下ドキュメントページをご参照ください。 ^DATABASEについて【IRIS】^DATABASEについて
記事
Mihoko Iijima · 2021年2月12日

SQL から ObjectScript で記述したプログラムを実行して値を返す方法

これは InterSystems FAQ サイトの記事です。 以下例のクラスメソッド getLatestID() のように ObjectScript のクラスメソッドを用意します。返したい値を戻り値に指定し、SQLストアドプロシージャ(SqlProc)キーワードを指定するだけで値が返せます。 ClassMethod getLatestID() As %Integer [ SqlName = getLatestID, SqlProc ] { set latestID=$Order(^ISJ.TestClass1D(""),-1) quit latestID } 操作を試す場合は、以下のクラス定義をご準備ください。 Class ISJ.TestClass1 Extends (%Persistent, %Populate) { Property name As %String; ClassMethod getLatestID() As %Integer [ SqlName = getLatestID, SqlProc ] { set latestID=$Order(^ISJ.TestClass1D(""),-1) quit latestID } } データ自動生成を行うユーティリティクラス(%Populate)を継承しているため、Populate() メソッドを実行するとテストデータが作成できます。以下の例では、10件の ISJ.TestClass1 クラスに対応するデータを作成しています。 (USER ネームスペースに上記クラスを定義した状態での実行例) USER>do ##class(ISJ.TestClass1).Populate(10) 作成したクラスメソッドをストアド関数として実行している例は以下の通りです。 (ターミナルをSQLの実行環境に変更してから SELECT文を実行している例です) USER>do $system.SQL.Shell() SQL Command Line Shell ---------------------------------------------------- The command prefix is currently set to: <<nothing>>. Enter <command>, 'q' to quit, '?' for help. [SQL]USER>>select ISJ.getLatestID() As LatestID 1. select ISJ.getLatestID() As LatestID LatestID 10 1 Rows(s) Affected statement prepare time(s)/globals/cmds/disk: 0.0545s/33171/145091/8ms execute time(s)/globals/cmds/disk: 0.0003s/1/415/0ms cached query class: %sqlcq.USER.cls30 --------------------------------------------------------------------------- [SQL]USER>>select ID from ISJ.TestClass1 2. select ID from ISJ.TestClass1 ID 1 2 3 4 5 6 7 8 9 10 10 Rows(s) Affected statement prepare time(s)/globals/cmds/disk: 0.0481s/33891/148856/0ms execute time(s)/globals/cmds/disk: 0.0003s/11/887/0ms cached query class: %sqlcq.USER.cls31 --------------------------------------------------------------------------- [SQL]USER>> また、以下実行例のように記述すると、最新 ID の name だけ表示します。 select name from ISJ.TestClass1 WHERE ID=ISJ.getLatestID()
記事
Mihoko Iijima · 2021年3月26日

Linux のシェルスクリプトでルーチンやメソッドの戻り値を取得する方法

これは InterSystems FAQ サイトの記事です。 Linux で シェルスクリプト(shell script) からルーチンやメソッドを実行し戻り値を取得するには、iris コマンドを使用します。 詳細はドキュメント「インスタンスの接続について」 をご参照ください。 コマンド記述例は以下の通りです。 iris session インスタンス名 -U ネームスペース名 "実行ルーチン・メソッド" シェルスクリプトへの戻り値の指定は、ルーチンやメソッド終了時に指定する QUIT や RETURN コマンドに引数を指定する方法ではなく、プロセス処理終了時に %SYSTEM.Process クラスの Terminate() メソッドを使用して 特殊変数 $? に値を返す方法を利用します。 値は、0~255 を指定できます。 ルーチンでの利用例は以下の通りです。 // ルーチン名をTestとします start1() public {  hang 5 // 5秒待つ  //第2引数に指定した数値がシェルスクリプトに戻ります  set st=$system.Process.Terminate($JOB,11) } 実行例は以下の通りです。 # iris session iris -U USER "start1^Test()" # echo $? 11 クラスメソッドでの利用例は以下の通りです。 Class Test.Class1 { ClassMethod test() {  hang 5 // 5秒待つ  //第2引数に指定した数値が シェルスクリプトに戻ります  set st=$system.Process.Terminate($JOB,12) } } 実行例は以下の通りです。 # iris session iris -U USER "##class(Test.Class1).test()" # echo $? 12 関連情報もあります。以下、FAQサイトのトピックをご参照ください。 Windowsバッチでターミナルスクリプトを実行する方法を教えてください Windows のバッチスクリプトでIRISのルーチンやメソッドの戻り値を取得する方法を教えてください。
記事
Hiroshi Sato · 2021年10月5日

Webゲートウェイ/CSPゲートウェイの設定管理ページにどのクライアントからもアクセスできるようにする

これは InterSystems FAQ サイトの記事です。Web/CSPゲートウェイ管理ページは、通常クライアントマシンからアクセスできないように構成されています。 任意のクライアントからアクセスするためには以下の操作を行います。 Web/CSPゲートウェイ管理ページにアクセス可能なシステムからブラウザを起動し、管理ポータルにアクセスします。管理ポータル>システム管理>構成>の所でCSPゲートウェイ管理をクリックします。表示されるページの左ペインに表示されるデフォルトパラメータをクリックします。 システム管理マシンの所に*.*.*.*と入力し、設定を保存ボタンを押下げます。 しかしながら任意のクライアントから管理ページにアクセスできるようにするのはセキュリティの観点からはあまりお勧めできません。アクセスできるクライアントはなるべく限定することをお勧めします。クライアントアドレスの指定方法としては、以下のような指定形式があります。 [SYSTEM]System_Manager=190.8.7.4-6 上記の例は、以下の記述をより簡単にしたものです。 [SYSTEM]System_Manager=190.8.7.6, 190.8.7.5, 190.8.7.4 以下の例のように、ワイルドカードも使用できます。 [SYSTEM]System_Manager=190.8.7.* 詳細は、下記ドキュメントページをご参照ください。Webゲートウェイ管理ページでのセキュリティの考慮事項【IRIS】CSP ウェブゲートウェイ管理ページでのセキュリティの考慮事項
お知らせ
Hiroshi Sato · 2020年6月29日

障害対応などの製品修正の提供方法について

お客様のシステムで発生した障害に対処するための製品修正や社内のコードレビューで新たに発見された潜在的な問題に対応するための修正などは、問題の重要度(運用に支障をきたす、アプリケーションの動作に影響があるなど)に応じて、順次新しいメジャーバージョン、メンテナンスバージョンに取り込まれていきます。 一方で、新しいバージョンのリリースには所定の時間が必要ですので、そのリリース前に該当する修正だけを適用したいというご要望にお応えするためにInterSystemsではアドホックと呼んでいる応急パッチを提供することも可能です。 しかしながら、この対応はあくまでも個別の応急処置であり、その問題が対処されたバージョンがリリースされた後には、そのバージョンをご使用いただくことを推奨致します。 この様な個別対応を繰り返し行うと、システムの構成管理が複雑化し、管理上のコストが上昇し、思わぬシステムトラブルの遠因になることもあります。 特に複数のエンドユーザー様に同じソリューションを提供する様なケースでは、システムの提供時期等によりシステム構成が変わり、適用可能なオペレーティングシステム等が変化することに伴い、全ての適用パッチを見直す必要が出てくることも想定されます。
記事
Mihoko Iijima · 2021年5月28日

キーバリュー形式で Python / Node.js / Java から IRIS にアクセスできるテンプレート(グラフ構造によくある人物相関図を IRIS で表現しています)

開発者の皆さん、こんにちは🌂 今年は早い梅雨入りでした ☔ さて、新しい✨ 実行/開発環境テンプレートを作成しました。 Docker 🐳、docker-compose 、git がインストールされていれば、すぐにお試しいただけます。ぜひご利用ください! 今回は、ご存知の方が多いと思われる(?)某アニメの登場人物を使った人物相関図をテーマに【キーバリュー形式で IRIS に登録してグラフ構造で表示してみた】を体験できるテンプレートです(テンプレートは、Python/Node.js/Java からお試しいただける環境をご用意しています)。 人物相関図のイメージ 以下、今回のテーマについて、ビデオと文字でご紹介しています。最後までお付き合いいただければ幸いです!(ビデオは全体で 7 分 20 秒) 人物相関図と言えば、グラフデータベースをイメージされると思います。 IRIS はグラフデータベースではないのですが、IRIS ネイティブのデータの「グローバル」を利用することで、グラフデータベースと似たような構造を表現することができます。 IRIS の高パフォーマンスを支える 「グローバル」 は 40 年以上前(= InterSystems 創業)から InterSystems のコア技術であるデータベースとして提供されてきました。 「グローバル」 に対する操作方法は、現代のカテゴリに合わせるとしたら NoSQL データベースと言えます。 では、どのようにグラフデータベースのような構造を表現しているか?についてですが、グラフ構造は、ノードと辺から構成されていて、辺は 2 つのノードを結び付けるものです。 SNS の「友達」で考えると、ノードは「ユーザ」、辺は「友達関係」で表現できます。 テンプレートで使用している人物相関図では、ノードは「登場人物」、辺は「登場人物との関係」を表現しています。 人物相関図のノードと辺(エッジ) ノードと辺を、どのようにグローバル変数に設定しているでしょうか。 ノードは以下の通りです(配列には、画面表示に利用するノードの ID を設定し、右辺に人物名を登録しています)。 ^Relation("Eren")="主人公(エレン)" 辺(エッジ)は以下の通りです(グローバル変数の配列を利用して、登場人物 → 関係のある人 [ソース→ターゲット] を設定しています)。 主人公エレンは、アルミン、ミカサ、ジークと関係がある。を表現しています。 ^Relation("Eren","Armin")="" ^Relation("Eren","Mikasa")="" ^Relation("Eren","Zeke")="" 両者で関係がある場合は、さらに以下のような配列を追加します。 ^Relation("Mikasa")="エレンの幼馴染(ミカサ)" ^Relation("Mikasa","Armin")="" ^Relation("Mikasa","Eren")="" 実際に、IRIS サーバ側で記述する場合には、ObjectScript の SET コマンドを使用してグローバル変数を設定します。 set ^Relation("Eren")="主人公(エレン)" set ^Relation("Eren","Mikasa")="" set ^Relation("Eren","Armin")="" set ^Relation("Eren","Zeke")="" set ^Relation("Mikasa")="エレンの幼馴染(ミカサ)" set ^Relation("Mikasa","Armin")="" set ^Relation("Mikasa","Eren")="" 配列のサブスクリプト(括弧の中身)は、配列のノード(例では、第 1 番目と第 2 番目)毎に Unicode 昇順でソートされます。 実行後、管理ポータルなどからグローバル変数一覧を参照すると、実行順に関係なく Unicode 昇順にソートされていることを確認できます。 管理ポータルは、http://localhost:52779/csp/sys/UtilHome.csp でアクセスできます(ユーザ名:_system 、パスワード:SYS)。 ※ポート番号はご利用環境に合わせてご変更ください。 管理ポータル > [システムエクスプローラ] > [グローバル] > 左画面で「ネームスペース」USER を選択 > ^Relation の「表示」をクリック ここまでご紹介したグローバル変数に対する各言語のコード例については、テンプレート用リポジトリの以下サブディレクトリをご参照ください。 👉 https://github.com/Intersystems-jp/IRIS-NativeAPI-Template Python :jupyter のコンテナを用意します(Jupyter は 8896 ポートでアクセスできます)。 Node.js:Node 12 のコンテナを用意します(8080 ポートで確認用 Web ページを参照できます)。 Java:OpenJDK 8 のコンテナを用意します。 1) テンプレートの処理概要 各言語ごとのシンプルなコード例とコンテナで提供している内容についての解説は、以下のビデオでもご紹介しています(全体で 9 分 20 秒)。 テンプレートでは、Python / Node.js / Java 用から IRIS 用コンテナへ、キーバリュー形式でのアクセスを行うため、Native API を使用しています。 各言語で必要な irisnative モジュールを使用するための手順は、言語ごとのサブディレクトリで解説しています。 Python Node.js Java ビデオでもご紹介していますが、シンプルなコード例は以下の通りです。 Python の例 #irisnativeモジュールインポート import irisnative #IRISに接続 (ホスト名,スーパーサーバポート番号,ネームスペース,ユーザ,パスワード) connection = irisnative.createConnection("iris",1972,"user","_system","SYS") #IRISインスタンス生成 iris_native = irisnative.createIris(connection) #値設定 iris_native.set("鎧の巨人\n(ライナー)","Relation","Reiner") iris_native.set("超大型の巨人\n(ベルトルト)","Relation","Bertolt") iris_native.set(None,"Relation","Reiner","Bertolt") iris_native.set(None,"Relation","Bertolt","Reiner") #Iterator() for source,value in iris_native.iterator("Relation").items(): print(source,"-",value) for target,value in iris_native.iterator("Relation",source).items(): print(" 関係者:",target) connection.close() Node.js の例 const irisnativeapi = require('intersystems-iris-native'); //接続 let connectionInfo = {"host": "nativeapi-iris","port": 1972,"ns":"USER","user":"_SYSTEM","pwd":"SYS"}; const connection = irisnativeapi.createConnection(connectionInfo); //IRISインスタンス生成 const irisNative = connection.createIris(); //値設定 irisNative.set("鎧の巨人(ライナー)","Relation","Reiner"); irisNative.set("超大型の巨人(ベルトルト)","Relation","Bertolt"); irisNative.set(null,"Relation","Reiner","Bertolt"); irisNative.set(null,"Relation","Bertolt","Reiner"); let ite1=irisNative.iterator("Relation"); //Iterator() for ([source,data] of ite1) { console.log("source"+"-"+data); let ite2=irisNative.iterator("Relation",source); for ([target] of ite2) { console.log(" 関係者:",target); } } connection.close(); 【Java の例】 package NativeAPI; import com.intersystems.jdbc.IRIS; import com.intersystems.jdbc.IRISConnection; import com.intersystems.jdbc.IRISDataSource; import com.intersystems.jdbc.IRISIterator; public class Test{ public static void main (String[] args) { try { //接続オープンには IRISDataSourceを使う IRISDataSource ds = new IRISDataSource(); // jdbc:IRIS://ホスト名:スーパーサーバポート/ネームスペース名 ds.setURL("jdbc:IRIS://localhost:1972/user"); ds.setUser("_SYSTEM"); //ユーザ名 ds.setPassword("SYS"); //パスワード IRISConnection dbconnection = (IRISConnection) ds.getConnection(); //create irisNative object IRIS irisNative = IRIS.createIRIS(dbconnection); irisNative.set("鎧の巨人(ライナー)","Relation","Reiner"); irisNative.set("超大型の巨人(ベルトルト)","Relation","Bertolt"); irisNative.set("","Relation","Reiner","Bertolt"); irisNative.set("","Relation","Bertolt","Reiner"); IRISIterator character=irisNative.getIRISIterator("Relation"); while (character.hasNext()) { String source=character.next(); System.out.println("\n人物 = "+ source + " - 説明:"+ character.getValue()); //関係のある人を表示(^Relation) IRISIterator correlate=irisNative.getIRISIterator("Relation",source); while (correlate.hasNext()) { String target=correlate.next(); System.out.println(" 関係者 : "+ target); } } //irisの接続をClose irisNative.close(); dbconnection.close(); } catch (Exception ex) { System.out.println(ex.getMessage()); } } 2) Native API について Native API は、IRIS 内部のネイティブデータ(=グローバル変数)を直接操作できる API で Python、Node.js、Java、.NET からアクセスできます。 グローバル変数の操作には、IRIS サーバーサイドプログラミングで使用する ObjectScript を利用しますが、Native API を利用することで、ObjectScript を使用せずにお好みの言語からアクセスすることができます。 また、Native API は、グローバル変数の設定/取得の他にも、クラスメソッド、ルーチン、関数を実行することができます。 3) テンプレートの使用方法 言語ごとのサブディレクトリにある README をご参照ください。 Python Node.js Java テンプレートの例に使用した某アニメの人物相関図ですが、実は完成していません。 あの登場人物がいない!とお気づきの方、ぜひ追加いただき、よろしければ記事の返信欄に貼り付けていただければ・・。 最後までお読みいただきありがとうございました! 関連記事(NoSQLをテーマにした詳細説明付き記事):Python Native APIでNoSQLデータベースにアクセス
記事
Toshihiko Minamoto · 2023年4月26日

2022.3 のカラムナーストレージ

[Global Summit 2022](https://learning.intersystems.com/course/view.php?id=2077) または [2022.2 ローンチウェビナー](https://www.intersystems.com/resources/whats-new-in-intersystems-iris-2022-2/)の内容からよく覚えていると思いますが、InterSystems IRIS の分析ソリューションに組み込むための目覚ましい新機能をリリースしようとしています。 分析クエリを桁違いに高速化する、代替の SQL テーブルデータ格納手法である[カラムナー(列指向)ストレージ](https://learning.intersystems.com/course/view.php?id=2112)です。 もともと 2022.2 の実験的機能としてリリースされましたが、[最新の 2022.3 開発者プレビュー](https://community.intersystems.com/post/intersystems-publishes-developer-preview-5-intersystems-iris-iris-health-healthshare-health)には多数の更新が含まれているため、別途ここで簡単に説明したいと思います。 ### 簡単な要約 カラムナーストレージにあまり詳しくない方は、[こちらの簡単な紹介動画](https://learning.intersystems.com/course/view.php?id=2112)かこの件に関する [GS2022 セッション](http://learning.intersystems.com/course/view.php?id=2077)をご覧ください。 手短に言うと、新しい `$vector` データ型を用いて、列ごとに 64k チャンクでテーブルデータをエンコーディングしています。 `$vector` は、疎データと密データの両方を効率的に格納できるように、アダプティブエンコーディングスキームを利用する(現時点では)内部専用のデータ型です。 エンコーディングは、一度に 64k チャンク全体の集計、グループ化、およびフィルタを計算し、可能な場合は [SIMD 命令](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data)を利用するなどの、一連の専用の `$vector` 演算にも最適化されています。  SQL クエリ時に、これらのチャンクに対しても動作するクエリプランを作成して、これらの演算を利用します。想像できるように、従来の行単位での処理に比べ、これによってクエリを実行するための IO の量と ObjectScript 命令の数が大幅に削減されます。 もちろん、行指向における単一値演算に比べれば、個別の IO はより大きく、`$vector` 演算は多少重くなりますが、非常に大きなメリットがあります。 $vector データを処理し、チャンク全体を一連の高速な個々の演算にプッシュする実行戦略を「ベクトル化された」クエリプランと呼んでいます ### とにかく速い 最も重要なのは、すべてが高速化されたことです。 列インデックスに関するオプティマイザの理解が深まったことで、リクエストされたフィールドの一部が列インデックスまたはデータマップに格納されていない場合であっても、より多くのクエリが列インデックスを使用するようになります。 また、多くのケースで列インデックスとビットマップインデックスが組み合わせられるようになります。これは、列インデックスを既存のスキーマに追加することから始める場合に役立ちます。 新しいキットには、いくつかのクエリ処理の機能強化に対する低レベルの `$vector` 演算の最適化から並列化できるより広範な一連のベクトル化されたクエリプランまで、パフォーマンスを改善する多数の変更がスタック全体に含められています。 `INSERT .. SELECT` ステートメントなどによる特定のデータ読み込み方法も インデックスの構築にすでに使用しているバッファリングされたモデルを採用し、テーブル全体を非常に高いパフォーマンスで構築できるようになっています。 ### ベクトル化された JOIN このリリースで追加された最も注目すべき機能は、ベクトル化の方法による列データの JOIN のサポートです。 2022.2 では、クエリ内で 2 つのテーブルからデータを結合する場合、列編成のデータでも行編成のデータでも機能する堅牢な行単位の JOIN 戦略に頼っていました。 今回は、JOIN の両端が列形式で格納されている場合、新しいカーネル API を使用して、メモリ内で JOIN し、`$vector` 形式を保持することができるようになっています。 これは最も複雑なクエリであっても完全にベクトル化されたクエリプランにするもう 1 つの重要なステップです。 以下に、新しい関数を利用するクエリの例を示します。[前のデモ](https://github.com/bdeboe/isc-taxi-demo)で使用した New York Taxi データセットの自己結合を行っています。 SELECT   COUNT(*),   MAX(r1.total_amount - r2.total_amount) FROM   NYTaxi.Rides r1,   NYTaxi.Rides r2 WHERE   r1.DOLocationID = r2.PULocationID   AND r1.tpep_dropoff_datetime = r2.tpep_pickup_datetime   AND r2.DOLocationID = r1.PULocationID   AND r1.passenger_count > 2   AND r2.passenger_count > 2 このクエリは、乗客が 2 人を超えるルートのペアを検索します。2 番目のルートが最初のルートが終了する時点で同時刻に開始し、2 番目のルートが最初の開始地点に戻るルートペアです。 非常に有用な分析ではありませんが、このスキーマには実際のテーブルが 1 つしかなく、複合 JOIN キーのお陰で味気なさが少し緩和されています。 このステートメントのクエリプランには、`Apply vector operation %VHASH`(複合 JOIN キーの作成用)と `Read vector-join temp-file A` などのスニペットが含まれており、新しいベクトル化された結合が機能していることを示しています。 これは、長めのクエリプランにある小さく些細なもののように聞こえるかもしれませんが、内部では多くのスマートエンジニアリングが伴っています。このようなことをただ許可せず、スキーマレイアウトに厳しい制約を課す一般的なカラムナーデータベースベンダーがかなりの数存在しますので、ぜひ一緒にこれを楽しみましょう! :-) クエリプランがその一時ファイルの読み取りを続けると、Join 後の作業にまだ行単位の処理が残っていることに気づくかもしれません。そこで... ### 今後の予定 カラムナーストレージは 2022.3 でも「実験的」となっていますが、本番対応に近づいており、間もなくマルチテーブルクエリ用の完全なエンドツーエンドベクトル化が可能になります(訳注:2023.1では本番用としてリリースされています)。 これには、先ほど述べた JOIN 後の作業、クエリオプティマイザのより幅広いサポート、カラムナーテーブルの読み込みのさらなる高速化、共有メモリサポートなどの JOIN のさらなる機能強化などが含まれます。 要するに、今こそ、2022.3 Community Edition を使用して [New York Taxi データセット](https://github.com/bdeboe/isc-taxi-demo/)を試してみる絶好の機会です(現時点では [IPM](https://openexchange.intersystems.com/package/NY-Taxi-Demo) で、または [Docker スクリプト](https://github.com/bdeboe/isc-taxi-demo)を使用)。そうすれば、2023.1 がリリースされる頃には「Run」を押すだけで済みます! カラムナーストレージを独自のデータとクエリで使用する方法について、より具体的なアドバイスに興味のある方は、私かアカウントチームに直接ご連絡ください。[Global Summit 2023](https://www.intersystems.com/gs2023/) でお会いできるかもしれません ;-)
記事
Mihoko Iijima · 2022年1月6日

IRIS ターミナルで履歴からのコマンド実行、コマンドのショートカット作成ができるのをご存知ですか?

開発者のみなさん、あけましておめでとうございます🎍 今年もどうぞよろしくお願いします! さて、この記事では、IRIS ターミナルに(こっそり)追加された便利機能をご紹介します!(つい最近知りまして、びっくりしました) 2023/4/13 追記:Pythonシェルへ切り替えるメソッドのショートカットが追加されていたので返信欄に追記しました。 IRIS ターミナルで以前実行したコマンドを再実行する場合、上矢印キーを連打しながらコマンドを探されていると思うのですが、IRIS 2021.1 から履歴表示と、履歴番号を指定した実行ができるようになっていました! では早速、履歴(history)の使い方をご紹介します。これがあれば、もう、上矢印キーを連打せずに以前実行したコマンドを再実行できます!! まずは、履歴を作るため、いくつかコマンドを実行します。​​​ ユーザ名:_system パスワード:*** USER> USER>write $ZDATE($NOW(),16) 2022年1月6日 USER> USER>write $ZV IRIS for Windows (x86-64) 2021.1 (Build 215U) Wed Jun 9 2021 09:39:22 EDT USER> USER>write $system.Util.ManagerDirectory() c:\intersystems\irishealth\mgr\ USER> ここで、:? と入力してみます。 (なんと、オプションが表示されます!!) USER>:? :<number> Recall command # <number> :? Display help :alias Create/display aliases :clear Clear history buffer :history Display command history :unalias Remove aliases :history (または :h)を実行すると、履歴が表示されます! USER>:history 1: _system 2: write $ZDATE($NOW(),16) 3: write $ZV 4: write $system.Util.ManagerDirectory() 5: :? 6: :history コロン(:)と履歴番号を指定し、以前実行したコマンドを再実行してみます。(:4 と入力しています。) USER>:4 write $system.Util.ManagerDirectory() c:\intersystems\irishealth\mgr\ USER> 上矢印キーを連打せずに、過去のコマンドを実行できました!便利です 続いて、alias オプションを使うとコマンドのショートカットが作成できるようですので、それも試してみます。 以下の実行例では、TESTネームスペースに移動した後、IRIS ターミナルをSQL実行モードに変更し、SQLを実行しています。 USER>set $namespace="test" TEST>do $system.SQL.Shell() SQL Command Line Shell ---------------------------------------------------- The command prefix is currently set to: <<nothing>>. Enter <command>, 'q' to quit, '?' for help. [SQL]TEST>>select * from Training.Person 1. select * from Training.Person ID Email Name 1 yamada@majorcorp.com 山田たろう 1 Rows(s) Affected statement prepare time(s)/globals/cmds/disk: 0.0462s/3307/171368/2ms execute time(s)/globals/cmds/disk: 0.0002s/2/675/0ms cached query class: %sqlcq.TEST.cls166 --------------------------------------------------------------------------- [SQL]TEST>>quit TEST> なかなか覚えにくいコマンドに対して、alias を設定しておくと便利そうですので、ターミナルを SQL の実行環境に変更する do $system.SQL.Shell() を設定してみます。 設定方法は以下の通りです。 :alias 名称 コマンド $system.SQL.Shell() を sqlmode の名称で alias に設定する例は以下の通りです。 TEST>:alias sqlmode do $system.SQL.Shell() TEST>:alias sqlmode do $system.SQL.Shell() 早速、設定した alias を使ってみましょう! TEST>:sqlmode do $system.SQL.Shell() SQL Command Line Shell ---------------------------------------------------- The command prefix is currently set to: <<nothing>>. Enter <command>, 'q' to quit, '?' for help. [SQL]TEST>>quit TEST> 無事、実行できました! (alias 名は省略できるので、上記登録の場合 :s でも実行できるようでした。) メモ:2021.2プレビュー版では、do $system.SQL.Shell() が alias 名 sql で事前に登録されているため、:sql で SQL 実行モードに切り替えできました。 履歴(history)や alias の例については、ドキュメントにも記載がありますので、併せてご参照ください! 前のコマンドの繰り返し いつの間にか、:py も追加されていました。(Pythonシェルへ切り替わります。) USER>:py Python 3.9.5 (default, Jan 13 2023, 15:20:22) [MSC v.1927 64 bit (AMD64)] on win32 Type quit() or Ctrl-D to exit this shell. >>> import datetime >>> datetime.datetime.now() datetime.datetime(2023, 4, 13, 11, 16, 43, 487481) >>> もちろん、以下メソッドの実行でも切り替わります。 do ##class(%SYS.Python).Shell()
記事
Shintaro Kaminaka · 2020年9月4日

IRIS for Health 上でFHIR リポジトリ+OAuth2 認可サーバ/リソースサーバ構成を構築する パート1

開発者の皆さん、こんにちは。 今回の記事ではFHIRと組み合わせて使用されるケースが増えてきている、権限の認可(Authorization)を行うためのOAuth2について取り上げます。 まずこのパート1では、IRIS for HealthおよびApacheのDockerコンテナの起動と、IRIS for Health上で、OAuth2認可サーバ機能を構成し、REST開発ツールPostmanからアクセスし、アクセストークンを取得する方法について解説します。さらにパート2以降では、IRIS for HealthにFHIRリポジトリ機能を追加し、OAuth2リソースサーバ構成を追加して、Postmanからアクセストークンを使用したFHIRリクエストの実行方法まで解説します。 InterSystems製品のOAuth2機能の解説については、すでにいくつかの記事が開発者コミュニティ上に公開されていますが、今回は改めて最新バージョンでの構成方法を解説したいと思います。InterSystems IRIS Open Authorization Framework(OAuth 2.0)の実装 - パート1 今回の記事では、最新のInterSystems IRIS for Health 2020.3 Preview Editionを使用します。実際にこの記事を元にして、環境を構築してみようと思われた方は必ずこのバージョン以降のキットを使用してください。一部機能がこのバージョン以前の製品には含まれていません。 事前準備 まずは事前準備です。セキュアな環境を構築するために、色々と準備することが多いです。 IRIS for Health 2020.3 Preview EditionはDockerコンテナバージョンだけが提供されています。(InterSystems Docker Hub/IRIS for Health)また、OAuth2構成を行うためには、WebサーバおよびSSL構成を行う必要があります。今回はApacheを使用します。Apache上でSSL構成を行う場合、SSL構成の証明書は正しくサーバのホスト名と一致している必要があります。この点を注意してください。 intersystems-jp GitHub リポジトリからのサンプルファイルの取得 インターシステムズ開発者コミュニティ専用のGitHubリポジトリに今回の構成で使用するサンプルの docker-compose.yml/Dockerfile等が公開されています。まずは以下のコマンドでご利用の環境にこのファイルを展開してください。(この記事の添付ファイルにも追加されているのでそちらからでもOKです。)このdocker-compose.yml/Dockerfile等のファイルは、OpenExchangeに公開されているiris-webgateway-exampleアプリケーションを参考に作成しています。 git clone https://github.com/Intersystems-jp/IRIS4H-OAuth2-handson.git 使用するキットに合わせた構成の変更 このdocker-compose.ymlファイルでは、IRIS for Healthのコンテナと、docker buildコマンドでbuildされるApache(httpd)のコンテナの二つが起動するように構成されています。GitHubで配布しているdocker-compose.ymlファイルでは、IRIS for Health Community Edition Preview Edition(2020.3.200.0) を指定しています。Community Editionはインターシステムズ製品の評価のために利用していただくことができます。 iris: image: store/intersystems/irishealth-community:2020.3.0.200.0 異なるバージョン(正式リリースバージョンやより新しいバージョン)を使用する場合は、この部分の指定を変更してください。 Apacheの方のコンテナはDockerfileの内容でbuildされますが、その際にApacheからIRISに接続するためのWebGatewayのキットが必要になります。キットの入手方法についてはインターシステムズパートナーの方々はWRCのキットダウンロードサイトするか、またはWRCサポートセンターへお問い合わせください。それ以外の方々はお手数ですが、こちらのアドレスまでお問い合わせください。 入手した製品に合わせてDockerfileの以下の箇所を変更します。ホストマシンのOSがWindows/Ubuntu/CentOSいずれであっても、ベースとなるhttpdのコンテナOSがDebianであるため、platformはlnxubuntux64の指定となります。 ARG version=2020.3.0.200.0 ARG platform=lnxubuntux64 ADD WebGateway-${version}-${platform}.tar.gz /tmp/ SSL証明書の用意 次にSSL証明書を用意します。OAuth2 認可アクセス時には、そのWebサーバで設定されているSSL証明書がアクセスしているURLと一致しているか確認を行います。公式な証明書でなくても、openssl等を使用した証明書作成で問題はありません。証明書作成時にCommon Nameにホスト名を入力してください。 また、作成した証明書は起動時に自動で読み込むため、パスフレーズが不要なファイルに変更しておく必要があります。以下のコマンドを参考にしてください。 $ openssl rsa -in cert.key.org -out cert.key 作成された CRTファイルおよび、KEYファイルをそれぞれ server.crt / server.key というファイル名で、Dockerfileと同じディレクトリにおいてください。 また、Apache Webサーバで利用する以外にも、OAuth2の設定でSSL証明書が必要になります。これらはホスト名の入力等は必要がありませんが、3セット作成しておいてください。(以降の設定ではauth.cer/auth.key , client.cer/client.key , resserver.cer/resserver.key として登場します。 ) dockerビルド と dockerコンテナの起動 これでようやく準備完了です!今ディレクトリには、DLした4つのファイル以外に、Webゲートウェイのインストールキット、そして二つのSSL証明書がある状態です。各ファイルのアクセス・実行権限等にもご注意ください。(例えば私は、webgateway-entrypoint.shに実行許可を追加しました。) docker-compose build docker-compose up -d 起動したら、docker ps コマンドで二つのコンテナが起動していることを確認します。 ApacheのコンテナIMAGE名:<directoryname>_web IRIS for Healthのコンテナ:store/intersystems/irishealth-community:2020.3.0.200.0(あるいはそれぞれ指定したIMAGE) 次は、以下の3つの方法で管理ポータルにアクセスしてみてください。最後の3番目の方法でうまくアクセスできれば、Apache Webサーバ経由のSSL構成は成功です! http://<hostname>:52773/csp/sys/UtilHome.csp : このURLはIRIS コンテナのPrivate Apache経由のアクセスです。構成したApacheは経由しません。 http://<hostname>/csp/sys/UtilHome.csp : このURLは構成したApacheを経由して管理ポータルにアクセスしています。 https://<hostname>/csp/sys/UtilHome.csp : このURLは構成したApache経由でSSL接続を使用して管理ポータルにアクセスしています。 SSL構成の作成 それでは、IRIS for Healthが起動し、管理ポータルにアクセスできるようになったので、最後の準備としてSSL構成を作成しましょう。 管理ポータル→システム管理→セキュリティ→SSL/TLS構成 と進み、先ほど作成した3ペアの証明書キーを使って、SSLの構成を3つ作成しておいてください。 名前は自由に決めることができますが、この記事では過去のOAuth2関連記事にならって、SSL4AUTH/SSL4CLIENT/SSL4RESSERVER としています。 *ホストとコンテナのディレクトリ共有について docker-composeファイルに記載されている以下のvolumes 指定によって、ホストの現在のディレクトリ位置=コンテナ内の /ISC ディレクトリ となっています。上記の設定等で証明書ファイルを指定する場合は、このディレクトリを活用してください。 volumes: - .:/ISC このディレクトリには、ファイルだけでなくIRISのデータベースファイルや構成ファイルも配置されます。詳しくはドキュメント「永続インスタンス・データを保存するための永続的な %SYS」をご覧ください。 IRIS for HealthでOAuth2の構成 いよいよIRIS for HealthにOAuth2を使ってアクセスするための具体的な準備に入ります! OAuth2認可サーバ構成 まずはOAuth2認可サーバの構成です!管理ポータル→システム管理→セキュリティ→OAuth 2.0→サーバ と進みます。 以下の内容に従って設定します。 「一般」タブ設定内容 発行者エンドポイント:ホスト名 <実際のホスト名を入力します> :プレフィックス 任意の値を入力できますが、ここでは「authserver」としています。 サポートする許可タイプ この記事の中では認証コード(Authorization Code)しか使用しませんが、他の許可タイプ(Grant Type)もテストしてみたい方はチェックを追加してください。また、「JWT承認」にもチェックを追加します。 SSL/TLS構成 先ほど追加したSSL構成を指定します。 「スコープタブ」では、「サポートしているスコープを追加」をクリックして追加します。あとで、表示される認可コード(Authorization Code)のログイン画面ではここで記述した「説明」が表示されます。 「間隔」タブはデフォルトから変更する内容はありません。「JWT」タブでは署名アルゴリズムに「RS512」を選択してみましょう。 最後の「カスタマイズ」タブでは、トークン・クラスを生成 の指定を %OAuth2.Server.JWT に変更します。 入力したら、「保存」ボタンを押して、構成を保存します。 これで、IRIS for HealthがOAuth2認可サーバとして稼働する基本の設定ができました。早速Postmanからアクセスしてアクセストークンが取得できるか試してみましょう! しかし、その前にあと2つ構成を行わなければいけません。 クライアントデスクリプションを追加する まずはOAuth2クライアントとしてアクセスするPOSTMANの情報を追加します。OAuth2クライアント登録は動的登録などで追加されることもあります。 サーバ構成画面の「クライアントデスクリプション」をクリックして先に進みます。 「クライアントデスクリプションを作成」をクリックしてエントリを追加します。 以下の内容に従ってクライアントデスクリプションを作成します。 「一般」タブ設定内容 名前 任意の名前を入力します。ここでは postmanとしました。 クライアントの種別 「機密」を選択します。 リダイレクトURL 「URLを追加」ボタンから追加します。postmanのリダイレクトURLとしてhttps://www.getpostman.com/oauth2/callbackを指定します。 サポートする許可タイプ OAuth2認可サーバの設定で構成した内容と同じ「認証コード」(Authorization Code) を指定します。(デフォルト)他の許可タイプもテストしてみたい場合はチェックを追加します。ただし、認可サーバの構成と同じ設定にする必要があります。また、「JWT承認」にもチェックを入れます。ただし、ここで指定する 認証署名アルゴリズム サポートする許可タイプで「JWT承認」をチェックすると、選択できるようになります。「RS512」を選択します。 入力できたら「保存」ボタンを押してクライアントデスクリプションを保存します。 「クライアント認証情報」タブをクリックすると、このエントリに対するクライアントIDとクライアントの秘密鍵を確認することができます。POSTMANからテストするときはこのIDと秘密鍵が必要になります。 ウェブアプリケーションの追加 POSTMANからアクセスする前にもう一つ大事な設定を加える必要があります。OAuth2認可サーバ構成画面で、この構成のエンドポイントが https://<hostname>/authserver/oauth2 と決定されました。このエンドポイントへのアクセスが正しくIRISで処理されるために、このURLパスに対するウェブアプリケーションの追加を行う必要があります。 システム管理→セキュリティ→アプリケーション→ウェブ・アプリケーション と進み、「新しいウェブ・アプリケーションを作成」をクリックします。 OAuth2のウェブ・アプリケーションのテンプレートが用意されているので、まず「コピー元」から「/oauth2」を選択します。 「ウェブ・アプリケーションの編集」設定項目 コピー元 /oauth2 プルダウンから必ずこちらを先に選択します。 名前 /authserver/oauth2 有効 「REST」のラジオボタンをチェックします。 各値を入力したら保存します。 POSTMANからOAuth2のテストを行う それではPOSTMANからテストしてみましょう。テストの実行は他のツールや実際のプログラムから行うこともできます。POSTMANの詳しい説明はこの記事では触れませんが、一点注意点として、POSTMANのSettingでSSL certificate verificationはOFFに変更するようにしてください。 POSTMANで新しいリクエストを作成後、AuthorizationタブのTYPEで「OAuth 2.0」を選択し、「Get New Access Token」をクリックします。 次の画面で、以下の内容に従って値を入力していきます。 「GET NEW ACCESS TOKEN」設定項目 Token Name 任意の名前を入力します。 Grant Type 「Authorization Code」を指定します。 Callback URL https://www.getpostman.com/oauth2/callback Auth URL https://<hostname>/authserver/oauth2/authorize?ui_locales=jaエンドポイント+/authorize の値を入力します。ui_locales=ja を追加することで日本語のログイン画面を表示させることができます。 Auth Token URL https://<hostname>/authserver/oauth2/tokenエンドポイント+/token の値を入力します。 Client ID クライアントデスクリプションの登録後にクライアント認証情報タブに表示される クライアントID を入力します。 Client Secret クライアントデスクリプションの登録後にクライアント認証情報タブに表示される クライアントの秘密鍵 を入力します。 Scope 認可サーバ構成で登録したスコープを入力します。 scope1 など。スペースで区切って複数指定することもできます。 State CSRF対策などで使用されるStateパラメータを入力します。特に使用しませんが空白にできないので、任意の文字列を入力しています。 パラメータ入力後、「Request Token」ボタンを押すと、以下のようなログイン画面が表示されたでしょうか? 管理ポータルにアクセスしているユーザ情報 (_SYSTEM等) でログインしてみてください。 ログイン後の次の画面では、このアプリケーションに権限の許可を与えるかどうか決定できます。「許可」をクリックしたあと、次の画面で以下のようにAccess Tokenが表示されれば、アクセストークン取得テストは成功です! OpenID Connectのテスト IRIS for Healthでは、OAuth2の認可処理だけでなく、OpenID Connectに準拠した認証処理を実施することができます。詳細はこちらのドキュメントを参照してください。 今回の構成では実はOpenID Connectの設定も有効になっています。OpenID ConnectののIDトークンも取得できるかテストしてみましょう! やり方は簡単です。先ほどのGET NEW ACCESS TOKEN画面で、Scopeに「openid」を追加してリクエストしてみましょう。 権限の要求ページでも、OpenID Connectが表記されます。ログイン処理を行い、許可を与えた後に次の画面が表示されたらIDトークン(id_token)も取得できることを確認してください。(画面を下にスクロールする必要があります。) いかがでしたか?Access Token そして id_tokenが取得できたでしょうか? 証明書等、少々手間がかかる準備もありますが、データベースプラットフォームであるIRIS for Health上でもこれだけ簡単にOAuth2認可サーバを構築することができました。 この連載の次のパートでは、いよいよFHIRリポジトリを構築し、FHIRリポジトリのOAuth2リソースサーバ登録、そして再びPOSTMANからOAuth2アクセストークンを使用したFHIRリポジトリへRESTアクセスする方法を紹介していきます。 記事を参考にしてアクセストークンの取得まで試してみたところ問題なく動作しました。 有益な記事をアップして頂きありがとうございます。 以下、記載されている手順以外で実行したものを列挙させて頂きます。 他に試す方のご参考になれば幸いです。 **SSL証明書の用意** Apache Webサーバー用のSSL証明書を作成 ``` openssl genrsa 2048 > server.key openssl req -new -key server.key > server.csr → CommonNameにDockerホストのホスト名を指定 openssl x509 -days 100 -req -signkey server.key < server.csr > server.crt ``` OAuth2.0用のSSL証明書を作成 ``` openssl genrsa 2048 > auth.key openssl x509 -days 100 -req -key auth.key -out auth.cer ``` **dockerビルド と dockerコンテナの起動** Dockerfileの38行目に以下を追加し、権限を設定 ``` RUN chmod +x /opt/webgateway/bin/webgateway-entrypoint.sh ``` **POSTMANからOAuth2のテストを行う** 自己署名証明書のためPostmanで「self signed certificate」エラーが発生するため、 設定から「SSL certificate verification」をOFFに設定。
記事
Toshihiko Minamoto · 2022年1月27日

Python用IRISネイティブAPI

はじめにバージョン2019.2より、InterSystems IRISは、高性能データアクセス手法としてPython用のネイティブAPIを提供してきました。 ネイティブAPIを使用すると、ネイティブのIRISデータ構造と直接対話することができます。グローバルInterSystems開発者であれば、おそらくすでにグローバルを理解していることでしょう。 ここでは、復習目的でその基本を確認しますが、次のセクションに進んでも構いません。InterSystems IRISはグローバルを使用してデータを格納しています。 グローバルは、値の有無に関係なくノードとサブノードで構成されるスパース配列です。 以下に、グローバルの概念的な例を示します。この例では、aはルートノードであり、グローバル名と呼ばれています。 各ノードには、グローバル名と1つまたは複数のサブスクリプト(サブノードの名前)で構成されるノードアドレスがあります。 aにはサブスクリプトbとcがあるため、それらのノードアドレスは、a->bおよびa->cとなります。ノードa->bとa->c->gには値(dとh)があり、ノードa->b->eとa->b->fには値がありません。 ノードa->bにはサブスクリプトeとfがあります。この構造の詳細な説明は、InterSystems dookbook『グローバルの使用法』に記載されています。グローバルの読み取りと書き込みネイティブのPython APIでは、IRISグローバルからデータを読み取りとそれへの書き込みを直接行えます。 irisnativeパッケージはGitHubで提供されています。または、InterSystems IRISがローカルマシンにインストールされている場合は、インストールディレクトリのdev/pythonサブディレクトリにあります。irisnative.createConnection関数は、IRISへの接続を作成し、irisnative.createIris関数は、この接続から、グローバルを操作するために使用するオブジェクトを取得します。 このオブジェクトには、グローバルの読み書きに使用するgetメソッドとsetメソッド、そしてノードとそのサブノードの削除に使用するkillメソッドがあります。 また、isDefinedメソッドもあり、このメソッドによって、リクエストされたノードが存在しない場合は「0」、 値があり子孫がない場合は「1」、値がなく子孫がある場合は「10」、値と子孫がある場合は「11」を返します。 import irisnative conn = irisnative.createConnection("127.0.0.1", 51773, "USER", "", "") iris = irisnative.createIris(conn) iris.set("value", "root", "sub1", "sub2") # sets "value" to root->sub1->sub2 print(iris.get("root", "sub1", "sub2")) print(iris.isDefined("root", "sub1")) iris.kill("root") conn.close() また、特定のノードのサブノードをループするiteratorメソッドもあります。 (使用方法は次のセクションで説明します。) 各メソッドの詳細な説明については、APIドキュメントをご覧ください。 サンフランシスコのGTFSトランジットデータファイル グローバルにデータを格納する General Transit Feed Specification(GTFS)は、公共交通機関の時刻表と経路に関するフォーマットです。 IRISネイティブAPIを使用して、2019年6月10日のサンフランシスコのGTFSデータを処理する方法を見てみましょう。 まず、データファイルの情報をIRISグローバルに格納します。 (このデモでは、すべてのファイルと列を使用するわけではありません。) ファイルはCSV形式で、最初の行に列名、残りの行にデータが含まれます。 Pythonで、必要なインポートを開始し、IRISへの接続を確立します。 import csv import irisnative conn = irisnative.createConnection("127.0.0.1", 51773, "USER", "", "") iris = irisnative.createIris(conn) 列名とデータに基づき、各ファイルの実用的なツリー構造を構築し、iris.setを使用してグローバルにデータを格納できます。 stops.txtファイルから始めましょう。このファイルにはサンフランシスコのすべての公共交通機関の停留所・停車駅が含まれます。 このファイルのstop_id列とstop_name列のみを使用します。 これらを、stop IDをサブスクリプト、stop nameをノードの値として使用し、1つのレイヤーのノードを持つツリー構造内のstopsというグローバルに格納します。 したがって、この構造は「stops → [stop_id]=[stop_name]」のようになります。 (この記事では、サブスクリプトがリテラルではなくデータファイルから読み取った値である場合に、角括弧を使用しています。) with open("stops.txt", "r") as csvfile: reader = csv.reader(csvfile) next(reader) # Ignore column names # stops -> [stop_id]=[stop_name] for row in reader: iris.set(row[6], "stops", row[4]) csv.readerは、カンマ区切りの値を保持するリストのイテレータを返します。 最初の行には列名が含まれているため、next(reader)を使ってその行をスキップします。 stop nameをstops -> [stop_id]の値として設定するために、 iris.setを使用します。 次はroutes.txtファイルです。このファイルのroute_type、route_id、route_short_name、およびroute_long_name列を使用します。 実用的なグローバル構造は、routes -> [route_type] -> [route_id] -> [route_short_name]=[route_long_name]です。 (route type(経路タイプ)は、路面電車の場合は「0」、バスの場合は「3」、ケーブルカーの場合は「5」です。) このCSVファイルからデータを読み取って、まったく同じようにグローバルに格納します。 with open("routes.txt", "r") as csvfile: reader = csv.reader(csvfile) next(reader) # Ignore column names # routes -> [route_type] -> [route_id] -> [route_short_name]=[route_long_name] for row in reader: iris.set(row[0], "routes", row[1], row[5], row[8]) 各経路には区間があります。これはtrips.txtファイルに格納されているもので、このファイルのroute_id、direction_id、trip_headsign、およびtrip_id列を使用します。 区間は、trip ID(後でstop timesファイルで確認します)で一意に識別されています。 ある経路の区間は、方向を基準とした2つのグループに分けられ、方向にはヘッドサインが関連付けられています。 これを踏まえると、ツリー構造はtrips -> [route_id] -> [direction_id]=[trip_headsign] -> [trip_id]となります。 ここでは2つのiris.set呼び出しが必要です。direction IDノードに値を設定する呼び出しと、trip IDの値のないノードを作成する呼び出しです。 with open("trips.txt", "r") as csvfile: reader = csv.reader(csvfile) next(reader) # Ignore column names # trips -> [route_id] -> [direction_id]=[trip_headsign] ->[trip_id] for row in reader: iris.set(row[3], "trips", row[1], row[2]) iris.set(None, "trips", row[1], row[2], row[6]) 最後に、stop times(停止時刻)を読み取って格納します。 これらは、stop_times.txtファイルに格納されており、このファイルのstop_id、trip_id、stop_sequence、departure_time列を使用します。 最初のオプションにはstoptimes -> [stop_id] -> [trip_id] -> [departure_time]を使用することができます。またはstop sequence(停止順)を維持する場合は、stoptimes -> [stop_id] -> [trip_id] -> [stop_sequence]=[departure_time]となります。 with open("stop_times.txt", "r") as csvfile: reader = csv.reader(csvfile) next(reader) # Ignore column names # stoptimes -> [stop_id] -> [trip_id] -> [stop_sequence]=[departure_time] for row in reader: iris.set(row[2], "stoptimes", row[3], row[0], row[4]) ネイティブAPIを使用してデータをクエリする 次の目標は、特定の名前の停留所・停車駅のすべての出発時刻を見つけることです。 まず、特定のstop nameからstop IDを取得し、そのIDを使ってstop_timesから関連する時刻を見つけます。 iris.iterator("stops")呼び出しを使うと、stopsルートノードのサブノードを反復できます。 サブスクリプトと値のペアを反復したいため(指定された名前を持つ値を比較し、一致すればスフにサブスクリプトを知ることができるため)、イテレータで.items()を呼び出し、戻り値の型を (subscript, value) のタプルに設定します。 次に、これらすべてのタプルを反復して、正しいstopを見つけ出します。 stop_name = "Silver Ave & Holyoke St" iter = iris.iterator("stops").items() stop_id = None for item in iter: if item[1] == stop_name: stop_id = item[0] break if stop_id is None: print("Stop not found.") import sys sys.exit() ノードが多数ある場合、反復によってキーの値でキーでルックアップするのはあまり効率的ではありません。 これを避けるには、サブスクリプトがstop nameで値がIDである別の配列を用意するとよいでしょう。 すると、value --> key ルックアップは、この新しい配列への1つのクエリで構成されます。 または、stop nameも一意であるため、stop IDの代わりに、コードのあらゆる箇所でstop nameを識別子として使用することもできます。 お分かりのとおり、stopsの数が膨大にある場合は、この検索に時間が掛かります。これは「フルスキャン」とも呼ばれています。 ただし、グローバルを利用して、値のIDを持つキーが名前となる逆配列を作成することができます。 iter = iris.iterator("stops").items() stop_id = None for item in iter: iris.set(item[0], "stopnames", item[1]) インデックスが名前で値がIDのstopnamesグローバルがあることで、名前でstop_idを見つける上記のコードは、フルスキャン検索を行わずに実行する以下のコードに変更されます。 stop_name = "Silver Ave & Holyoke St" stop_id=iris.get("stopnames", stop_name) if stop_id is None: print("Stop not found.") import sys sys.exit() この時点で、stop times(停止時刻)を見つけることができます。 サブツリーstoptimes -> [stop_id]にはtrip IDサブノードがあり、このサブノードにはstop timesサブノードがあります。 trip IDではなく、stop timesのみに関心があるため、すべてのtrip IDを反復して、それぞれのすべてのstop timesを収集します。 all_stop_times = set() trips = iris.iterator("stoptimes", stop_id).subscripts() for trip in trips: all_stop_times.update(iris.iterator("stoptimes", stop_id, trip).values()) ここでは、イテレータに.items()を使用していませんが、trip IDはサブスクリプトであるため.subscripts()と.values()を使用します。または、値とdeparture times(出発時刻)に関心がある場合には、下位レイヤー([stop_sequence]=[departure_time])を使用します。 .update呼び出しは、イテレータのすべての項目を既存のセットに追加します。 すると、セットにはすべての(一意の)stop timesが含まれます。 for stop_time in sorted(all_stop_times): print(stop_time) では、もう少し複雑にしてみましょう。 ある停車駅・停留所のすべての出発時刻を見つける代わりに、ある停車駅・停留所におけるroute IDが指定された特定の経路(両方向)のみの出発時刻を見つけることにします。 stop nameからstop IDを見つけ出すコードは、そのまま使用できます。 次に、特定の経路のすべてのtrip IDを取得します。 これらのIDは、departure timesを取得する際の追加の制限として使用されます。 trips -> [route_id]のサブセットは2つの方向に分割され、すべてのtrip IDがサブノードとなります。 前と同じようにdirectionsを反復し、すべてのdirectionsのサブノードをセットに追加します。 route = "14334" selected_trips = set() directions = iris.iterator("trips", route).subscripts() for direction in directions: selected_trips.update(iris.iterator("trips", route, direction).subscripts()) 次のステップとして、取得されたstop IDを[stop_id]、選択されるtrip IDを[trip_id]とするstoptimes -> [stop_id] -> [trip_id]のすべてのサブノードの値を見つけ出します。 selected_tripsセットを反復して、すべての関連する値を見つけます。 all_stop_times = set() for trip in selected_trips: all_stop_times.update(iris.iterator("stoptimes", stop_id, trip).values()) for stop_time in sorted(all_stop_times): print(stop_time) 最後の例では、isDefined関数の使用方法を示します。 以前に記述したコードを拡張し、route IDをハードコーディングする代わりに、経路の短縮名を指定すると、route IDはそれに基づいて取得されるようになります。 経路名のあるノードは、ツリーの最下位レイヤーにあります。 その上のレイヤーにはroute IDが含まれます。 すべての経路タイプを反復してからすべてroute IDを反復すると、ノードroutes -> [route_type] -> [route_id] -> [route_short_name]が存在して値がある(isDefinedが1を返す)場合は、探しているIDが[route_id]であることがわかります。 route_short_name = "44" route = None types = iris.iterator("routes").subscripts() for type in types: route_ids = iris.iterator("routes", type).subscripts() for route_id in route_ids: if iris.isDefined("routes", type, route_id, route_short_name) == 1: route = route_id if route is None: print("No route found.") import sys sys.exit() このコードは、ハードコーディングされたroute = "14334"の行の代わりになります すべてのIRIS操作が完了したら、データベースへの接続を閉じることができます。 conn.close() 今後の内容 Python のネイティブAPIを使ってInterSystems IRIS のデータ構造にアクセスする方法を説明し、それをサンフランシスコの公共交通機関データに適用しました。 APIの詳細な説明については、ドキュメントをご覧ください。 ネイティブAPIは、Java、.NET、およびNode.jsで提供されています。
記事
Mihoko Iijima · 2020年10月25日

%Net.HttpRequest クラスを使用して https のアクセスでエラーが発生したときに確認したいこと

これはInterSystems FAQ サイトの記事です。 %Net.HttpRequest クラスの SSLConfiguration プロパティに SSL/TLS 構成の「クライアント」構成名が指定されているかご確認ください。 %Net.HttpRequest クラスを使用して、https の url にアクセスするためには、以下のドキュメントに記載されている SSL/TLS 構成 の「クライアント」構成を作成して指定した名前を SSLConfiguration プロパティに指定する必要があります。 SSL/TLS構成のクライアント構成方法 管理ポータルの [システム管理] > [セキュリティ] > [SSL/TLS構成] メニューを開き、「構成名」に任意名を設定し、「保存」ボタンをクリックします(そのほかの構成パラメータは、デフォルト値で作成します)。 実行例は以下の通りです(https://www3.nhk.or.jp/news/ にアクセスしています)。 set httprequest=##class(%Net.HttpRequest).%New()set httprequest.Https=1set httprequest.Server="www.nhk.or.jp"set httprequest.SSLConfiguration="test1" //設定したSSL/TSL構成名を指定set st=httprequest.Get()set st=httprequest.Get("/news/")write $ZCVT(httprequest.HttpResponse.Data.Read(1000),"I","UTF8") // 取得できたレスポンスの一部を表示<!DOCTYPE HTML><!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7 eq-ie6"> <![endif]--><!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8 eq-ie7"> <![endif]--><!--[if IE 8]> <html class="no-js lt-ie9 eq-ie8"> <![endif]--><!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]--><head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# website: http://ogp.me/ns/website#"><meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta http-equiv="X-UA-Compatible" content="requiresActiveX=true" /> <title>NHKニュースサイト 日本全国・世界の速報、最新情報|NHK NEWS WEB</title><meta name="robots" content="noodp,noarchive"><meta name="keywords" content="NHK,ニュース,NHK NEWS WEB" /><meta name="description" content="NHKのニュースサイト「NHK NEWS WEB」。国内外の取材網を生かし、さまざまな分野のニュース をいち早く、正確にお伝えします。ニュース
記事
Megumi Kakechi · 2021年3月10日

RESTでセッション共有化する方法

これは InterSystems FAQ サイトの記事です。 REST アプリケーションはステートレスであることが想定されています。 しかし、複数の REST 呼び出し間でデータを保持したい場合などの理由で、Webセッションを使用したい場合は、UseSession パラメータを使用することができます。 以下のように、Web アプリケーションのディスパッチクラスとして定義された %CSP.REST のサブクラスでUseSession パラメータを指定することで、CSPと同じようにWebセッションを使用することが可能となります。 Class REST.MyServices Extends %CSP.REST { Parameter UseSession As Integer = 1; 詳細は以下のドキュメントをご覧ください。 REST での Web セッションの使用 以下は、UseSession パラメータを使用した簡単なサンプルになります。最初に、2つのクラスを作成してください。 * REST.SessionTest.cls Class REST.SessionTest Extends %CSP.REST { Parameter UseSession As Integer = 1; XData UrlMap { <Routes> <Route Url="/test/" Method="GET" Call="test"/> </Routes> } ClassMethod test() As %Status { write "{""SessionId"":"""_%session.SessionId_"""}" quit $$$OK } } * REST.test.cls Class REST.test Extends %CSP.Page { ClassMethod OnPage() As %Status { &html< <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <script type="text/javascript" src="http://code.jquery.com/jquery-2.2.4.js"></script> <script type="text/javascript"> function go() { $.ajax({ type:'GET', dataType:'json', url:'/csp/user/rest/test/', success:function(data) { ans = ''; for(var i in data) { ans = ans + '\n' + JSON.stringify(data[i]); } alert(ans); } }); return; } </script> </head> <body> <form name="xxx" method="post" action=""> <input type="button" name="test" onclick="go();" value="push" /></p> </form> Session ID: #(%session.SessionId)#<br> </body> </html> サンプルの使用方法は以下になります。 1. 上記2つのクラスをUSERネームスペースに作成し、コンパイルする 2. ウェブアプリケーション /csp/user/rest を作成、  ディスパッチクラスに REST.SessionTest を設定、  セッションCookieパスを /csp/user に変更して保存 3. ブラウザで /csp/user/REST.test.cls を開いて、push ボタンを押す
記事
Megumi Kakechi · 2023年3月8日

2つのグローバルの内容を比較する方法

これは InterSystems FAQ サイトの記事です。 ^%GCMP ユーティリティにて2つのグローバルの内容を比較することができます。 例としてUSERとSAMPLESネームスペースにある、^testと^testを比較する場合は以下のようになります。※以下の例では、2つのネームスペースに全く同じグローバルを700個作り、その中の一つの中身を変えて検出対象としています。 USER>kill ^test // USERネームスペースに、比較元のグローバル作成(削除したあとに作成) USER>for i=1:1:100 { for j=1:1:7 { set ^test(i,j)="テスト"_i } } USER>zn "samples" // SAMPLESネームスペースに、比較先のグローバル作成(削除したあとに作成) SAMPLES>kill ^test SAMPLES>for i=1:1:100 { for j=1:1:7 { set ^test(i,j)="テスト"_i } } SAMPLES>set ^test(50,5,1)=1 // SAMPLESネームスペースに作成したグローバルの一つを変更 SAMPLES>do ^%GCMP Compare global ^test // 比較したいグローバル on directory set: (this system) // Enter in namespace: SAMPLES => // Enter(このネームスペースで良ければ) with global ^test=> // 比較相手のグローバル on directory set: (this system) // Enter in namespace: SAMPLES => USER // 比較相手のネームスペース Output differences on Device: // 結果出力先、ターミナルで見る場合は <Enter> 押下 // ※ログファイル名をフルパスで書けば、そちらに出力される Right margin: 80 => Compare global ^test in SAMPLES with global ^test in USER ^test(50,5,1) exists in ^|"SAMPLES"|test but not in ^|"USER"|test // 相違のあるグローバルを検出 Time=.001822 SAMPLES> 同一インスタンス内ではなく、異なるサーバのインスタンス間での比較を行いたい場合は、^DATACHECKユーティリティを使用します。^DATACHECKユーティリティの使用方法については、以下の関連記事をご覧ください。 【関連】2つのデータベースにある複数のグローバルやルーチンを比較する方法
記事
Toshihiko Minamoto · 2020年9月9日

Linux にてTZ 環境変数が未設定の場合の Caché への影響

最近の大規模なベンチマーク活動で、アプリケーションのスケーリングに悪影響を与える過度の %sys CPU 時間が観察されました。 **問題** TZ 環境変数が設定されていないため、 _localtime()_ システムコールに多くの時間が費やされていることがわかりました。 観察結果を確認するための単純なテストルーチンが作成されましたが、TZ が設定されている場合と TZ が未設定の場合とでは経過時間と必要な CPUリソースが驚くほど違っていました。 TZ が設定されていない場合、_localtime()_ から /etc/local_time への _stat()_ システムコールの継承使用は非常に負荷が高いことがわかりました。 **推奨事項** InterSystems は、x86 または Linux on Power のいずれの Linux インストール環境でも、TZ 環境変数を適切に設定して最適なパフォーマンスを確保することを強く推奨しています。  詳細については、「man tzset」を参照してください。 現在の Caché 2016.1のフィールドテストでは日付および時刻関連の関数に関する最適化が行われており、初期テストでは大幅に改善していることがわかっています。 以下は、TZ が設定されていない場合に PowerPC上のLinuxで新しい内部関数の呼び出しをテストした結果の出力例です。 Caché 2016.1 FT 未満: real 0m22.60s user 0m1.64s sys 0m20.89s Caché 2016.1 FT の場合: real 0m0.40s user 0m0.37s sys 0m0.00s 皆さんが TZ 環境変数に関してアプリケーションで経験したことや、TZ 環境変数の設定有無による Caché  2016.1 のフィールドテストへの影響についてコメントを投稿してください。