0 フォロワー · 156 投稿

SQLは、リレーショナルデータベースにデータを格納、操作、および取得するための標準言語です。

記事 Toshihiko Minamoto · 11月 5, 2025 8m read

新しい InterSystems IRIS® Cloud SQL と InterSystems IRIS® Cloud IntegratedML® クラウド製品のユーザーであり、デプロイメントのメトリクスにアクセスして独自の可観測性プラットフォームに送信しようと考えている方のために、メトリクスを Google Cloud Platform Monitoring(旧称 StackDriver)に送信して手っ取り早く行う方法をご紹介します。

クラウドポータルには、概要メトリクス表示用のトップレベルのメトリクスが含まれており、ユーザーに公開されているメトリクスエンドポイントを使用しますが、ある程度探索しなければ、そこにあることには気づきません。

🚩 このアプローチは、「今後名前が付けられる予定の機能」を利用している可能性があるため、それを踏まえると将来性があるものではなく、確実に InterSystemsでサポートされているアプローチではありません。


では、より包括的なセットをエクスポートしたい場合はどうでしょうか?この技術的な記事/例では、メトリクスを取得して可観測性に転送する方法を紹介します。Open Telemetry Collector を使用して、任意のメトリクスターゲットを取得し、任意の可観測性プラットフォーム送信できるように、ニーズに合わせて変更することができます。

上記の結果に導く仕組みは多数の方法で得られますが、ここでは Kubernetes pod を使用して、1 つのコンテナーで Python スクリプトを実行し、もう 1 つのコンテナーで Otel を実行して、メトリクスのプルとプッシュを行います... 自分のやり方を選択することはできますが、この例と記事では、k8s を主人公に Python を使って行います。

手順:

  • 前提条件
  • Python
  • コンテナー
  • Kubernetes
  • Google Cloud Monitoring

前提要件:

  • IRIS®  Cloud SQL の有効なサブスクリプション
  • 実行中の 1 つのデプロイメント(オプションで Integrated ML を使用)
  • 環境に提供するシークレット

環境変数

 
 シークレットの取得
この内容は少し複雑で本題から少し外れているためティーザーに入れましたが、これがシークレットの生成に必要なる値です。
ENV IRIS_CLOUDSQL_USER 'user'
ENV IRIS_CLOUDSQL_PASS 'pass'

☝ これは https://portal.live.isccloud.io の認証情報です。

ENV IRIS_CLOUDSQL_USERPOOLID 'userpoolid'
ENV IRIS_CLOUDSQL_CLIENTID 'clientid'
ENV IRIS_CLOUDSQL_API 'api'

☝ これはブラウザの開発ツールから取得する必要があります。

  • `aud` = clientid
  • `userpoolid`= iss
  • `api` = request utl

ENV IRIS_CLOUDSQL_DEPLOYMENTID 'deploymentid'

☝これはクラウドサービスポータルから取得できます

 

Python:

以下に、クラウドポータルからメトリクスを取得し、それを Otel Collectorが取得するメトリクスとしてローカルにエクスポートする Python ハッキングを示します。

 
iris_cloudsql_exporter.py
import time
import os
import requests
import json

from warrant import Cognito from prometheus_client.core import GaugeMetricFamily, REGISTRY, CounterMetricFamily from prometheus_client import start_http_server from prometheus_client.parser import text_string_to_metric_families

classIRISCloudSQLExporter(object):definit(self): self.access_token = self.get_access_token() self.portal_api = os.environ['IRIS_CLOUDSQL_API'] self.portal_deploymentid = os.environ['IRIS_CLOUDSQL_DEPLOYMENTID']

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">collect</span><span class="hljs-params">(self)</span>:</span>
    <span class="hljs-comment"># Requests fodder</span>
    url = self.portal_api
    deploymentid = self.portal_deploymentid
    print(url)
    print(deploymentid)

    headers = {
        <span class="hljs-string">'Authorization'</span>: self.access_token, <span class="hljs-comment"># needs to be refresh_token, eventually</span>
        <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>
    }

    metrics_response = requests.request(<span class="hljs-string">"GET"</span>, url + <span class="hljs-string">'/metrics/'</span> + deploymentid, headers=headers)
    metrics = metrics_response.content.decode(<span class="hljs-string">"utf-8"</span>)

    <span class="hljs-keyword">for</span> iris_metrics <span class="hljs-keyword">in</span> text_string_to_metric_families(metrics):
        <span class="hljs-keyword">for</span> sample <span class="hljs-keyword">in</span> iris_metrics.samples:

            labels_string = <span class="hljs-string">"{1}"</span>.format(*sample).replace(<span class="hljs-string">'\''</span>,<span class="hljs-string">"\""</span>)
            labels_dict = json.loads(labels_string)
            labels = []

            <span class="hljs-keyword">for</span> d <span class="hljs-keyword">in</span> labels_dict:
                labels.extend(labels_dict)
            <span class="hljs-keyword">if</span> len(labels) &gt; <span class="hljs-number">0</span>:
                g = GaugeMetricFamily(<span class="hljs-string">"{0}"</span>.format(*sample), <span class="hljs-string">'Help text'</span>, labels=labels)
                g.add_metric(list(labels_dict.values()), <span class="hljs-string">"{2}"</span>.format(*sample))
            <span class="hljs-keyword">else</span>:
                g = GaugeMetricFamily(<span class="hljs-string">"{0}"</span>.format(*sample), <span class="hljs-string">'Help text'</span>, labels=labels)
                g.add_metric([<span class="hljs-string">""</span>], <span class="hljs-string">"{2}"</span>.format(*sample))
            <span class="hljs-keyword">yield</span> g

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_access_token</span><span class="hljs-params">(self)</span>:</span>
    <span class="hljs-keyword">try</span>:
        user_pool_id = os.environ[<span class="hljs-string">'IRIS_CLOUDSQL_USERPOOLID'</span>] <span class="hljs-comment"># isc iss </span>
        username = os.environ[<span class="hljs-string">'IRIS_CLOUDSQL_USER'</span>]
        password = os.environ[<span class="hljs-string">'IRIS_CLOUDSQL_PASS'</span>]
        clientid = os.environ[<span class="hljs-string">'IRIS_CLOUDSQL_CLIENTID'</span>] <span class="hljs-comment"># isc aud </span>
        print(user_pool_id)
        print(username)
        print(password)
        print(clientid)
        
        <span class="hljs-keyword">try</span>:
            u = Cognito(
                user_pool_id=user_pool_id,
                client_id=clientid,
                user_pool_region=<span class="hljs-string">"us-east-2"</span>, <span class="hljs-comment"># needed by warrant, should be derived from poolid doh</span>
                username=username
            )
            u.authenticate(password=password)
        <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> p:
            print(p)
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        print(e)

    <span class="hljs-keyword">return</span> u.id_token

ifname == 'main':

start_http_server(<span class="hljs-number">8000</span>)
REGISTRY.register(IRISCloudSQLExporter())
<span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>:
    REGISTRY.collect()
    print(<span class="hljs-string">"Polling IRIS CloudSQL API for metrics data...."</span>)
    <span class="hljs-comment">#looped e loop</span>
    time.sleep(<span class="hljs-number">120</span>)</code></pre>

 

Docker:

 
Dockerfile
FROM python:3.8ADD src /src
RUN pip install prometheus_client
RUN pip install requests
WORKDIR /src
ENV PYTHONPATH '/src/'ENV PYTHONUNBUFFERED=1ENV IRIS_CLOUDSQL_USERPOOLID 'userpoolid'ENV IRIS_CLOUDSQL_CLIENTID 'clientid'ENV IRIS_CLOUDSQL_USER 'user'ENV IRIS_CLOUDSQL_PASS 'pass'ENV IRIS_CLOUDSQL_API 'api'ENV IRIS_CLOUDSQL_DEPLOYMENTID 'deploymentid'RUN pip install -r requirements.txt
CMD ["python" , "/src/iris_cloudsql_exporter.py"]
docker build -t iris-cloudsql-exporter .
docker image tag iris-cloudsql-exporter sween/iris-cloudsql-exporter:latest
docker push sween/iris-cloudsql-exporter:latest


デプロイメント:

k8s、ネームスペースを作成します:

kubectl create ns iris

k8s、シークレットを追加します:

kubectl create secret generic iris-cloudsql -n iris \
    --from-literal=user=$IRIS_CLOUDSQL_USER \
    --from-literal=pass=$IRIS_CLOUDSQL_PASS \
    --from-literal=clientid=$IRIS_CLOUDSQL_CLIENTID \
    --from-literal=api=$IRIS_CLOUDSQL_API \
    --from-literal=deploymentid=$IRIS_CLOUDSQL_DEPLOYMENTID \
    --from-literal=userpoolid=$IRIS_CLOUDSQL_USERPOOLID

otel、構成を作成します:

apiVersion: v1
data:
  config.yaml: |
    receivers:
      prometheus:
        config:
          scrape_configs:
          - job_name: 'IRIS CloudSQL'
              # Override the global default and scrape targets from this job every 5 seconds.
            scrape_interval: 30s
            scrape_timeout: 30s
            static_configs:
                    - targets: ['192.168.1.96:5000']
            metrics_path: /
exporters:
  googlemanagedprometheus:
    project: "pidtoo-fhir"
service:
  pipelines:
    metrics:
      receivers: [prometheus]
      exporters: [googlemanagedprometheus]

kind: ConfigMap metadata: name: otel-config namespace: iris

k8s、otel 構成を configmap としてロードします:

kubectl -n iris create configmap otel-config --from-file config.yaml

k8s、ロードバランサー(確実にオプション)、MetalLB をデプロイします。これはクラスタ外部からグスクレイピングして検査するために行っています。

cat <<EOF | kubectl apply -f -n iris -
apiVersion: v1
kind: Service
metadata:
  name: iris-cloudsql-exporter-service
spec:
  selector:
    app: iris-cloudsql-exporter
  type: LoadBalancer
  ports:
  - protocol: TCP
    port: 5000
    targetPort: 8000
EOF

gcp、Google Cloud へのキーが必要です。サービスアカウントにスコープを設定する必要があります

  • roles/monitoring.metricWriter
kubectl -n iris create secret generic gmp-test-sa --from-file=key.json=key.json

k8s; deployment/pod そのもの。2 つのコンテナー:

 
deployment.yaml
kubectl -n iris apply -f deployment.yaml

実行

特に問題がなければ、ネームスペースを詳しく調べて、状況を確認してみましょう。

✔ GCP と Otel 用の 2 つの configmap

 

✔ 1 つのロードバランサー

 

✔ 1 つの pod、2 つのコンテナーが正しくスクレイピングされます

  

Google Cloud Monitoring

可観測性を調べてメトリクスが正しく受信されていることを確認し、可観測性を高めましょう!

 

0
0 20
記事 Kosaku Ikeda · 10月 26, 2025 14m read

コミュニティの皆さんこんにちは。
 

ベクトル検索関連の処理が完全にノーマークだった私が、一先ず「やってみよう!」との事で、2つの動画のサンプルを実行してみました。
Pythonは初心者なので、アレな箇所があっても目をつぶっていただけると幸いです。

また、間違っている箇所があったら、ご指摘いただけると幸いです。


■参考にした動画

■参考にしたコミュニティ記事

【目的】

本記事では、動画で紹介された内容を実際にIRIS環境上で実行できるよう、具体的な環境構築とコーディングを記載致します。
コミュニティの皆さんが簡単に試せるようになれば幸いです。

またGithubにサンプルソースを配置しているので、必要な方は参考にして下さい。

【準備】

■作業環境

※環境作成方法に問題のない方は、読み飛ばしていただいて構いません。

項目 バージョン情報・他
OS WIndowsServer2019
IRIS IRIS Community 2025.2.0.227.0
Python 3.12.10
開発環境 VS Code 1.105.1
0
0 41
InterSystems公式 Seisuke Nakahashi · 10月 23, 2025

インターシステムズは、InterSystems IRIS® data platformInterSystems IRIS® for HealthTMHealthShare® Health Connect のメンテナンスバージョン 2025.1.2 および 2024.1.5 をリリースしました。今回のリリースでは、最近お知らせした以下の警告や勧告の修正が含まれています。

製品の品質改善のために、開発者コミュニティを通じてぜひご意見をお聞かせください。

ドキュメント

詳細な変更リストとアップグレードチェックリストはこちらのドキュメントをご参照ください(すべて英語, 2025.1):

早期アクセスプログラム (Early Access Programs; EAPs)

0
0 25
記事 Hiroshi Sato · 10月 21, 2025 1m read

これは InterSystems FAQ サイトの記事です。
 

%String型のプロパティをOrder Byの条件にしてクエリーを発行した際のデータは以下のような順番で並べられます。

SELECT * FROM Shop.Order orderby StatusFlag
null
-1
-2
-99
0

これは%String型(文字列型)のプロパティの照合順として正しい振る舞いです。

文字列照合の並び順

文字列プロパティに対し、+ をつけることで、数値照合と同じ照合順を得ることができます。

SELECT * FROM Shop.Order orderby +StatusFlag
null
-99
-1
-2
0
0
0 15
記事 Hiroshi Sato · 10月 21, 2025 1m read

これは InterSystems FAQ サイトの記事です。
 

SQLアクセス( ADO含む)を行う場合は、SQLトランザクションを使用して、トランザクションを制御します。

一方オブジェクトアクセス(ObjectScript)ではtstart / tcommit / trollbackコマンド 
(Native SDK for .NETでは IRIS の TStart(), TCommit(), TRollback() メソッド)
によってトランザクションを制御します。

この2種類のトランザクションモードを混在させて使用することはサポートされていません。

詳細は、以下のドキュメントをご参照ください。

トランザクション管理

また関連するメソッドの以下ドキュメントの注意事項にも

「このメソッドは Native SDK トランザクション・モデルを使用し、ADO.NET/SQL トランザクション・メソッドとは互換性がありません。

この 2 つのトランザクション・モデルを混在させないでください。」

と記載をしております。

Native SDK for .NET のクイック・リファレンス

0
0 13
記事 Hiroshi Sato · 9月 25, 2025 2m read

これは InterSystems FAQ サイトの記事です。
 

何の設定も行なっていない場合、GROUP BYやDISTINCTで指定したフィールドは大文字小文字を区別せずに全て大文字として処理されます。

これはGROUP BYやDISTINCTのグループ化がフィールドに対して定義された照合タイプに基づいて行われ、その文字列照合の既定値がSQLUPPERになっているためです。

以下のドキュメントに説明がある通り、DISTINCT は、フィールドに対して定義された照合タイプに基づいて、文字列値をグループ化します。

大文字/小文字の区別と DISTINCT の最適化

これを変更する方法は、以下の3種類になっています。

(A) %SQLSTRING または %EXACT照合関数を使用する
(B) フィールドの文字列照合を SQLSTRINGに変更する
 また、フィールドにインデックスが設定されている場合には、インデックスの文字列照合も SQLSTRINGに変更する
(C) 管理ポータルで設定を変更する 

※(B)について補足
・文字列照合をEXACTに設定しても動作しますが、一般的にはSQLSTRINGの使用が推奨されています。
・フィールドの文字列照合とインデックスの文字列照合は、同じ設定にすることが推奨されています。

※(C)の設定手順

0
0 21
記事 Hiroshi Sato · 9月 3, 2025 3m read

これは InterSystems FAQ サイトの記事です。
 

JSON利用の普及に伴いインターシステムズは、JSONに関連する様々な機能強化をIRISに対して行なっています。

その一環として、SQLのJSON_OBJECTのサポートがあります。

この機能に関して現時点より(2025年6月)古いバージョンでは残念ながら制限や不具合が存在しています。

今後も機能強化やバグフィックスを継続していく予定となっているため、この機能の利用を検討および既に利用している方は最新バージョンでのご利用をお勧めします。

ここでは、現時点でわかっている制限事項/不具合についてお知らせします。

  • VIEW経由でJSON_OBJECTを利用した場合にエラーとなる場合がある 以下のようなクラスとVIEWを定義します。  
Class User.test Extends%Persistent
{

  Property p1 As%String;Property p2 As%String;
}
Class User.myview [ ClassType = view, ViewQuery = { select p1 as v10, p1 as v11, p2 as v12 from test } ] { }
0
0 24
記事 Hiroshi Sato · 7月 27, 2025 4m read

これは InterSystems FAQ サイトの記事です。
 

ODBCクライアントからのアクセスでエラーが発生した場合、返ってきたエラーメッセージやコンソールログ上の情報だけでは原因がよくわからない場合があります。

そのような場合にODBCドライバのクライアントログを有効にすることで、エラーについての詳細情報を取得することができます

ODBCログの有効化については以下をご参照ください。

ODBCログの有効化

Windowsの場合は、上記に記載されている通り、2種類のログが取得できます。

  • クライアント・ドライバのログを有効にするには、ODBCデータソースアドミニストレータで使用しているDSNの「ODBCログ」チェックボックスをチェックします。 

 

  • ドライバー・マネージャのログを有効にするには、[トレース]タブをクリックして[トレースの開始]ボタンをクリックします。

  

変更の反映にはODBCクライアントアプリケーションを再起動する必要があります。

既定のODBCクライアントログファイル名は IRISODBC.log で、既定の場所は C:¥Users¥Public¥Logs です。

既定のトレースログ名は、SQL.logで、既定の場所は、c:¥Users¥<ユーザー名>AppData¥Local¥Temp¥です。

(すべてのユーザーIDのコンピュータ全体のトレースをチェックしない場合)

0
0 87
記事 Mihoko Iijima · 6月 30, 2025 2m read

これは InterSystems FAQ サイトの記事です。

ObjectScript で日付の比較を行う場合、一旦 $HOROLOG 形式(内部数値)に変換することで算出しやすくなりますが、SQL 関数を利用して算出することもできます。

ObjectScript から SQL 関数を実行するには、%SYSTEM.SQL.Functions クラスを使用します。

※ 2021.1以前のバージョンでは、%SYSTEM.SQL クラスを使用します。

%SYSTEM パッケージは、システム・オブジェクトと呼ばれ ObjectScript では $SYSTEM 特殊変数を利用して以下の構文で実行します。

  $SYSTEM.サブパッケージ名.クラス名.メソッド名() または $SYSTEM.クラス名.メソッド名()

以下、SQL 関数 DATEDIFF を使用して日付の比較を行う例です。

USER>write$system.SQL.Functions.DATEDIFF("dd","2025-01-20","2025-03-20")
59

 

分での比較

USER>write$system.SQL.Functions.DATEDIFF("mi","2025-01-20","2025-03-20")
84960

 

秒での比較

0
0 28
記事 Toshihiko Minamoto · 5月 27, 2025 14m read

次回の Python コンテストでは、Python を使用して IRIS をデータベースとして使用する簡単な REST アプリケーションを作成する方法についての小さなデモを作成しようと思います。 以下のツールを使用します。

  • FastAPI フレームワーク: 高パフォーマンス、学習しやすい、高速コーディング、プロダクション対応
  • SQLAlchemy: Python SQL ツールキットで、アプリケーション開発者が SQL の全性能と柔軟性を活用できるオブジェクトリレーションマッパーです。
  • Alembic: Python 用の SQLAlchemy データベースツールキットと使用する軽量のデータベース移行ツール。
  • Uvicorn: Python の ASGI ウェブサーバー実装。

環境の準備

バージョン 3.7 以降の Python がすでにインストール済みだと思います。 プロジェクトフォルダを作成し、その中に以下のコンテンツで requirements.txt ファイルを作成します。

fastapi==0.101.1
alembic==1.11.1
uvicorn==0.22.0
sqlalchemy==2.0.20
sqlalchemy-iris==0.10.5

Python で仮想環境を使用することをお勧めします。新しい環境を作成して有効化しましょう。

python -m venv env && source env/bin/activate

そして、依存関係をインストールします。

pip install -r requirements.txt

クイックスタート

FastAPI を使って最も単純な REST Api を作成しましょう。 これを行うには、app/main.py を作成します。

from fastapi import FastAPI

app = FastAPI( title='TODO Application', version='1.0.0', )

@app.get("/ping")asyncdefpong():return {"ping": "pong!"}

この時点で、アプリケーションを純分に起動して動作させることができます。 サーバーの起動には、uvicorn を使用します。

$ uvicorn app.main:app         
INFO:     Started server process [94936]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

ping リクエストを発行できます。

$ curl http://localhost:8000/ping
{"ping":"pong!"}

FastAPI には API をテストできる UI が用意されています。

Docker 化環境

IRIS をアプリケーションに追加するには、コンテナーを使用します。 IRIS イメージはそのままで使用できますが、Python アプリケーション用の Docker イメージを作成する必要があります。 また、Dockerfile が必要です。

FROM python:3.11-slim-buster

WORKDIR /usr/src/app RUN --mount=type=bind,src=.,dst=.
pip install --upgrade pip &&
pip install -r requirements.txt
COPY . . ENTRYPOINT [ "/usr/src/app/entrypoint.sh" ]

コンテナー内でアプリケーションを起動するには、簡単な entrypoint.sh が必要です。

#!/bin/sh
# Run SQL Migrations, to make DB Schema up to date
alembic upgrade head

# Start Python application uvicorn app.main:app
--workers 1
--host 0.0.0.0
--port 8000 "$@"

実行フラグを忘れずに追加しましょう。

chmod +x entrypoint.sh

docker-compose.yml で IRIS と組み合わせます。

version:"3"services:  iris:    image:intersystemsdc/iris-community    ports:      -1972    environment:      -IRISUSERNAME=demo      -IRISPASSWORD=demo    healthcheck:      test:/irisHealth.sh      interval:5s  app:    build:.    ports:      -8000:8000    environment:      -DATABASE_URL=iris://demo:demo@iris:1972/USER    volumes:      -./:/usr/src/app    depends_on:      iris:        condition:service_healthy    command:      ---reload

ではビルドしましょう。

docker-compose build

最初のデータモデル

アプリケーションに IRIS データベースへのアクセスを宣言し、app/db.py ファイルを追加しましょう。このファイルによって データベースにアクセスできるように SQLAlchemy が構成されます。これは docker-compose.yml によって渡される URL で定義されます。これには後でアプリで使用するハンドラーがいくつか含まれています。

import os

from sqlalchemy import create_engine from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base from sqlalchemy.orm import sessionmaker

DATABASE_URL = os.environ.get("DATABASE_URL") ifnot DATABASE_URL: DATABASE_URL = "iris://demo:demo@localhost:1972/USER" engine = create_engine(DATABASE_URL, echo=True, future=True)

Base: DeclarativeMeta = declarative_base()

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

definit_db(): engine.connect()

defget_session(): session = SessionLocal() yield session

では、初の唯一のモデルをアプリケーションに定義しましょう。 ファイル app/models.py を作成して編集します。SQLAlchemy を使用して、Todo と言う、id、title、description の 3 つ列を持つモデルを定義します。

from sqlalchemy import Column, Integer, String, Text
from app.db import Base

classTodo(Base):tablename = 'todo' id = Column(Integer, primary_key=True, index=True) title = Column(String(200), index=True, nullable=False) description = Column(Text, nullable=False)

SQL の移行の準備

変化し続ける世界では、アプリケーションは将来的に改善されると考えるため、テーブル構造は最終的なものではなく、さらにテーブル、列、インデックスなどを追加できるとわかっています。 この場合の最善のシナリオは、SQL Migration ツールを使用することです。これは、アプリケーションのバージョンに応じてデータベースの現在の構造をアップグレードできるツールであり、何かが誤ってしまった場合には、これらのツールを使用することでダウングレードすることもできます。 このプロジェクトでは Python と SQLAlchemy を使用していますが、SQLAlchemy の作者は Alembic というツールを提供しており、ここではそれを使用します。

IRIS と、アプリケーションを含むコンテナーを起動する必要がありますが、この時点では、コマンドを実行できるように Bash する必要があります。

$ docker-compose run --entrypoint bash app
[+] Creating 2/0
 ✔ Network fastapi-iris-demo_default   Created                                                                                                                                                        0.0s 
 ✔ Container fastapi-iris-demo-iris-1  Created                                                                                                                                                        0.0s 
[+] Running 1/1
 ✔ Container fastapi-iris-demo-iris-1  Started                                                                                                                                                        0.1s 
root@7bf903cd2721:/usr/src/app# 

コマンド alembic init app/migrations を実行します。

root@7bf903cd2721:/usr/src/app# alembic init app/migrations
  Creating directory '/usr/src/app/app/migrations' ...  done
  Creating directory '/usr/src/app/app/migrations/versions' ...  done
  Generating /usr/src/app/app/migrations/README ...  done
  Generating /usr/src/app/app/migrations/script.py.mako ...  done
  Generating /usr/src/app/app/migrations/env.py ...  done
  Generating /usr/src/app/alembic.ini ...  done
  Please edit configuration/connection/logging settings in '/usr/src/app/alembic.ini' before proceeding.
root@7bf903cd2721:/usr/src/app#

これにより Alembic 構成が準備されたため、アプリケーションのニーズに適合するように修正する必要があります。 これを行うには app/migrations/env.py ファイルを編集します。 これはファイルの始まりに過ぎないため、更新する必要があります。sqlalchemy.urltarget_metadata を更新することに専念しましょう。 その以下の変更はありません。

import os
import urllib.parse
from logging.config import fileConfig

from sqlalchemy import engine_from_config from sqlalchemy import pool

from alembic import context

# this is the Alembic Config object, which provides# access to the values within the .ini file in use. config = context.config

DATABASE_URL = os.environ.get("DATABASE_URL")

decoded_uri = urllib.parse.unquote(DATABASE_URL) config.set_main_option("sqlalchemy.url", decoded_uri)

# Interpret the config file for Python logging.# This line sets up loggers basically.if config.config_file_name isnotNone: fileConfig(config.config_file_name)

# add your model's MetaData object here# for 'autogenerate' supportfrom app.models import Base target_metadata = Base.metadata # target_metadata = None

すでにモデルが存在するため、コマンド alembic revision --autogenerate で移行を作成する必要があります。

root@7bf903cd2721:/usr/src/app# alembic revision --autogenerate
INFO  [alembic.runtime.migration] Context impl IRISImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'todo'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_todo_id' on '['id']'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_todo_title' on '['title']'
  Generating /usr/src/app/app/migrations/versions/1e4d3b4d51ca_.py ...  done
root@7bf903cd2721:/usr/src/app# 
 
生成された移行を見てみましょう。

では、これをデータベースに適用しましょう。コマンド alembic upgrade head を使用します。ここで、head は最新バージョンにアップグレードするためのキーワードです。

root@7bf903cd2721:/usr/src/app# alembic upgrade head
INFO  [alembic.runtime.migration] Context impl IRISImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> 1e4d3b4d51ca, empty message
 
Downgrade
アプリケーションのアップグレード中に元に戻す必要があることが分かった場合に、例えば 1 つ前のリビジョンにデータベースをダウングレードするには head-1 とします。
root@7bf903cd2721:/usr/src/app# alembic downgrade head-1
INFO  [alembic.runtime.migration] Context impl IRISImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running downgrade 1e4d3b4d51ca -> , empty message

and to completely downgrade back to an empty state, use keyword base

現在の状態をいつでも確認できます。いくつかの移行が欠落している場合にはその情報を得られます。

root@7bf903cd2721:/usr/src/app# alembic check
INFO  [alembic.runtime.migration] Context impl IRISImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
No new upgrade operations detected.

データをアクセス可能にする

REST に戻り、実行させる必要があります。現在のコンテナーを終了し、アプリサービスを通常どおり実行すると、uvicorn には --reload フラグが設定されているため、Python ファイル内の変更をチェックし、変更がある場合には再起動されます。

$ docker-compose up app
[+] Running 2/0
 ✔ Container fastapi-iris-demo-iris-1  Running                                                                                                                                                        0.0s 
 ✔ Container fastapi-iris-demo-app-1   Created                                                                                                                                                        0.0s 
Attaching to fastapi-iris-demo-app-1, fastapi-iris-demo-iris-1
fastapi-iris-demo-app-1   | INFO  [alembic.runtime.migration] Context impl IRISImpl.
fastapi-iris-demo-app-1   | INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
fastapi-iris-demo-app-1   | INFO:     Will watch for changes in these directories: ['/usr/src/app']
fastapi-iris-demo-app-1   | INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
fastapi-iris-demo-app-1   | INFO:     Started reloader process [8] using StatReload
fastapi-iris-demo-app-1   | INFO:     Started server process [10]
fastapi-iris-demo-app-1   | INFO:     Waiting for application startup.
fastapi-iris-demo-app-1   | INFO:     Application startup complete.

FastAPI は Pydantic プロジェクトを使用してデータスキーマを宣言しているため、それも必要です。app/schemas.py を作成しましょう。列は models.py と同じですが、単純な Python フォームを使用します。

from pydantic import BaseModel

classTodoCreate(BaseModel): title: str description: str

classTodo(TodoCreate): id: int

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span>:</span>
    from_attributes = <span class="hljs-keyword">True</span>

SQLAlchemy ORM を使ってデータベースを操作する app/crud.py で CRUD 操作を宣言します。

from sqlalchemy.orm import Session
from . import models, schemas

defget_todos(db: Session, skip: int = 0, limit: int = 100):return db.query(models.Todo).offset(skip).limit(limit).all()

defcreate_todo(db: Session, todo: schemas.TodoCreate): db_todo = models.Todo(**todo.dict()) db.add(db_todo) db.commit() db.refresh(db_todo) return db_todo

そして最後に、app/main.py を更新し、ToDo を読み取って作成するルートを追加できます。

from fastapi import FastAPI, Depends
from .db import init_db, get_session
from . import crud, schemas

app = FastAPI( title='TODO Application', version='1.0.0', )

@app.on_event("startup")defon_startup(): init_db()

@app.get("/ping")asyncdefpong():return {"ping": "pong!"}

@app.get("/todo", response_model=list[schemas.Todo])asyncdefread_todos(skip: int = 0, limit: int = 100, session=Depends(get_session)): todos = crud.get_todos(session, skip=skip, limit=limit) return todos

@app.post("/todo", response_model=schemas.Todo)asyncdefcreate_todo(todo: schemas.TodoCreate, session=Depends(get_session)):return crud.create_todo(db=session, todo=todo)

これに応じてドキュメント ページが更新され、実際に操作できるようになりました。

 
試してみましょう。
新しい ToDo を追加します。

その内容を確認しましょう。

IRIS で確認してみましょう。

─$ docker-compose exec iris irissqlcli iris+emb:///
Server:  IRIS for UNIX (Ubuntu Server LTS for ARM64 Containers) 2023.2 (Build 227U) Mon Jul 31 2023 17:43:25 EDT
Version: 0.5.4
[SQL]irisowner@/usr/irissys/:USER> .tables
+-------------------------+
| TABLE_NAME              |
+-------------------------+
| SQLUser.alembic_version |
| SQLUser.todo            |
+-------------------------+
Time: 0.043s
[SQL]irisowner@/usr/irissys/:USER> select * from todo
+----+-------+---------------------+
| id | title | description         |
+----+-------+---------------------+
| 1  | demo  | it's really working |
+----+-------+---------------------+
1 row in set
Time: 0.004s
[SQL]irisowner@/usr/irissys/:USER> select * from alembic_version
+--------------+
| version_num  |
+--------------+
| 1e4d3b4d51ca |
+--------------+
1 row in set
Time: 0.045s
[SQL]irisowner@/usr/irissys/:USER>

 

REST の作成において Python と FastAPI を簡単に使用していただけたなら幸いです。 このプロジェクトのソースコードは、GitHub の https://github.com/caretdev/fastapi-iris-demo にあります。

0
0 45
記事 Toshihiko Minamoto · 5月 14, 2025 9m read

お客様のプロジェクトにおいて、どのようにすればいつ何時に誰がデータベースを変更したかを追跡できるか問われました。 SQL とオブジェクトへの両方のアクセスで Insert、Update、および Delete を追跡することが目的です。

以下は、変更ログを維持するために作成したテーブルです。

/// Changelog, keep track of changes to any table
Class ChangeLog.DB.ChangeLog Extends (%Persistent, %JSON.Adaptor)
{

/// Action 
Property Action As %String(%JSONFIELDNAME = "action", DISPLAYLIST = ",Create,Update,Delete", MAXLEN = 1, VALUELIST = ",0,1,2");

/// Classname of the %Persistent class
Property ClassName As %String(%JSONFIELDNAME = "table", MAXLEN = "") [ SqlFieldName = TableName ];

/// ID of the record
Property DRecordId As %String(%JSONFIELDNAME = "id") [ SqlFieldName = RecordId ];

/// Name of the user that made the change
Property DUsername As %String(%JSONFIELDNAME = "user") [ SqlFieldName = Username ];

/// ISO 8601 formatted UTC timestamp e.g 2023-03-20T15:14:45.7384083Z
Property ETimestamp As %String(%JSONFIELDNAME = "timestamp", MAXLEN = 28) [ SqlFieldName = Timestamp ];

/// Changed Data (only there for Action < 2)
Property NewData As %String(%JSONFIELDNAME = "changed-data", MAXLEN = "");

/// Old Data (only there for Action > 0)
Property OldData As %String(%JSONFIELDNAME = "old-data", MAXLEN = "");

}

変更を追跡するためのテーブルは単純な Name-Value タイプでした。

Class ChangeLog.DB.NameValues Extends %Persistent
{

/// Name
Property name As %String;

Index nameIndex On name [ Unique ];

/// Value
Property value As %String(MAXLEN = "");

/// CreateOrUpdate
ClassMethod CreateOrUpdate(name As %String, value As %String = "") As %Status
{
    if ..nameIndexExists(name)
    {
        if (value = "")
        {
            return ..nameIndexDelete(name)
        }

        set record = ..nameIndexOpen(name)
    }
    else
    {
        set record = ..%New()
        set record.name = name
    }

    if (value = "") // Do not store!
    {
        return $$$OK        
    }

    set record.value = value

    return record.%Save()
}

}

まず、%OnAfterSave() メソッドを使用することを試してみました。これは非常に簡単でしたた、SQL を介して更新が起こったときに呼び出されませんでした。 そこで、代わりにトリガーメソッドを記述しなければならないことがわかりました。https://docs.intersystems.com/healthconnectlatest/csp/docbook/DocBook.UI.Page.cls?KEY=GSQL_triggers をご覧ください。

適用する特定の構文ルールに慣れてきたら、トリガーメソッドを書くのは比較的簡潔な作業であったため、NameValues クラスに以下のトリガーを追加しました。

/// Write the DB changelog
Trigger LogUpdate [ Event = INSERT/UPDATE, Foreach = row/object, Time = AFTER ]
{
    New changelog
    set changelog = ##class(ChangeLog.DB.ChangeLog).%New()
    set changelog.ClassName = $CLASSNAME()
    set changelog.DRecordId = {ID}
    set changelog.Action = (%oper = "UPDATE")
    set changelog.DUsername = $SYSTEM.Process.UserName()
    set changelog.ETimestamp = $ZDATETIME($ZTIMESTAMP, 3, 7, 7)
    if (%oper = "UPDATE") // also add old data
    {
        set changelog.OldData = { "name": ({name*O}), "value": ({value*O}) }.%ToJSON()
    }
    set changelog.NewData = { "name": ({name*N}), "value": ({value*N}) }.%ToJSON()
    do changelog.%Save()
}

/// Write delete to changelog
Trigger LogDelete [ Event = DELETE, Foreach = row/object ]
{
    New changelog
    set changelog = ##class(ChangeLog.DB.ChangeLog).%New()
    set changelog.ClassName = $CLASSNAME()
    set changelog.DRecordId = {ID}
    set changelog.Action = 2
    set changelog.DUsername = $SYSTEM.Process.UserName()
    set changelog.ETimestamp = $ZDATETIME($ZTIMESTAMP, 3, 7, 7)
    set changelog.OldData = { "name": ({name*O}), "value": ({value*O}) }.%ToJSON()
    do changelog.%Save()
}

上記のコードは Name-Values テーブルように特別に書かれたもので、ここでは name プロパティの古い値に使用する {nameO} と、value プロパティの新しい値を表す {valueN} のように、古いプロパティ値と新しいプロパティ値にアクセスする特別な構文を使用しています。 さらに、 value プロパティが特定の更新中に実際に変更したかを確認する {value*C} も追加することが可能でした。

特定のテーブルに対するトリガーを作成できたので、トリガーの構文が特定のプロパティ名をサポートしてもワイルドカードは使用しないことを考えると、同じように動作しても完全に汎用になるように変更するにはどうすればよいか考えました。

最近の記事(https://community.intersystems.com/post/how-add-webterminal-when-you-have-no-terminal-access)では [ CodeMode = objectgenerator ] を使用したため、ここでもそれを使用できないか考えました。

興味深いことに、https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GOBJ_generators#GOBJ_methodgen のセクションから、オブジェクト生成中にプロパティをリスト表示する方法がすぐにわかりました。 さらに、CodeMode = objectgenerator はトリガーでもサポートされているのです!

そういった背景において、トリガーを完全に汎用化し始め、以下のコードが出来上がりました。

/// %Persistent type with a changelog
Class ChangeLog.DB.PersistentWithChangeLog Extends %Persistent
{
/// Write the DB changelog
Trigger LogUpdate [ CodeMode = objectgenerator, Event = INSERT/UPDATE, Foreach = row/object, Time = AFTER ]
{
    do %code.WriteLine(" new changelog")
    do %code.WriteLine(" set changelog = ##class(ChangeLog.DB.ChangeLog).%New()")
    do %code.WriteLine(" set changelog.ClassName = ..%ClassName()")
    do %code.WriteLine(" set changelog.DRecordId = {ID}")
    do %code.WriteLine(" set changelog.Action = (%oper = ""UPDATE"")")
    do %code.WriteLine(" set changelog.DUsername = $UserName")
    do %code.WriteLine(" set changelog.ETimestamp = $ZDATETIME($ZTIMESTAMP, 3, 7, 7)")
    do %code.WriteLine(" if (%oper = ""UPDATE"") {")
    do %code.WriteLine("     new data")
    do %code.WriteLine("     set data = {}")

    for i = 1:1:%compiledclass.Properties.Count()
    {
        set prop = %compiledclass.Properties.GetAt(i)
        set propName = prop.Parameters.GetAt("%JSONFIELDNAME")

        if (propName = "")
        {
            set propName = prop.Name
        }

        if (prop.Name '[ "%") && 'prop.Transient && (prop.Type ? 1"%Library"0.E)
        {
            do %code.WriteLine("     do data.%Set(""" _ propName _ """, {"_ prop.Name _ "*O})")
        }
    }

    do %code.WriteLine("     set changelog.OldData = data.%ToJSON()")
    do %code.WriteLine(" }")
    do %code.WriteLine(" set data = {}")

    for i = 1:1:%compiledclass.Properties.Count()
    {
        set prop = %compiledclass.Properties.GetAt(i)
        set propName = prop.Parameters.GetAt("%JSONFIELDNAME")

        if (propName = "")
        {
            set propName = prop.Name
        }

        if (prop.Name '[ "%") && 'prop.Transient && (prop.Type ? 1"%Library"0.E)
        {
            do %code.WriteLine(" if {"_ prop.Name _ "*C} && '$ISOBJECT({"_ prop.Name _ "*N}) {")
            do %code.WriteLine("     do data.%Set(""" _ propName _ """, {"_ prop.Name _ "*N})")
            do %code.WriteLine(" }")
        }
    }
    do %code.WriteLine(" set changelog.NewData = data.%ToJSON()")
    do %code.WriteLine(" do changelog.%Save()")
    return $$$OK
}

/// Write delete to changelog
Trigger LogDelete [ CodeMode = objectgenerator, Event = DELETE, Foreach = row/object ]
{
    do %code.WriteLine(" new changelog")
    do %code.WriteLine(" set changelog = ##class(ChangeLog.DB.ChangeLog).%New()")
    do %code.WriteLine(" set changelog.ClassName = ..%ClassName()")
    do %code.WriteLine(" set changelog.DRecordId = {ID}")
    do %code.WriteLine(" set changelog.Action = 2")
    do %code.WriteLine(" set changelog.DUsername = $UserName")
    do %code.WriteLine(" set changelog.ETimestamp = $ZDATETIME($ZTIMESTAMP, 3, 7, 7)")
    do %code.WriteLine(" new data")
    do %code.WriteLine(" set data = {}")

    for i = 1:1:%compiledclass.Properties.Count()
    {
        set prop = %compiledclass.Properties.GetAt(i)
        set propName = prop.Parameters.GetAt("%JSONFIELDNAME")

        if (propName = "")
        {
            set propName = prop.Name
        }

        if (prop.Name '[ "%") && 'prop.Transient && (prop.Type ? 1"%Library"0.E)
        {
            do %code.WriteLine(" do data.%Set(""" _ propName _ """, {"_ prop.Name _ "*O})")
        }
    }

    do %code.WriteLine(" set changelog.OldData = data.%ToJSON()")
    do %code.WriteLine(" do changelog.%Save()")
    return $$$OK
}

もちろんこれを Name-Values クラスでテストしました。

/// Test table with name values
/// Each change must be recorded in the ChangeLog Table
Class ChangeLog.DB.NameValues Extends ChangeLog.DB.PersistentWithChangeLog
{

/// Name
Property name As %String;

Index nameIndex On name [ Unique ];

/// Value
Property value As %String(MAXLEN = "");

/// CreateOrUpdate
ClassMethod CreateOrUpdate(name As %String, value As %String = "") As %Status
{
    if ..nameIndexExists(name)
    {
        if (value = "")
        {
            return ..nameIndexDelete(name)
        }

        set record = ..nameIndexOpen(name)
    }
    else
    {
        set record = ..%New()
        set record.name = name
    }

    if (value = "") // Do not store!
    {
        return $$$OK        
    }

    set record.value = value

    return record.%Save()
}

}

上手くいきました! 次の 3 つのコマンドを実行すると...

  1. 以下を使用して、レコードを NameValues に挿入する
w ##class(ChangeLog.DB.NameValues).CreateOrUpdate("name", "value1")
  1. 以下を使用してそのインスタンスを更新する
w ##class(ChangeLog.DB.NameValues).CreateOrUpdate("name", "value2")
  1. 以下を使用してすべてのレコードを削除する
delete FROM ChangeLog_DB.NameValues

変更ログは以下のようになりました。

IDActionTableNameRecordIdUsernameTimestampNewDataOldData
1CreateNameValues1_SYSTEM2023-11-27T12:52:05.8768627Z{"name":"name","value":"value1"}
2UpdateNameValues1_SYSTEM2023-11-27T12:52:09.7573293Z{"value":"value2"}{"name":"name","value":"value1"}
3DeleteNameValues1_SYSTEM2023-11-27T12:53:15.2558132Z{"name":"name","value":"value2"}

次に、顧客プロジェクトに定義される %Persistent を拡張するすべての 12 個のクラスを、代わりに ChangeLog.DB.PersistentWithChangeLog を拡張するように変更しました。 これにより、いくつかの変更が発生しました(上記のコードにすでに存在します)。

  1. デフォルトでクラスの一部であるプロパティ***%%OID*** と %Concurrency を除外することにしました。
  2. 一時的なプロパティは、SQL プロパティとして存在しないため、除外する必要があります。
  3. 古いデータと新しいデータを JSON としてログしているため、定義されるときにプロパティ名として "%JSONFIELDNAME" パラメーターを使用するのが合理的でした。

"UniqueIndex" という一意のインデックスを使うクラスが %Persistent を拡張する際にコンパイルされるが、ChangeLog.DB.PersistentWithChangeLog を拡張する際にはコンパイルされないという説明のつかない問題に 1 つ遭遇しました。 この問題は、インデックス名を UniqueIndex2 に変更すると解消されました。

最後になりましたが、[ CodeMode = objectgenerator ] の能力には本当にワクワクさせられています。この記事があなたの役に立てられることを願っています!

0
0 39
記事 Tomoko Furuzono · 3月 29, 2022 2m read

これは、InterSystems FAQサイトの記事です。
 

%SQL.Util.Proceduresクラスの CSV() プロシジャを使用することにより、実現できます。
下記が使用例のコードとなります。(test.csvというファイルが c:\temp にあるという前提)

 Set rowtype="Name VARCHAR(50),UID VARCHAR(50), PHONE VARCHAR(50)"
 Set filename="c:\temp\test.csv"
 Set result=##class(%SQL.Statement).%ExecDirect(,"call %SQL_Util.CSV(,?,?)",.rowtype,filename)
 Set rset =result.%NextResult()
 
 // 全件表示したい場合は、do rset.%Display()
 While rset.%Next() {
     Write "Name:",rset.%GetData(1)," UID:",rset.%GetData(2)," PHONE:",rset.%GetData(3),!
     }

 Set rset="",result=""
 Quit

上記を実行することにより結果セットとして各行およびフィールドにアクセスできます。
実行例:

3
1 636
記事 Mihoko Iijima · 9月 29, 2020 2m read

これはInterSystems FAQ サイトの記事です。


LAST_IDENTITY() SQL関数を使用すると取得できます。
※ この関数は、埋め込み SQL または ODBC 利用時に使用できます。ダイナミック SQL、SQL シェル、または管理ポータルの SQL インタフェースによる値には設定されません。

簡単な埋め込み SQL での例をご紹介します。

1
0 1088
記事 Megumi Kakechi · 4月 22, 2025 5m read

先日、お客様よりタイトルのご質問をいただき、サンプルコードを作成しました。せっかくですので、こちらでも共有したいと思います。

今回は、データベースの空き容量情報を取得する、%SYS.DatabaseQueryクラスのFreeSpaceクエリを使用したサンプルとします。

C#.Net と VB.Net で作成してみました。


★C#.Net

0
0 55
記事 Toshihiko Minamoto · 4月 11, 2025 10m read

django_logo

説明

これは、ネイティブウェブアプリケーションとして IRIS にデプロイできる Django アプリケーションのテンプレートです。

インストール

  1. リポジトリをクローンする
  2. 仮想環境を作成する
  3. 要件をインストールする
  4. docker-compose ファイルを実行する
git clone
cd iris-django-template
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
docker-compose up

使用法

ベース URL は http://localhost:53795/django/ です。

エンドポイント

  • /iris - IRISAPP ネームスペースに存在する上位 10 個のクラスを持つ JSON オブジェクトを返します。
  • /interop - IRIS の相互運用性フレームワークをテストするための ping エンドポイント。
  • /api/posts - Post オブジェクトの単純な CRUD エンドポイント。
  • ``/api/comments` - Comment オブジェクトの単純な CRUD エンドポイント。

このテンプレートからの開発方法

WSGI 導入記事をご覧ください: wsgi-introduction

概要: セキュリティポータルで DEBUG フラグをトグルすると、開発作業の過程で変更内容がアプリケーションに反映されるようになります。

コードの説明

Django アプリケーションは次のように構造化されています。

  • app - Django プロジェクトフォルダ
    • app - 構成用の Django アプリフォルダ
      • settings.py - Django の設定ファイル
      • urls.py - ビューを URL に接続する Django URL 構成ファイル
      • wsgi.py - Django WSGI ファイル
      • asgi.py - Django ASGI ファイル
    • community - コミュニティアプリの Django アプリフォルダ、Post と Comment オブジェクトでの CRUD 操作
      • models.py - Post と Comment オブジェクトの Django モデルファイル
      • views.py - Post と Comment オブジェクトにアクセスするための Django ビューファイル
      • serializers.py - Post と Comment オブジェクトの Django シリアライザーファイル
      • admin.py - 管理インターフェースに CRUD 操作を追加する Django 管理ファイル
      • migrations - データベースを構築するための Django マイグレーションフォルダ
      • fixtures - Django fixtures フォルダデモデータ
    • sqloniris - IRIS アプリでの SQL に使用する Django アプリフォルダ
      • views.py - IRISAPP ネームスペースをクエリするための Django ビューファイル
      • apps.py - Django アプリ構成ファイル
    • interop - 相互運用性アプリ用の Django アプリフォルダ
      • views.py - 相互運用性フレームワークをテストするための Django ビューファイル
      • apps.py - Django アプリ構成ファイル
    • manage.py - Django 管理ファイル

app/settings.py

このファイルには、アプリケーションの Django 設定が含まれます。

...

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'community',
    'sqloniris',
    'interop',
    'rest_framework'
]

...

REST_FRAMEWORK = {
    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 20
}

...

DATABASES = {
    "default": {
        "ENGINE": "django_iris",
        "EMBEDDED": True,
        "NAMESPACE": "IRISAPP",
        "USER":"SuperUser",
        "PASSWORD":"SYS",
    }
}

一部の重要な設定:

  • INSTALLED_APPS - Django プロジェクトにインストール済みのアプリのリストが含まれます。
    • community - Post と Comment オブジェクトでの CRUD 操作用 Django アプリ。
    • sqloniris - IRIS での SQL 操作に使用する Django アプリ。
    • interop - 相互運用性操作用の Django アプリ。
    • rest_framework - REST API 用の Django REST フレームワーク。
  • REST_FRAMEWORK - Django REST フレームワークの設定が含まれます。
    • DEFAULT_PERMISSION_CLASSES - 認証済みのユーザーのみが CRUD 操作を実行できます。
    • DEFAULT_PAGINATION_CLASS - REST API のページネーションクラス。
  • DATABASES - IRIS データベース接続の設定が含まれます。
    • ここでは、django_iris エンジンを使って IRIS データベースに接続しています。

app/urls.py

このファイルには、Django アプリケーションの URL 構成が含まれます。

from django.contrib import admin
from django.urls import path,include
from rest_framework import routers
from community.views import PostViewSet, CommentViewSet
from sqloniris.views import index
from interop.views import index as interop_index

router = routers.DefaultRouter()
router.register(r'posts', PostViewSet)
router.register(r'comments', CommentViewSet)


urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include(router.urls)),
    path('iris/', index),
    path('interop/', interop_index)
]
  • router - REST API のデフォルトのルーターが含まれます。
  • routeer.register - Post と Comment ビューセットをルーターに登録します。
  • urlpatterns - Django アプリケーションの URL パターンが含まれます。
    • /admin/ - Django 管理インターフェース。
    • /api/ - Post と Comment オブジェクトの REST API。
    • /iris/ - IRIS エンドポイントでの SQL。
    • /interop/ - 相互運用性エンドポイント。

app/wsgi.py

このファイルには、Django アプリケーションの WSGI 構成が含まれます。

これが、Django アプリケーションを実行するために IRIS に提供する必要のあるファイルです。

Security->Applications->Web Applications セクションで、このファイルへのパスを指定する必要があります。

  • アプリケーション名
    • app.wsgi
  • コーラブル名
    • application
  • WSGI アプリディレクトリ
    • /irisdev/app/app

community/models.py

このファイルには、Post と Comment オブジェクトの Django モデルが含まれます。

from django.db import models

# Create your models here.
class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()

class Comment(models.Model):
    content = models.TextField()
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
  • Post - Post オブジェクトのモデル。
    • title - 投稿のタイトル。
    • content - 投稿のコンテンツ。
  • Comment - Comment オブジェクトのモデル。
    • content - コメントのコンテンツ。
    • post - Post オブジェクトの外部キー。
    • related_name - コメントの関連名。

community/seializers.py

このファイルには、Post と Comment オブジェクトの Django シリアライザーが含まれます。

Django REST フレームワークでは、Django モデルを JSON オブジェクトにシリアル化できます。

from rest_framework import serializers
from community.models import Post, Comment

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ('id', 'title', 'content', 'comments')

class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = ('id', 'content', 'post')
  • PostSerializer - Post オブジェクトのシリアライザー。
  • CommentSerializer - Comment オブジェクトのシリアライザー。
  • fields - シリアル化されるフィールド。

community/views.py

このファイルには、Post と Comment オブジェクトの Django ビューが含まれます。

Django REST フレームワークを使て、Django モデルの CRUD 操作を作成できます。

from django.shortcuts import render
from rest_framework import viewsets

# Import the Post and Comment models
from community.models import Post, Comment

# Import the Post and Comment serializers
from community.serializers import PostSerializer, CommentSerializer

# Create your views here.
class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

class CommentViewSet(viewsets.ModelViewSet):
    queryset = Comment.objects.all()
    serializer_class = CommentSerializer
  • PostViewSet - Post オブジェクトのビューセット。
  • CommentViewSet - Comment オブジェクトのビューセット。
  • queryset - ビューセットのクエリセット。
  • serializer_class - ビューセットのシリアライザークラス。

sqloniris/views.py

このファイルには、IRIS 操作における SQL の Django ビューが含まれます。

from django.http import JsonResponse

import iris

def index(request):
    query = "SELECT top 10 * FROM %Dictionary.ClassDefinition"
    rs = iris.sql.exec(query)
    # Convert the result to a list of dictionaries
    result = []
    for row in rs:
        result.append(row)
    return JsonResponse(result, safe=False)
  • index - IRIS 操作における SQL のビュー。
  • query - IRIS データベースで実行される SQL クエリ。
  • rs - クエリの結果セット。
  • result - 結果セットからのリストのリスト。
  • JsonResponse - ビューの JSON レスポンス。リストのリスト表示を許可するには safe を False に設定します。

interop/views.py

このファイルには、相互運用性操作における SQL の Django ビューが含まれます。

from django.http import HttpResponse

from grongier.pex import Director

bs = Director.create_python_business_service('BS')

def index(request):
    result = bs.on_process_input(request)
    return HttpResponse(result, safe=False)
  • bs - Director クラスを使用して作成されるビジネスサービスオブジェクト。
  • index - 相互運用性操作のビュー。
  • result - ビジネスサービスの結果。

注: コードを単純化するために JsonResponse は使用しません。JSON オブジェクトを返す場合は使用できます。

トラブルシューティング

スタンドアロンモードで Django アプリケーションを実行する方法

スタンドアロンモードで Django アプリケーションを実行するには、以下のコマンドを使用できます。

cd /irisdev/app/app
python3 manage.py runserver 8001

これは、デフォルトのポート 8001 で Django アプリケーションを実行します。

注: このコマンドを実行するには、コンテナー内にいる必要があります。

docker exec -it iris-django-template-iris-1 bash

IRIS でアプリケーションを再起動する

DEBUG モードでアプリケーションに複数の呼び出しを行うと、変更はアプリケーションに反映されます。

IRIS 管理ポータルへのアクセス方法

http://localhost:53795/csp/sys/UtilHome.csp に移動すると、IRIS 管理ポータルにアクセスできます。

このテンプレートをローカルで実行する

これには、マシンに IRIS がインストールされている必要があります。

次に、IRISAPP というネームスペースを作成する必要があります。

要件をインストールします。

# Move to the app directory
cd /irisdev/app/app

# python manage.py flush --no-input
python3 manage.py migrate
# create superuser
export DJANGO_SUPERUSER_PASSWORD=SYS
python3 manage.py createsuperuser --no-input --username SuperUser --email admin@admin.fr

# load demo data
python3 manage.py loaddata community/fixtures/demo.json

# collect static files
python3 manage.py collectstatic --no-input --clear

# init iop
iop --init

# load production
iop -m /irisdev/app/app/interop/settings.py

# start production
iop --start Python.Production

静的ファイルの配信方法

Django アプリケーションで静的ファイルを配信するには、以下のコマンドを使用できます。

cd /irisdev/app
python3 manage.py collectstatic

これは、Django アプリケーションから静的ファイルを収集して、/irisdev/app/static ディレクトリに配信します。

IRIS で静的ファイルを公開するには、Security->Applications->Web Applications セクションを構成します。

web_applications

0
0 37
記事 Hiroshi Sato · 4月 2, 2025 3m read

Mac版のIRISにSQLを使用して他ツールからアクセスするケースはそもそも少ないと思いますが、DBeaverにJDBCを使用してアクセスできることはこのコミュニティの住人であれば、知っている人は結構いるかと思います。

しかし今回ちょっと理由があってMac上のIRISにODBCを使ってアクセスする方法についてトライしてみました。

ここではその備忘録を書き留めておこうと思います。

実際の所、Mac上のクライアントツールでODBCでアクセスできるツールもそんなにないのですが、

候補としてMS-Excel(MS-Query経由)またはLibreOfficeがありました。

まず結論としてExcelは色々とトライしましたが、原因不明ですがうまくつながりませんでした。

(どうもExcel(MS-Query)が拒絶している感じです)

LibreOfficeは何とか接続でき、データの取得はできる様になりました。

まず、前準備としてODBC Driver Managerというものをセットアップする必要があります。

細かくいうとこれにもiODBCとUnixODBCの2系統があるのですが、ExcelおよびLibreOfficeはiODBCにしか対応していない感じです。
(これは正確な情報ではない可能性はあります)

0
0 43
記事 Tomoko Furuzono · 3月 31, 2025 2m read

これは、InterSystems FAQ サイトの記事です。
 

小数点桁数を指定しない単純な整数への切り上げ・切り捨ては、それぞれ、以下の関数で実行できます。

(SQL関数)

切り上げ:CEILING
切り捨て:FLOOR

(ObjectScript関数)

切り上げ: $system.SQL.Functions.CEILING()
切り捨て: $system.SQL.Functions.FLOOR()

USER>write $system.SQL.Functions.CEILING(168.5)
169
USER>write $system.SQL.Functions.FLOOR(168.5)
168

※バージョン2021.1以前は以下のメソッドを使用します。

 切り上げ: $system.SQL.CEILING()
 切り捨て: $system.SQL.FLOOR()


小数桁数を指定して切り上げ・切り捨てを行いたい場合は、2つの関数を組み合わせ、以下のようなメソッドを作成して対応します。

0
0 88
記事 Tomoko Furuzono · 3月 31, 2025 1m read

これは、InterSystems FAQ サイトの記事です。
 

ウィンドウ関数は、結果セットを部分的に切り出した領域に集約関数を適用できるもので、WHERE GROUP BY および HAVING 節が適用された後、SELECT クエリで選択された行に対して作用します。
IRIS/IRIS for Health 2021.1からサポートしています。
サポートされるウィンドウ関数は以下の通りです。

  • FIRST_VALUE(field)
  • PERCENT_RANK()
  • RANK()
  • ROW_NUMBER()
  • SUM(field)

詳細については、下記ドキュメントページをご確認ください。
ウィンドウ関数の概要
関連記事:IRIS SQLでは OFFSET/LIMIT句のような機能をサポートしてますか?
                  IRIS SQLクエリで取得した結果セットのランキング(順位)を算出する方法

0
0 45
記事 Toshihiko Minamoto · 3月 27, 2025 8m read

fastapi_logo

説明

これは、ネイティブウェブアプリケーションとして IRIS にデプロイできる FastAPI アプリケーションのテンプレートです。

インストール

  1. リポジトリをクローンする
  2. 仮想環境を作成する
  3. 要件をインストールする
  4. docker-compose ファイルを実行する
git clone
cd iris-fastapi-template
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
docker-compose up

使用法

ベース URL は http://localhost:53795/fastapi/ です。

エンドポイント

  • /iris - IRISAPP ネームスペースに存在する上位 10 個のクラスを持つ JSON オブジェクトを返します。
  • /interop - IRIS の相互運用性フレームワークをテストするための ping エンドポイント。
  • /posts - Post オブジェクトの単純な CRUD エンドポイント。
  • /comments - Comment オブジェクトの単純な CRUD エンドポイント。

このテンプレートからの開発方法

WSGI 導入記事をご覧ください: wsgiサポートの概要

概要: セキュリティポータルで DEBUG フラグをトグルすると、開発作業の過程で変更内容がアプリケーションに反映されるようになります。

コードの説明

app.py

これは FastAPI アプリケーションのメインのファイルです。 FastAPI アプリケーションとルートが含まれます。

from fastapi import FastAPI, Request

import iris

from grongier.pex import Director

# import models
from models import Post, Comment, init_db
from sqlmodel import Session,select

app = FastAPI()

# create a database engine
url = "iris+emb://IRISAPP"
engine = init_db(url)
  • from fastapi import FastAPI, Request - FastAPI クラスト Request クラスをインポートします。
  • import iris - IRIS モジュールをインポートします。
  • from grongier.pex import Director: Flask アプリを IRIS 相互運用性フレームワークにバインドする Director クラスをインポートします。
  • from models import Post, Comment, init_db - モデルと init_db 関数をインポートします。
  • from sqlmodel import Session,select - Session クラスと sqlmodel モジュールの選択された関数をインポートします。
  • app = FastAPI() - FastAPI アプリケーションを作成します。
  • url = "iris+emb://IRISAPP" - IRIS ネームスペースの URL を定義します。
  • engine = init_db(url) - sqlmodel ORM のデータベースエンジンを作成します。

models.py

このファイルには、アプリケーションのモデルが含まれます。

from sqlmodel import Field, SQLModel, Relationship, create_engine

class Comment(SQLModel, table=True):
    id: int = Field(default=None, primary_key=True)
    post_id: int = Field(foreign_key="post.id")
    content: str
    post: "Post" = Relationship(back_populates="comments")

class Post(SQLModel, table=True):
    id: int = Field(default=None, primary_key=True)
    title: str
    content: str
    comments: list["Comment"] = Relationship(back_populates="post")

説明することは特にありません。外部キーとリレーションによる単なるモデルの定義です。

init_db 関数は、データベースエンジンの作成に使用されます。

def init_db(url):

    engine = create_engine(url)

    # create the tables
    SQLModel.metadata.drop_all(engine)
    SQLModel.metadata.create_all(engine)

    # initialize database with fake data
    from sqlmodel import Session

    with Session(engine) as session:
        # Create fake data
        post1 = Post(title='Post The First', content='Content for the first post')
        ...
        session.add(post1)
        ...
        session.commit()

    return engine
  • engine = create_engine(url) - データベースエンジンを作成します。
  • SQLModel.metadata.drop_all(engine) - すべてのテーブルをドロップします。
  • SQLModel.metadata.create_all(engine) - すべてのテーブルを作成します。
  • with Session(engine) as session: - データベースを操作するためのセッションを作成します。
  • post1 = Post(title='Post The First', content='Content for the first post') - Post オブジェクトを作成します。
  • session.add(post1) - Post オブジェクトをセッションに追加します。
  • session.commit() - 変更内容をデータベースにコミットします。
  • return engine - データベースエンジンを返します。

/iris エンドポイント

######################
# IRIS Query example #
######################

@app.get("/iris")
def iris_query():
    query = "SELECT top 10 * FROM %Dictionary.ClassDefinition"
    rs = iris.sql.exec(query)
    # Convert the result to a list of dictionaries
    result = []
    for row in rs:
        result.append(row)
    return result
  • @app.get("/iris") - /iris エンドポイントの GET ルートを定義します。
  • query = "SELECT top 10 * FROM %Dictionary.ClassDefinition" - IRIS ネームスペースで上位 10 個のクラスを取得するクエリを定義します。
  • rs = iris.sql.exec(query) - クエリを実行します。
  • result = [] - 結果を保存する空のリストを作成します。
  • for row in rs: - 結果セットを反復処理します。
  • result.append(row) - 結果リストを行にアペンドします。
  • return result - 結果リストを返します。

/interop エンドポイント

########################
# IRIS interop example #
########################
bs = Director.create_python_business_service('BS')

@app.get("/interop")
@app.post("/interop")
@app.put("/interop")
@app.delete("/interop")
def interop(request: Request):
    
    rsp = bs.on_process_input(request)

    return rsp

  • bs = Director.create_python_business_service('BS') - Python ビジネスサービスを作成します。
    • ビジネスサービスの複数のインスタンスを防止するために、ルート定義の外に作成する必要があります。
  • @app.get("/interop") - /interop エンドポイントの GET ルートを定義します。
  • @app.post("/interop") - /interop エンドポイントの POST ルートを定義します。
  • ...
  • def interop(request: Request): - ルートハンドラーを定義します。
  • rsp = bs.on_process_input(request) - ビジネスサービスの on_process_input メソッドを呼び出します。
  • return rsp - レスポンスを返します。

/posts エンドポイント

############################
# CRUD operations posts    #
############################

@app.get("/posts")
def get_posts():
    with Session(engine) as session:
        posts = session.exec(select(Post)).all()
        return posts
    
@app.get("/posts/{post_id}")
def get_post(post_id: int):
    with Session(engine) as session:
        post = session.get(Post, post_id)
        return post
    
@app.post("/posts")
def create_post(post: Post):
    with Session(engine) as session:
        session.add(post)
        session.commit()
        return post

このエンドポイントは、Post オブジェクトで CRUD 操作を実行するために使用されます。

説明することは特にありません。すべての投稿を取得し、ID で投稿を取得し、投稿を作成するためのルートの定義です。

すべては sqlmodel ORM を使って行われます。

/comments エンドポイント

############################
# CRUD operations comments #
############################


@app.get("/comments")
def get_comments():
    with Session(engine) as session:
        comments = session.exec(select(Comment)).all()
        return comments
    
@app.get("/comments/{comment_id}")
def get_comment(comment_id: int):
    with Session(engine) as session:
        comment = session.get(Comment, comment_id)
        return comment
    
@app.post("/comments")
def create_comment(comment: Comment):
    with Session(engine) as session:
        session.add(comment)
        session.commit()
        return comment

このエンドポイントは、Comment オブジェクトで CRUD 操作を実行するために使用されます。

説明することは特にありません。すべてのコメントを取得し、ID でコメントを取得し、コメントを作成するためのルートの定義です。

すべては sqlmodel ORM を使って行われます。

トラブルシューティング

スタンドアロンモードで FastAPI アプリケーションを実行する方法

以下のコマンドを使用して、いつでもスタンドアロンの Flask アプリケーションを実行できます。

python3 /irisdev/app/community/app.py

注: このコマンドを実行するには、コンテナー内にいる必要があります。

docker exec -it iris-fastapi-template-iris-1 bash

IRIS でアプリケーションを再起動する

DEBUG モードでアプリケーションに複数の呼び出しを行うと、変更はアプリケーションに反映されます。

IRIS 管理ポータルへのアクセス方法

http://localhost:53795/csp/sys/UtilHome.csp に移動すると、IRIS 管理ポータルにアクセスできます。

このテンプレートをローカルで実行する

これには、マシンに IRIS がインストールされている必要があります。

次に、IRISAPP というネームスペースを作成する必要があります。

要件をインストールします。

IoP のインストール:

#init iop
iop --init

# load production
iop -m /irisdev/app/community/interop/settings.py

# start production
iop --start Python.Production

セキュリティポータルでアプリケーションを構成します。

0
0 75
InterSystems公式 Masahito Miura · 3月 27, 2025

インターシステムズは、InterSystems IRIS®data platformInterSystems IRIS® for HealthTM、および HealthShare® Health Connect の 2025.1 リリースを一般提供 (GA) したことを発表しました。2025.1 は、拡張メンテナンス(EM)リリースです。
リリースハイライト
今回のリリースには、以下のような数々の興味深いアップデートが含まれます:

0
0 120
記事 Tomoko Furuzono · 3月 25, 2025 1m read

これは、InterSystems FAQサイトの記事です。
 

SQLでクエリ実行時、ORDER BYで並べ替えをする場合、各RDBMSによって、照合順が異なることがあります。

たとえば、NULLと空文字の混じった文字列のカラムを並べ替える場合、

IRIS SQLでは、既定の照合順は下記のようになりますが、 

NULL, 空文字, 0, 00, 01, 1, 10, 100, 11, A, a, B, b

Oracleでは、下記のような照合順になります。

空文字, 0, 00, 01, 1, 10, 100, 11, A, B, a, b, NULL


複数のDB由来のデータを取り扱う際には、このような照合順の違いを合わせたい場合があります。

IRISの場合、既定の文字列照合はSQLUPPERですが、照合タイプを変更することによって、照合順を変えることが出来ます。
ドキュメント:照合

上記のIRIS SQLとOracleの違いを合わせるためには、照合タイプを「SQLSTRING」に設定し、インデックスを作成します。

Property TestColumn As%String(COLLATION = "SQLSTRING"); 
Index IdxTest On TestColumn;
0
0 62
InterSystems公式 Seisuke Nakahashi · 3月 12, 2025

InterSystems はこのたび「テーブル・パーティショニング機能」の早期アクセスプログラムを開始しました。これにより、IRIS をお使いのお客様が非常に大きなテーブルを管理したり、行データや関連インデックスをデータベースやストレージ階層間で分散できるようになります。テーブル・パーティショニングは IRIS のリレーショナル・データ管理の核心深くに関連する機能であるため、初期段階でのフィードバックを提供いただけたり、状況に応じて機能調整にご協力いただける、少数の熱心なお客様と一緒になって、確実な機能実装を進めたいと考えています。

非常に大規模なリレーショナル・データセットをご利用中で、より効率的な運用を望まれており、新機能を試してみたいというお客様は、ぜひ https://www.intersystems.com/early-access-program/ からご登録ください。登録された方には、一時的な開発ライセンス、新機能を含む最新キットとコンテナ・イメージ、チュートリアル がすべて掲載された評価ポータルをご案内するメールをお送りします。

0
0 41
記事 Hiroshi Sato · 3月 4, 2025 4m read

これは InterSystems FAQ サイトの記事です。
 

計算プロパティを定義する際に利用可能なキーワードが複数あります。

詳細は、以下をご参照ください。

計算プロパティの定義

実際のこれらのキーワードの関連性は、少々複雑ですので具体的なコードを作成して動作を確認してみます。 

以下のようなクラス定義を作成します。(プロパティとインデックス定義のみ表示します)

完全なクラス定義は以下より、ダウンロードできます。

サンプルクラス定義

0
0 57
記事 Hiroshi Sato · 3月 3, 2025 1m read

これは InterSystems FAQ サイトの記事です。
 

IRISには、データ項目の値を実体として持たずに、何らかの演算処理の結果として提供する機能があります。

これを計算プロパティまたは計算フィールドといいます。

計算プロパティを定義するためには、最低限以下の手順を実行します。

プロパティ定義にSqlComputedキーワードを含めます。

プロパティ定義にSqlComputedCodeを含めて、値を算出するための処理ロジックとして含めます。

または、SqlComputedCodeを含めずに、代わりに<プロパティ名>Computationという名前のクラスメソッドを記述します。

以下は、Age(年齢)プロパティを計算プロパティとして定義した例になります。

0
0 38
記事 Toshihiko Minamoto · 2月 27, 2025 7m read

Flask_logo

説明

これは、ネイティブウェブアプリケーションとして IRIS にデプロイできる Flask アプリケーションのテンプレートです。

インストール

  1. リポジトリをクローンする
  2. 仮想環境を作成する
  3. 要件をインストールする
  4. docker-compose ファイルを実行する
git clone
cd iris-flask-template
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
docker-compose up

使用法

ベース URL は http://localhost:53795/flask/ です。

エンドポイント

  • /iris - IRISAPP ネームスペースに存在する上位 10 個のクラスを持つ JSON オブジェクトを返します。
  • /interop - IRIS の相互運用性フレームワークをテストするための ping エンドポイント。
  • /posts - Post オブジェクトの単純な CRUD エンドポイント。
  • /comments - Comment オブジェクトの単純な CRUD エンドポイント。

このテンプレートからの開発方法

WSGI 導入記事をご覧ください: wsgi-introduction

概要: セキュリティポータルで DEBUG フラグをトグルすると、開発作業の過程で変更内容がアプリケーションに反映されるようになります。

コードの説明

app.py

これはアプリケーションのメインのファイルです。 Flask アプリケーションとエンドポイントが含まれます。

from flask import Flask, jsonify, request
from models import Comment, Post, init_db

from grongier.pex import Director

import iris

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'iris+emb://IRISAPP'

db = init_db(app)
  • from flask import Flask, jsonify, request: Flask ライブラリをインポートします。
  • from models import Comment, Post, init_db: モデルとデータベース初期化関数をインポートします。
  • from grongier.pex import Director: Flask アプリを IRIS 相互運用性フレームワークにバインドする Director クラスをインポートします。
  • import iris: IRIS ライブラリをインポートします。
  • app = Flask(__name__): Flask アプリケーションを作成します。
  • app.config['SQLALCHEMY_DATABASE_URI'] = 'iris+emb://IRISAPP': データベース URI を IRISAPP ネームスペースに設定します。
    • iris+emb URI スキームは、埋め込み接続として IRIS に接続するために使用されます(別の IRIS インスタンスの必要はありません)。
  • db = init_db(app): Flask アプリケーションでデータベースを初期化します。

models.py

このファイルには、アプリケーションの SQLAlchemy モデルが含まれます。

from dataclasses import dataclass
from typing import List
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

@dataclass
class Comment(db.Model):
    id:int = db.Column(db.Integer, primary_key=True)
    content:str = db.Column(db.Text)
    post_id:int = db.Column(db.Integer, db.ForeignKey('post.id'))

@dataclass
class Post(db.Model):
    __allow_unmapped__ = True
    id:int = db.Column(db.Integer, primary_key=True)
    title:str = db.Column(db.String(100))
    content:str = db.Column(db.Text)
    comments:List[Comment] = db.relationship('Comment', backref='post')

説明することは特にありません。モデルはデータクラスとして定義されており、db.Model クラスのサブクラスです。

__allow_unmapped__ 属性は、comments 属性を使用せずに Post オブジェクトを作成できるようにするために使用する必要があります。

dataclasses はオブジェクトを JSON にシリアル化するのに使用されます。

init_db 関数は、Flask アプリケーションでデータベースを初期化します。

def init_db(app):
    db.init_app(app)

    with app.app_context():
        db.drop_all()
        db.create_all()
        # Create fake data
        post1 = Post(title='Post The First', content='Content for the first post')
        ...
        db.session.add(post1)
        ...
        db.session.commit()
    return db
  • db.init_app(app): Flask アプリケーションでデータベースを初期化します。
  • with app.app_context(): アプリケーションのコンテキストを作成します。
  • db.drop_all(): データベースのすべてのテーブルをドロップします。
  • db.create_all(): データベースのすべてのテーブルを作成します。
  • アプリケーションの偽データを作成します。
  • データベースオブジェクトを返します。

/iris エンドポイント

######################
# IRIS クエリ例 #
######################

@app.route('/iris', methods=['GET'])
def iris_query():
    query = "SELECT top 10 * FROM %Dictionary.ClassDefinition"
    rs = iris.sql.exec(query)
    # Convert the result to a list of dictionaries
    result = []
    for row in rs:
        result.append(row)
    return jsonify(result)

このエンドポイントは、IRIS データベースでクエリを実行し、IRISAPP ネームスペースに存在する上位 10 個のクラスを返します。

/interop エンドポイント

########################
# IRIS interop example #
########################
bs = Director.create_python_business_service('BS')

@app.route('/interop', methods=['GET', 'POST', 'PUT', 'DELETE'])
def interop():
    
    rsp = bs.on_process_input(request)

    return jsonify(rsp)

このエンドポイントは、IRIS の相互運用性フレームワークをテストするために使用されます。 ビジネスサービスオブジェクトを作成し、それを Flask アプリケーションにバインドします。

注: bs オブジェクトは有効な状態を維持するために、リクエストの範囲外にある必要があります。

  • bs = Director.create_python_business_service('BS'): 'BS' というビジネスサービスオブジェクトを作成します。
  • rsp = bs.on_process_input(request): リクエストオブジェクトを引数としてビジネスサービスオブジェクトの on_process_input メソッドを呼び出します。

/posts エンドポイント

############################
# CRUD operations posts    #
############################

@app.route('/posts', methods=['GET'])
def get_posts():
    posts = Post.query.all()
    return jsonify(posts)

@app.route('/posts', methods=['POST'])
def create_post():
    data = request.get_json()
    post = Post(title=data['title'], content=data['content'])
    db.session.add(post)
    db.session.commit()
    return jsonify(post)

@app.route('/posts/<int:id>', methods=['GET'])
def get_post(id):
    ...

このエンドポイントは、Post オブジェクトで CRUD 操作を実行するために使用されます。

dataclasses モジュールにより、Post オブジェクトは簡単に JSON にシリアル化できます。

以下では、すべての投稿を取得する sqlalchemy の query メソッドと、新しい投稿を作成するための addcommit メソッドを使用しています。

/comments エンドポイント

############################
# CRUD operations comments #
############################

@app.route('/comments', methods=['GET'])
def get_comments():
    comments = Comment.query.all()
    return jsonify(comments)

@app.route('/comments', methods=['POST'])
def create_comment():
    data = request.get_json()
    comment = Comment(content=data['content'], post_id=data['post_id'])
    db.session.add(comment)
    db.session.commit()
    return jsonify(comment)

@app.route('/comments/<int:id>', methods=['GET'])
def get_comment(id):
    ...

このエンドポイントは、Comment オブジェクトで CRUD 操作を実行するために使用されます。

Comment オブジェクトは外部キーによって Post オブジェクトにリンクされます。

トラブルシューティング

スタンドアロンモードで Flask アプリケーションを実行する方法

以下のコマンドを使用して、いつでもスタンドアロンの Flask アプリケーションを実行できます。

python3 /irisdev/app/community/app.py

注: このコマンドを実行するには、コンテナー内にいる必要があります。

docker exec -it iris-flask-template-iris-1 bash

IRIS でアプリケーションを再起動する

DEBUG モードでアプリケーションに複数の呼び出しを行うと、変更はアプリケーションに反映されます。

IRIS 管理ポータルへのアクセス方法

http://localhost:53795/csp/sys/UtilHome.csp に移動すると、IRIS 管理ポータルにアクセスできます。

このテンプレートをローカルで実行する

これには、マシンに IRIS がインストールされている必要があります。

次に、IRISAPP というネームスペースを作成する必要があります。

要件をインストールします。

IoP のインストール:

#init iop
iop --init

# load production
iop -m /irisdev/app/community/interop/settings.py

# start production
iop --start Python.Production

セキュリティポータルでアプリケーションを構成します。

0
0 69
記事 Megumi Kakechi · 2月 26, 2025 2m read

これは InterSystems FAQ サイトの記事です。
埋め込みSQLの出力ホスト変数は、SQLCODE=0(埋め込みSQL正常終了)の場合のみ、正しい値が設定されていることが保証されます。

InterSystems製品のバージョンによっては、SQLCODEが0以外の場合(該当データがない100やエラー等)で値が設定される場合もありますが、その値は無効です。

特に、IRIS2021.1以降のバージョンでは、SQLCODE=100 の場合、INTO 節で指定された出力ホスト変数は NULL("") にクリアされますので注意が必要です。

Cacheや、IRIS2020.x 以前のバージョンでは、明示的な値のクリアを行っておりませんでしたが、こちらについても値は保証されるものではありません。

埋め込みSQLを使用する場合は、必ずSQLCODEを確認してエラーチェックを行うようにして下さい。
また、エラーチェック以外でも、SQLCODE = 0(データあり) の場合と SQLCODE = 100(データなし) の場合は処理を分けるようにし、SQLCODE = 100 の場合は出力ホスト変数を参照しないようご注意ください。


例)
誤った使用例:

0
0 42
InterSystems公式 Masahito Miura · 2月 21, 2025

2025 年 2 月 15 日 – 警告:SQLクエリが間違った結果を返す

インターシステムズは、SQL クエリが不正な結果を返す原因となる 2 つの問題を修正しました。さらに、日付/時刻データ型の処理における不整合を修正しました。この日付/時刻データ型の処理の修正により以前の不整合な動作に依存していた既存のアプリケーションでは、異なる予期しない(正しい)結果が返される可能性があります。

DP-436825: ラテラル結合を使用したSQLクエリが間違った結果を返すことがある

0
0 52
質問 Yuji Ohata · 11月 22, 2024

こんにちは、皆さま。
業務でIRISを用いて開発を行っている者です。

IRISに直接Insert文を発行する時と、JDBCを経由してInsert文を発行する時とで、
挙動差異があるように見受けれれまいたので、何かご存知な方がいらっしゃれば教えてください。

次の様なテーブルがあります。

それに対してDBeaverとIRIS管理ポータルからInsertを実行します。

★DBeaver

★IRIS管理ポータル

この後実際に作成されたグローバルの情報を確認すると、
『VARCHAR型』で作成したカラムに数値をInsertした際に、
実データの型が異なっていることがわかります。

DBeaver(JDBC) ➡ 文字列で登録
IRIS直     ➡ 数値で登録

個人的には『VARCHAR型』に数値が入ること自体がおかしいので、
DBeaverの方がまし(ホントは型違反とかになってほしい)と思うのですが、
この差異が何で発生するのか、また回避する方法をご存じな方がいらっしゃれば情報共有頂きたいです。

また、そもそもIRISはRDBを使っても型チェックが曖昧になるものなのかも気になっています。
さすがに数値のカラムに文字列を入れようとすると怒られるのですが、
上記の様な場合にチェックの仕組みが働かないのには違和感がありました。

3
0 130
質問 Yuji Ohata · 12月 4, 2024

こんにちは、皆さま。
業務でIRISを用いて開発を行っている者です。

私自身SQLがあまり得意ではなく、
業務で書いてみたもののパフォーマンスがよくなく、
不要なループが含まれていた、無駄な検索条件が含まれている…なんてことが多々あります。

IRISの管理コンソールなどにはいろんな機能があると思うのですが、
パフォーマンス改善をするために利用できるツールなどはあったりしますでしょうか?

何かご存知の方がいらっしゃいましたら、情報共有頂けますと幸いです。

2
0 52
記事 Toshihiko Minamoto · 11月 21, 2024 6m read

image
コミュニティの皆さん、こんにちは。
この記事では、iris-RAG-Gen という私のアプリケーションをご紹介します。

iris-RAG-Gen は、IRIS Vector Search の機能を使用して、Streamlit ウェブフレームワーク、LangChain、および OpenAI で ChatGPT をパーソナライズするジェネレーティブ AI 検索拡張生成(RAG: Retrieval-Augmented Generation)アプリケーションです。 このアプリケーションは IRIS をベクトルストアとして使用します。
image

アプリケーションの機能

  • ドキュメント(PDF または TXT)を IRIS に取り込む
  • 選択されたドキュメントの取り込みを使ってチャットする
  • ドキュメントの取り込みを削除する
  • OpenAI ChatGPT

ドキュメント(PDF または TXT)を IRIS に取り込む

以下の手順に従って、ドキュメントを取り込みます。

  • OpenAI キーを入力します。
  • ドキュメント(PDF または TXT)を選択します。
  • ドキュメントの説明を入力します。
  • 「Ingest Document」ボタンをクリックします。

image
 

ドキュメントの取り込み機能は、ドキュメントの詳細を rag_documents テーブルに挿入し、ベクトルデータを保存する 'rag_document + id'(rag_documents の ID)テーブルを作成します。

image

以下の Python コードは選択されたドキュメントをベクトルに保存します。

from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import PyPDFLoader, TextLoader
from langchain_iris import IRISVector
from langchain_openai import OpenAIEmbeddings
from sqlalchemy import create_engine,text

classRagOpr:#Ingest document. Parametres contains file path, description and file type defingestDoc(self,filePath,fileDesc,fileType): embeddings = OpenAIEmbeddings() #Load the document based on the file typeif fileType == "text/plain": loader = TextLoader(filePath)
elif fileType == "application/pdf": loader = PyPDFLoader(filePath)

    <span class="hljs-comment">#load data into documents</span>
    documents = loader.load()        
    
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=<span class="hljs-number">400</span>, chunk_overlap=<span class="hljs-number">0</span>)
    <span class="hljs-comment">#Split text into chunks</span>
    texts = text_splitter.split_documents(documents)
    
    <span class="hljs-comment">#Get collection Name from rag_doucments table. </span>
    COLLECTION_NAME = self.get_collection_name(fileDesc,fileType)
           
    <span class="hljs-comment"># function to create collection_name table and store vector data in it.</span>
    db = IRISVector.from_documents(
        embedding=embeddings,
        documents=texts,
        collection_name = COLLECTION_NAME,
        connection_string=self.CONNECTION_STRING,
    )

<span class="hljs-comment">#Get collection name</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_collection_name</span><span class="hljs-params">(self,fileDesc,fileType)</span>:</span>
    <span class="hljs-comment"># check if rag_documents table exists, if not then create it </span>
    <span class="hljs-keyword">with</span> self.engine.connect() <span class="hljs-keyword">as</span> conn:
        <span class="hljs-keyword">with</span> conn.begin():     
            sql = text(<span class="hljs-string">"""
                SELECT *
                FROM INFORMATION_SCHEMA.TABLES
                WHERE TABLE_SCHEMA = 'SQLUser'
                AND TABLE_NAME = 'rag_documents';
                """</span>)
            result = []
            <span class="hljs-keyword">try</span>:
                result = conn.execute(sql).fetchall()
            <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> err:
                print(<span class="hljs-string">"An exception occurred:"</span>, err)               
                <span class="hljs-keyword">return</span> <span class="hljs-string">''</span>
            <span class="hljs-comment">#if table is not created, then create rag_documents table first</span>
            <span class="hljs-keyword">if</span> len(result) == <span class="hljs-number">0</span>:
                sql = text(<span class="hljs-string">"""
                    CREATE TABLE rag_documents (
                    description VARCHAR(255),
                    docType VARCHAR(50) )
                    """</span>)
                <span class="hljs-keyword">try</span>:    
                    result = conn.execute(sql) 
                <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> err:
                    print(<span class="hljs-string">"An exception occurred:"</span>, err)                
                    <span class="hljs-keyword">return</span> <span class="hljs-string">''</span>
    <span class="hljs-comment">#Insert description value </span>
    <span class="hljs-keyword">with</span> self.engine.connect() <span class="hljs-keyword">as</span> conn:
        <span class="hljs-keyword">with</span> conn.begin():     
            sql = text(<span class="hljs-string">"""
                INSERT INTO rag_documents 
                (description,docType) 
                VALUES (:desc,:ftype)
                """</span>)
            <span class="hljs-keyword">try</span>:    
                result = conn.execute(sql, {<span class="hljs-string">'desc'</span>:fileDesc,<span class="hljs-string">'ftype'</span>:fileType})
            <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> err:
                print(<span class="hljs-string">"An exception occurred:"</span>, err)                
                <span class="hljs-keyword">return</span> <span class="hljs-string">''</span>
            <span class="hljs-comment">#select ID of last inserted record</span>
            sql = text(<span class="hljs-string">"""
                SELECT LAST_IDENTITY()
            """</span>)
            <span class="hljs-keyword">try</span>:
                result = conn.execute(sql).fetchall()
            <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> err:
                print(<span class="hljs-string">"An exception occurred:"</span>, err)
                <span class="hljs-keyword">return</span> <span class="hljs-string">''</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"rag_document"</span>+str(result[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>])</code></pre>

 

管理ポータルで以下の SQL コマンドを入力し、ベクトルデータを取得します。

SELECT top 5id, embedding, document, metadata
FROM SQLUser.rag_document2

image

 

選択されたドキュメントの取り込みを使ってチャットする

チャットオプションの選択セクションから「Document」を選択して質問を入力します。アプリケーションはベクトルデータを読み取り、関連する回答を返します。
image
以下の Python コードは、選択されたドキュメントをべく鳥に保存します。

from langchain_iris import IRISVector
from langchain_openai import OpenAIEmbeddings,ChatOpenAI
from langchain.chains import ConversationChain
from langchain.chains.conversation.memory import ConversationSummaryMemory
from langchain.chat_models import ChatOpenAI

classRagOpr:defragSearch(self,prompt,id):#Concat document id with rag_doucment to get the collection name COLLECTION_NAME = "rag_document"+str(id) embeddings = OpenAIEmbeddings() #Get vector store reference db2 = IRISVector ( embedding_function=embeddings,
collection_name=COLLECTION_NAME, connection_string=self.CONNECTION_STRING, ) #Similarity search docs_with_score = db2.similarity_search_with_score(prompt) #Prepair the retrieved documents to pass to LLM relevant_docs = ["".join(str(doc.page_content)) + " "for doc, _ in docs_with_score] #init LLM llm = ChatOpenAI( temperature=0,
model_name="gpt-3.5-turbo" ) #manage and handle LangChain multi-turn conversations conversation_sum = ConversationChain( llm=llm, memory= ConversationSummaryMemory(llm=llm), verbose=False ) #Create prompt template = f""" Prompt: {prompt} Relevant Docuemnts: {relevant_docs} """#Return the answer resp = conversation_sum(template) return resp['response']

</code></pre>


詳細については、iris-RAG-Gen の Open Exchange アプリケーションページをご覧ください。

よろしくお願いします。

0
0 66