Node.jsからOracle Databaseにドキュメントをロードしてみた(SODA)

## はじめに
本記事では、Node.jsからSimple Oracle Document Accessを使ってOracle Databaseへアクセスをする手順を紹介します。


## SODA(Simple Oracle Document Access)とは
Simple Oracle Document Access (SODA)は、NoSQL形式のAPIセットです。

SODAを使用すると、Oracle Databaseのドキュメント(特にJSON)のコレクションを作成および格納でき、SQLや、ドキュメントがどのようにデータベースに格納されているかを理解していなくても、そのコレクションの取得や問合せを行うことができます。

様々な言語とRepresentational State Transfer (REST)アーキテクチャ・スタイルで使用するための別々のSODA実装があります。

今回は、Oracle Databaseをドキュメントストアとして使用し、Node.jsで作成したサンプルプログラムからドキュメントをロードするまでの手順をまとめます。

## node-oracledbの特徴
 - 以下のように、npmから簡単にインストール可能です。

```
npm install oracledb
```
 - Node.js 10以降、および複数のOracleDatabaseバージョンをサポートします。 
 - SQL・PL / SQL文の実行、およびSODAドキュメントスタイルのアクセスAPIへのアクセスが可能です。
 - ラージオブジェクト（CLOBおよびBLOB）およびSQLオブジェクトのバインディングを含む広範なOracleデータ型をサポートします。
 - Connection Poolingを含む接続管理が可能です。
 - OracleDatabaseの高可用性機能が使用できます。
 - 暗号化されたネットワークトラフィックとセキュリティ機能を含む、Oracle NetworkServiceインフラストラクチャをフルに活用します。

## Node-oracledbのアーキテクチャ
![スクリーンショット 2021-02-13 15.41.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/9ff71873-8cd3-eefe-0911-627f0fd45022.png)

Node-oracledbは、Node.jsアプリケーションがOracleデータベースにアクセスできるようにするNode.jsのアドオンです。
 Node.jsプログラムは、node-oracledb関数を呼び出します。
内部的にnode-oracledb関数により、Oracleクライアントライブラリが動的にロードされます。
これにより、node-oracledbからOracle Databaseへの接続が確立されるため、SQL、PL / SQL、およびSODAを使用できます。

## 前提

 - 検証環境は下記の通りです


```
$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.5
BuildVersion:	18F132

$ node -v
v14.15.5
```

## 手順

ここからは、Oracle Databaseをドキュメントストアとして使用し、Node.jsで作成したサンプルプログラムからドキュメントをロードするまでの手順をまとめます。

### 前準備
#### Oracle Databaseのインストール
今回は、Oracle Database container imagesを使用します。
インストール方法は[こちら](https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance)をご覧ください。

バージョンは下記の通りです。

```sql

SQL> select banner from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production

```

#### Oracle Instant Clientのインストール

Node-oracledbはnpmからインストールが可能ですが、Oracle Clientは個別にインストールする必要があります。
今回は、Oracle Instant Clientを使用します。[こちら](https://www.oracle.com/database/technologies/instant-client/macos-intel-x86-downloads.html)から、以下2つの最新版パッケージをインストールします。

 - Basic Package (ZIP)
 - SQL*Plus Package (ZIP)

※Basic PackageとSQL*Plus Packageは、同一ディレクトリに展開する必要があります。

適切にインストールすると、下記のディレクトリ構成になります。

```
$ ls -lha
total 525576
drwxr-xr-x@ 34 root   admin   1.1K  2 13 13:57 .
drwxr-xr-x   4 kohei  staff   128B  2 13 14:19 ..
-r-xr-xr-x@  1 root   admin   5.6K  2 23  2019 BASIC_LICENSE
-rw-r--r--@  1 root   admin   1.6K  9 20 22:12 BASIC_README
-r-xr-xr-x@  1 root   admin   5.6K  2 23  2019 SQLPLUS_LICENSE
-rw-r--r--@  1 root   admin   1.6K  9 20 22:13 SQLPLUS_README
-rwxr-xr-x@  1 root   admin   9.7K  9 20 22:12 adrci
-rwxr-xr-x@  1 root   admin    39K  9 20 22:12 genezi
-r-xr-xr-x@  1 root   admin   342B  4 25  2015 glogin.sql
lrwxrwxrwx   1 root   admin    20B  2 13 13:55 libclntsh.dylib -> libclntsh.dylib.19.1
lrwxrwxrwx   1 root   admin    20B  2 13 13:55 libclntsh.dylib.10.1 -> libclntsh.dylib.19.1
lrwxrwxrwx   1 root   admin    20B  2 13 13:55 libclntsh.dylib.11.1 -> libclntsh.dylib.19.1
lrwxrwxrwx   1 root   admin    20B  2 13 13:55 libclntsh.dylib.12.1 -> libclntsh.dylib.19.1
lrwxrwxrwx   1 root   admin    20B  2 13 13:55 libclntsh.dylib.18.1 -> libclntsh.dylib.19.1
-rwxr-xr-x@  1 root   admin    95M  9  9 16:28 libclntsh.dylib.19.1
-rwxr-xr-x@  1 root   admin   4.6M  9  9 16:28 libclntshcore.dylib.19.1
-rwxr-xr-x@  1 root   admin   8.0M  8 31 21:56 libnnz19.dylib
lrwxrwxrwx   1 root   admin    18B  2 13 13:55 libocci.dylib -> libocci.dylib.19.1
lrwxrwxrwx   1 root   admin    18B  2 13 13:55 libocci.dylib.10.1 -> libocci.dylib.19.1
lrwxrwxrwx   1 root   admin    18B  2 13 13:55 libocci.dylib.11.1 -> libocci.dylib.19.1
lrwxrwxrwx   1 root   admin    18B  2 13 13:55 libocci.dylib.12.1 -> libocci.dylib.19.1
lrwxrwxrwx   1 root   admin    18B  2 13 13:55 libocci.dylib.18.1 -> libocci.dylib.19.1
-rwxr-xr-x@  1 root   admin   1.5M  9  9 19:10 libocci.dylib.19.1
-rwxr-xr-x@  1 root   admin   124M  9 20 22:12 libociei.dylib
-r-xr-xr-x@  1 root   admin   148K  9  9 19:06 libocijdbc19.dylib
-rwxr-xr-x@  1 root   admin   102K  9  9 18:37 liboramysql19.dylib
-r-xr-xr-x@  1 root   admin   1.3M  9  2 01:56 libsqlplus.dylib
-rwxrwxrwx@  1 root   admin   1.6M  9 20 22:07 libsqlplusic.dylib
-rw-r--r--@  1 root   admin   4.2M  5 20  2020 ojdbc8.jar
-rwxr-xr-x@  1 root   admin   8.3K  9 20 22:13 sqlplus
-rw-r--r--@  1 root   admin   1.6M  5 20  2020 ucp.jar
-rwxr-xr-x@  1 root   admin   147K  9 20 22:12 uidrvci
-rw-r--r--@  1 root   admin    73K  7  3  2020 xstreams.jar
```

#### tnsnames.oraの作成

動的にロードされたOracleクライアントライブラリに対して、接続先のOracle Databaseの情報を示します。

```
$ pwd
/Applications/ora/client/instantclient_19_8

$ mkdir -p network/admin
$ cd network/admin
$ vim tnsnames.ora
```

```:tnsnames.ora
ORCLCDB=localhost:1521/ORCLCDB
ORCL=
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = ORCL)
    )
  )
```

#### 環境変数の設定
Oracle Instant Clientを使用してOracle Databaseへ接続するための環境変数を設定します。

```
$ vim ~/.bash_profile
・・・
# Oracle Instant Client
export LD_LIBRARY_PATH=/Applications/ora/client/instantclient_19_8:$LD_LIBRARY_PATH
export PATH=$LD_LIBRARY_PATH:$PATH
export TNS_ADMIN=/Applications/ora/client/network/admin
```


#### テストユーザ作成、権限付与

実行中のdockerコンテナにログインし、テスト用ユーザの作成、必要な権限付与を行います。

```
$ docker ps
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS                 PORTS                                            NAMES
e85249e82ae4        oracle/database:19.3.0-ee   "/bin/sh -c 'exec $O…"   3 weeks ago         Up 6 hours (healthy)   0.0.0.0:1521->1521/tcp, 0.0.0.0:5500->5500/tcp   oracle

$ docker exec -it oracle /bin/bash
[oracle@e85249e82ae4 ~]$ 
[oracle@e85249e82ae4 ~]$ sqlplus /nolog

SQL*Plus: Release 19.0.0.0.0 - Production on Sat Feb 13 08:10:26 2021
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.

SQL> conn sys/WElcome##01@ORCL as sysdba
Connected.
```

```sql

SQL> create user test identified by test
  2  default tablespace users
  3  temporary tablespace temp
  4  quota unlimited on users;

User created.

SQL> grant connect, resource to test;

Grant succeeded.
```

#### SQL*Plusから接続確認
dockerコンテナ内で、作成したテスト用ユーザでOracle Databaseへ接続が可能であることを確認します。接続が可能であることを確認したら、dockerコンテナからログアウトします。

```sql

SQL> conn test/test@orcl;
Connected.

SQL> exit
[oracle@e85249e82ae4 ~]$ exit
exit
```

### node-oracledbのインストール
Oracle Instant Clientをインストールした環境に、oracledbパッケージをインストールします。
今回は、node-oracledb version 5.1を使用します。

```
$ npm install oracledb

$ npm list --depth=0
/Users/kohei
├── oracledb@5.1.0
```


### プログラム作成
作成したテスト用ユーザでOracle Databaseに接続し、データをInsertするサンプルプログラムを作成します。

```js:mysoda.js
const oracledb = require('oracledb');

const mypw = 'test';

oracledb.autoCommit = true;

async function run() {
    let connection;

    try {
        connection = await oracledb.getConnection( {
            user: "test",
            password : mypw,
            connectString: "localhost:1521/orcl"
        });

        // Create a new document collection
        const soda = connection.getSodaDatabase();
        const collectionName = 'nodb_soda_collection';  // Table Name
        const myCollection = await soda.createCollection(collectionName);

        // Insert a new document
        const myContent = {name: "Kohei", address: {city: "Setagaya", Country: "Japan"}, pet: "Mocha"};
        await myCollection.insertOne(myContent);

        // Print
        const filterSpec = {"pet": "Mocha"};
        const myDocuments = await myCollection.find().filter(filterSpec).getDocuments();
        myDocuments.forEach(function(element) {
            const content = element.getContent();
            console.log(content.name, ' has a pet "Mocha".');
        });
    } catch (err) {
        console.log ('Error in processing:\n', err);
    } finally {
        if (connection) {
            try {
                await connection.close();
            } catch (err) {
                console.log('Error in closing connection:\n', err);
            }
        }
    }
}

run();
```

### 実行
実行すると、以下の内容が標準出力されます。
サンプルプログラムがうまく実行されたことがわかります。

```
$ node mysoda.js 
Kohei has a pet "Mocha".
```

### テーブル確認
内部的に作成されたテーブルを確認します。
JSON_DOCUMENTがBLOBとして列定義されており、１件のデータが挿入されていることがわかります。

```sql

SQL> desc nodb_soda_collection;
 Name					                   Null?    Type
 ----------------------------------------- -------- ----------------------------
 ID			                    		   NOT NULL VARCHAR2(255)
 CREATED_ON			                 	   NOT NULL TIMESTAMP(6)
 LAST_MODIFIED			              	   NOT NULL TIMESTAMP(6)
 VERSION		                 		   NOT NULL VARCHAR2(255)
 JSON_DOCUMENT				        	    BLOB

SQL> select count(*) from nodb_soda_collection;

  COUNT(*)
----------
	     1
```

ドキュメントに格納されたデータを確認すると、サンプルプログラム内で指定したデータが格納されていることがわかります。

```sql
SQL> select t.json_document.name, t.json_document.address, t.json_document.pet from nodb_soda_collection t;

NAME	   ADDRESS				    PET
---------- ---------------------------------------- ----------
Kohei	   {"city":"Setagaya","Country":"Japan"}    Mocha
```

## おわりに

Node.jsからOracle Databaseにドキュメントをロードすることができました。

SODAは、他にも以下の言語をサポートしています。
Java、C（Oracle Call Interfaceを使用）、Node.js、Python、PL/SQL、SODA for REST

|SODA API	|入手方法 |
|:------------:|:------------:|
|SODA for Java	|Oracle Database 19c (19.6) JDBC Driver & UCP [SODA for Java 1.1.7](https://github.com/oracle/soda-for-java/releases)
|SODA for C	|Oracle Client 19.6 以降 [SODA for C](https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/c/19/adsdc/soda-c-prerequisites.html#GUID-944DC773-6A9B-4F9E-8404-EC0B9CF8975D)
|SODA for Node.js	|Oracle Client 19.6 以降 [node-oracledb](https://oracle.github.io/node-oracledb/INSTALL.html#quickstart)
|SODA for Python	|Oracle Client 19.6 以降 [cx_Oracle 8](https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html)
|SODA for PL/SQL	|AutonomousDB内に内包
|SODA for REST	|RESTful Servicesへのアクセス

是非とも皆さんも触ってみてください！

## 参考URL
 - [node-oracledb 5.1 Documentation for the Oracle Database Node.js Add-on](https://oracle.github.io/node-oracledb/doc/api.html)
 - [SODA Document](https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/index.html)
 - [JSON 開発者ガイド](https://docs.oracle.com/cd/F19136_01/adjsn/index.html)
 - [[AutonomousDB] Autonomous DatabaseにJSONドキュメントを格納してみた。（ORDS+SODA)](https://qiita.com/kenwatan/items/db9134e1c9bf49315a79)
 - [[Oracle Cloud] SODA Driver for Java を使って見た](https://qiita.com/sugimount/items/9cdf802160f2898d782f)