Oracle Graph クイックスタート（オンプレ）

次のような方のためのガイドとして、なるべく簡潔に書いてみました。参考になれば幸いです。この記事で使用している Graph Server and Client のバージョンは 23.1 です。

- Oracle Graph をすぐに試したい
- 新規または既存の Oracle Database（12.2 以上）を利用する
    - 無償の Express Edition も使用可能です
- オンプレ環境（クラウド上の VM も含む）を利用する

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410176/d0ea0635-0249-75c3-9fc7-d55eb5e6f4a4.png)


# まずは Graph Server を使わない場合

無償の SQL クライアントである [SQL Developer](https://www.oracle.com/jp/database/sqldeveloper/technologies/download/) は Graph Client を含んでいるため、データベースに必要な権限を持つユーザーが作成されていれば、すぐにグラフ機能を使い始めることができます。

## データベースユーザーの作成

まず、DBA 権限を持つユーザーで新しいユーザー（ここでは graphuser）を作成します。既存のユーザーを使用しても構いません。

```sql
CREATE USER graphuser
IDENTIFIED BY ********
DEFAULT TABLESPACE users
TEMPORARY TABLESPACE temp
QUOTA UNLIMITED ON users;
```

次の権限を付与します。

```sql
GRANT CREATE SESSION, CREATE TABLE, CREATE VIEW TO graphuser
```

## SQL Developer の利用

[SQL Developer](https://www.oracle.com/jp/database/sqldeveloper/technologies/download/) は Oracle が提供している無償のデータベース管理ツールです。使い方の詳細は省きます。ここでは、バージョン 21.2.1.204.1703 を使用しています。

### サンプルデータの作成

上記のユーザーで SQL Developer からデータベースに接続したら、サンプルの表データを作成します。

```sql
CREATE TABLE bank_account (
  acc_id NUMBER
);

CREATE TABLE bank_transaction (
  acc_id_src NUMBER, 
  acc_id_dst NUMBER, 
  txn_id NUMBER, 
  amount NUMBER
);

INSERT INTO bank_account VALUES (1);
INSERT INTO bank_account VALUES (2);
INSERT INTO bank_account VALUES (3);
INSERT INTO bank_account VALUES (4);
INSERT INTO bank_account VALUES (5);

INSERT INTO bank_transaction VALUES (1, 2, 101, 800);
INSERT INTO bank_transaction VALUES (2, 3, 102, 700);
INSERT INTO bank_transaction VALUES (3, 4, 103, 600);
INSERT INTO bank_transaction VALUES (4, 5, 104, 500);
INSERT INTO bank_transaction VALUES (5, 1, 105, 400);
```

### グラフの作成

表データからグラフを作成するためには PGQL を使う必要がある（ただし、Database 23c 以降では SQL でも可能になる予定）ため、Property Graph を右クリックして PGQL Worksheet を開きます。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410176/c7a831a0-aaf1-38b9-437a-9fa7e89814a1.png)

bank_graph というグラフを作成します。

```
CREATE PROPERTY GRAPH bank_graph
  VERTEX TABLES (
    bank_account
      KEY (acc_id)
      LABEL account
  )
  EDGE TABLES (
    bank_transaction
      KEY (txn_id)
      SOURCE KEY(acc_id_src) REFERENCES bank_account
      DESTINATION KEY(acc_id_dst) REFERENCES bank_account
      LABEL transferred_to
  )
  OPTIONS (PG_VIEW)
;
```

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410176/e98c1698-2899-1f45-86c9-96007133055e.png)

### PGQL クエリの実行

試しに PGQL の SELECT 文を実行してみます。

```
SELECT a2.acc_id, t.amount
FROM MATCH (a1) -[t:transferred_to]-> (a2) ON bank_graph
WHERE a1.acc_id = 1
```

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410176/c04c2a80-4fdf-31ec-133a-26e6370a195e.png)

# 次に Graph Server を追加する場合

上記の通り、Oracle Database は単体でも PGQL クエリをしようすることができます（厳密には、Graph Client を含む SQL クライアントを使うことにより SQL が PGQL に変換されている）が、別に Graph Server を用意してグラフをメモリに展開しておくことで、グラフを辿るクエリを高速化したり、グラフアルゴリズムや Graph Neural Networks を使用することができます。

## Graph Server のインストール

Graph Server のライセンスは Oracle Database に含まれており Oracle Linux または RedHat Linux の 7 と 8 でサポートされています。

- [2.2.1.1 Prerequisites for Installing Oracle Graph Server](https://docs.oracle.com/en/database/oracle/property-graph/23.1/spgdg/using-rpm-installation.html#GUID-D617B745-AC9F-431E-A861-B45374392162)

### RPM パッケージのインストール

Graph Server と JDK 11 をダウンロードしてインストールします。

- [Oracle Graph Server and Client Downloads](https://www.oracle.com/database/technologies/spatialandgraph/property-graph-features/graph-server-and-client/graph-server-and-client-downloads.html)
- [Java SE 11 Archive Downloads](https://www.oracle.com/jp/java/technologies/javase/jdk11-archive-downloads.html)

```
sudo yum install -y libgfortran
sudo rpm -i oracle-graph-23.1.0.x86_64.rpm
sudo rpm -ivh jdk-11.0.17_linux-x64_bin.rpm 
```

### JDBC URL の設定

Graph Server からデータベースに接続するため、設定ファイルに JDBC URL を書き込んで、Graph Server を再起動します。

```
vi /etc/oracle/graph/pgx.conf

...
"pgx_realm": {
  "implementation": "oracle.pg.identity.DatabaseRealm",
  "options": {
    "jdbc_url": "jdbc:oracle:thin:@myhost:1521/myservice",    <-- Here
    "token_expiration_seconds": 14400,
...

sudo systemctl restart pgx
```

### データベースロールの作成

Graph Server を使うために必要なロールを作成するため、DBA 権限を持つユーザーでデータベースに接続し、以下のスクリプトを実行します。

```sql
-- This procedure creates a list of roles needed for graph.
DECLARE
  PRAGMA AUTONOMOUS_TRANSACTION;
  role_exists EXCEPTION;
  PRAGMA EXCEPTION_INIT(role_exists, -01921);
  TYPE graph_roles_table IS TABLE OF VARCHAR2(50);
  graph_roles graph_roles_table;
BEGIN
  graph_roles := graph_roles_table(
    'GRAPH_DEVELOPER',
    'GRAPH_ADMINISTRATOR',
    'GRAPH_USER',
    'PGX_SESSION_CREATE',
    'PGX_SERVER_GET_INFO',
    'PGX_SERVER_MANAGE',
    'PGX_SESSION_READ_MODEL',
    'PGX_SESSION_MODIFY_MODEL',
    'PGX_SESSION_NEW_GRAPH',
    'PGX_SESSION_GET_PUBLISHED_GRAPH',
    'PGX_SESSION_COMPILE_ALGORITHM',
    'PGX_SESSION_ADD_PUBLISHED_GRAPH');
  FOR elem IN 1 .. graph_roles.count LOOP
    BEGIN
      dbms_output.put_line('create_graph_roles: ' || elem || ': CREATE ROLE ' || graph_roles(elem));
      EXECUTE IMMEDIATE 'CREATE ROLE ' || graph_roles(elem);
    EXCEPTION
      WHEN role_exists THEN
        dbms_output.put_line('create_graph_roles: role already exists. continue');
      WHEN OTHERS THEN
        RAISE;
    END;
  END LOOP;
EXCEPTION
  when others then
    dbms_output.put_line('create_graph_roles: hit error ');
    raise;
END;
/
-- This procedure add some grants to the graph roles.
DECLARE
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  EXECUTE IMMEDIATE 'GRANT PGX_SESSION_CREATE TO GRAPH_ADMINISTRATOR';
  EXECUTE IMMEDIATE 'GRANT PGX_SERVER_GET_INFO TO GRAPH_ADMINISTRATOR';
  EXECUTE IMMEDIATE 'GRANT PGX_SERVER_MANAGE TO GRAPH_ADMINISTRATOR';
  EXECUTE IMMEDIATE 'GRANT PGX_SESSION_CREATE TO GRAPH_DEVELOPER';
  EXECUTE IMMEDIATE 'GRANT PGX_SESSION_NEW_GRAPH TO GRAPH_DEVELOPER';
  EXECUTE IMMEDIATE 'GRANT PGX_SESSION_GET_PUBLISHED_GRAPH TO GRAPH_DEVELOPER';
  EXECUTE IMMEDIATE 'GRANT PGX_SESSION_MODIFY_MODEL TO GRAPH_DEVELOPER';
  EXECUTE IMMEDIATE 'GRANT PGX_SESSION_READ_MODEL TO GRAPH_DEVELOPER';
  EXECUTE IMMEDIATE 'GRANT PGX_SESSION_CREATE TO GRAPH_USER';
  EXECUTE IMMEDIATE 'GRANT PGX_SESSION_GET_PUBLISHED_GRAPH TO GRAPH_USER';
EXCEPTION
  when others then
    dbms_output.put_line('add_graph_roles_grants: hit error ');
    raise;
END;
/
```

次の権限を付与します。

```sql
GRAPH_DEVELOPER, PGX_SESSION_ADD_PUBLISHED_GRAPH TO graphuser
```

### TLS の無効化

初期状態では TLS が有効化されていますが、テスト使用の段階では証明書の設定などの手順を省きたいため、設定ファイル（server.conf）を編集して TLS を無効化しておきます。また、23.3 より前のバージョンでは Graph Visualization アプリケーションの WAR ファイルも修正が必要です。

```
vi /etc/oracle/graph/server.conf

  "enable_tls": false,
```

```sh
yum install -y zip unzip

# GraphViz
WAR=$(find /opt/oracle/graph/graphviz -name '*.war')
TMP=$(mktemp -d)
cd $TMP
unzip $WAR WEB-INF/web.xml
sed -i 's|<secure>true</secure>|<secure>false</secure>|' WEB-INF/web.xml
sed -i 's|https://|http://|' WEB-INF/web.xml
sudo zip $WAR WEB-INF/web.xml
rm -r $TMP

# restart
sudo systemctl restart pgx
```

## Java Client の利用

Graph Server の RPM インストールに含まれている Java Client（JShell に Graph Client ライブラリを読み込んだもの）を用いて Graph Server に接続します。この際、データベースのユーザー情報でログインします。

```
$ opg4j -b http://localhost:7007 --user graphuser
password: 
For an introduction type: /help intro
Oracle Graph Server Shell 23.1.0
Variables instance, session, and analyst ready to use.
opg4j>
```

### グラフのロード

先にデータベースに（ビューとして）作成したグラフを Graph Server にロードしてみます。

```
opg4j> var graph = session.readGraphByName("BANK_GRAPH", GraphSource.PG_VIEW)
opg4j> graph
graph ==> PgxGraph[name=BANK_GRAPH,N=5,E=5,created=1678876016842]
```

### グラフの公開

さらにこのグラフに他のセッション（次の手順で Graph Visualization アプリケーションを用いる）からアクセスするために、公開（publish）しておきます。

```
opg4j> graph.publish()
```

## Graph Visualization App の利用

ブラウザから `http://<ip_address>:7007/ui/` にアクセスして、データベースのユーザー情報でログインします。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410176/013e59e2-1445-cbb4-981c-c1257f8dd1cf.png)

### PGQL クエリの実行

PGQL の結果が可視化されることを確認します。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410176/16598d4c-a730-63f9-722e-1b6a3a9c723e.png)

## Python Client の利用

Graph Client をダウンロードして、Python が実行できるクライアント PC にインストールします。

- [Oracle Graph Server and Client Downloads](https://www.oracle.com/database/technologies/spatialandgraph/property-graph-features/graph-server-and-client/graph-server-and-client-downloads.html)

```
$ pip install oracle-graph-client==23.1.0
```

### Python Client から接続

次のスクリプトを使用して Graph Server に接続します。下のスクリーンショットでは Visual Studio Code で test.ipynb（Jupyter Notebook）を作成して、スクリプトを実行しています。

```python
from opg4py import graph_server
from pypgx import setloglevel
from getpass import getpass

setloglevel("ROOT", "WARN")

base_url = "http://localhost:7007"
username = "graphuser"
password = getpass("Password: ")

instance = graph_server.get_instance(base_url, username, password)
session = instance.create_session("jupyter")
analyst = session.create_analyst()

print(session)
```

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410176/3b853b97-e209-1eee-3604-21d02a56d757.png)

メモリ上で公開されているグラフ BANK_GRAPH にアクセスします。

```python
graph = session.get_graph("BANK_GRAPH")
graph
```

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410176/22ae4053-e574-0b23-7421-e25a506c1bbb.png)

### PGQL クエリの実行

全てのエッジおよびその始点と終点を取得します。

```python
graph.query_pgql("""
SELECT a1.acc_id AS a1_acc_id, a2.acc_id AS a2_acc_id, t.amount
FROM MATCH (a1)-[t]->(a2)
ORDER BY a1_acc_id
""").print()
```

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410176/41269b61-9959-38a3-7306-edd379959efa.png)

パスの検索を実行してみます。

```python
graph.query_pgql("""
SELECT
  ELEMENT_NUMBER(e) AS order
, v1.acc_id AS a1_acc_id
, e.amount
, v2.acc_id AS a2_acc_id
FROM MATCH SHORTEST
  ((a1) (-[t:transferred_to]->(a) WHERE t.amount > 300)+ (a2))
  ONE ROW PER STEP (v1, e, v2)
WHERE a1.acc_id = 2 AND a2.acc_id = 5
  AND COUNT(a) = COUNT(DISTINCT a)
ORDER BY order ASC
""").print()
```

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410176/8ff1379f-ee40-1c9d-67dc-450d5b2d49f0.png)

以上、クイックスタートでした！