MongoDB ShellからOracle Database 23c FreeにアクセスしてJSON Relational Duality Viewをコレクションとして操作してみた

# はじめに
こちらの記事でMongoDB ShellからOracle Database 23c Freeにアクセスするための手順を書きました。

https://qiita.com/500InternalServerError/items/9e234ad6eac72c860738

この記事では、その続きとして、Oracle Database 23cの新機能であるJSON Relational Duality Viewを作成し、作成したJSON Relational Duality ViewをMongoDB Shellからコレクションとして扱うための手順と、JSON Relational Duality Viewに対するドキュメント操作によってベースとなるリレーショナル表のデータがどのように変化するのかを検証しました。

なお、こちらの記事で使用してるサンプルは「[Oracle Database 23c JSON-Relational Duality Developer's Guide](https://docs.oracle.com/en/database/oracle/oracle-database/23/jsnvu/index.html)」の「Car-Racing Duality Views Example」を使用しています。 

# 1. JSON Relational Duality Viewのベースとなる表の作成

チーム情報を格納する表teamを作成します。
```
SQL> CREATE TABLE team
  2    (team_id INTEGER GENERATED BY DEFAULT ON NULL AS IDENTITY,
  3     name    VARCHAR2(255) NOT NULL UNIQUE,
  4     points  INTEGER NOT NULL,
  5     CONSTRAINT team_pk PRIMARY KEY(team_id));

Table created.

SQL>
```
ドライバー情報を格納する表driverを作成します。
```
SQL> CREATE TABLE driver
  2    (driver_id INTEGER GENERATED BY DEFAULT ON NULL AS IDENTITY,
  3     name      VARCHAR2(255) NOT NULL UNIQUE,
  4     points    INTEGER NOT NULL,
  5     team_id   INTEGER,
  6     CONSTRAINT driver_pk PRIMARY KEY(driver_id),
  7     CONSTRAINT driver_fk FOREIGN KEY(team_id) REFERENCES team(team_id));

Table created.

SQL>
```
レース情報を格納する表raceを作成します。
```
SQL> CREATE TABLE race
  2    (race_id   INTEGER GENERATED BY DEFAULT ON NULL AS IDENTITY,
  3     name      VARCHAR2(255) NOT NULL UNIQUE,
  4     laps      INTEGER NOT NULL,
  5     race_date DATE,
  6     podium    JSON,
  7     CONSTRAINT race_pk PRIMARY KEY(race_id));

Table created.

SQL>
```
ドライバーとレースのマッピング情報を格納する表driver_race_mapを作成します。
```
SQL> CREATE TABLE driver_race_map
  2    (driver_race_map_id INTEGER GENERATED BY DEFAULT ON NULL AS IDENTITY,
  3     race_id            INTEGER NOT NULL,
  4     driver_id          INTEGER NOT NULL,
  5     position           INTEGER,
  6     CONSTRAINT driver_race_map_pk  PRIMARY KEY(driver_race_map_id),
  7     CONSTRAINT driver_race_map_fk1 FOREIGN KEY(race_id) REFERENCES race(race_id),
  8     CONSTRAINT driver_race_map_fk2 FOREIGN KEY(driver_id) REFERENCES driver(driver_id));

Table created.

SQL>
```
driver_race_map表へのINSERT実行時にドライバーポイントとチームポイントを加算するトリガーdriver_race_map_triggerを作成します。
```
SQL> CREATE OR REPLACE TRIGGER driver_race_map_trigger
  2    BEFORE INSERT ON driver_race_map
  3    FOR EACH ROW
  4    DECLARE
  5      v_points  INTEGER;
  6      v_team_id INTEGER;
  7  BEGIN
  8    SELECT team_id INTO v_team_id FROM driver WHERE driver_id = :NEW.driver_id;
  9  
 10    IF :NEW.position = 1 THEN
 11      v_points := 25;
 12    ELSIF :NEW.position = 2 THEN
 13      v_points := 18;
 14    ELSIF :NEW.position = 3 THEN
 15      v_points := 15;
 16    ELSIF :NEW.position = 4 THEN
 17      v_points := 12;
 18    ELSIF :NEW.position = 5 THEN
 19      v_points := 10;
 20    ELSIF :NEW.position = 6 THEN
 21      v_points := 8;
 22    ELSIF :NEW.position = 7 THEN
 23      v_points := 6;
 24    ELSIF :NEW.position = 8 THEN
 25      v_points := 4;
 26    ELSIF :NEW.position = 9 THEN
 27      v_points := 2;
 28    ELSIF :NEW.position = 10 THEN
 29      v_points := 1;
 30    ELSE
 31      v_points := 0;
 32    END IF;
 33    UPDATE driver SET points = points + v_points
 34      WHERE driver_id = :NEW.driver_id;
 35    UPDATE team SET points = points + v_points
 36      WHERE team_id = v_team_id;
 37  END;
 38  /

Trigger created.

SQL>
```
# 2. JSON Relational Duality Viewの作成

レース情報をJSON形式で提供するビューrace_dvを作成します。
```
SQL> CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW race_dv AS
  2    SELECT JSON {'_id' IS r.race_id,
  3                 'name'   IS r.name,
  4                 'laps'   IS r.laps WITH NOUPDATE,
  5                 'date'   IS r.race_date,
  6                 'podium' IS r.podium WITH NOCHECK,
  7                 'result' IS
  8                   [ SELECT JSON {'driverRaceMapId' IS drm.driver_race_map_id,
  9                                  'position'        IS drm.position,
 10                                  UNNEST
 11                                    (SELECT JSON {'driverId' IS d.driver_id,
 12                                                  'name'     IS d.name}
 13                                       FROM driver d WITH NOINSERT UPDATE NODELETE
 14                                       WHERE d.driver_id = drm.driver_id)}
 15                       FROM driver_race_map drm WITH INSERT UPDATE DELETE
 16                       WHERE drm.race_id = r.race_id ]}
 17      FROM race r WITH INSERT UPDATE DELETE;

View created.

SQL>
```
ドライバー情報をJSON形式で提供するビューdriver_dvを作成します。
```
SQL> CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW driver_dv AS
  2    SELECT JSON {'_id' IS d.driver_id,
  3                 'name'     IS d.name,
  4                 'points'   IS d.points,
  5                 UNNEST
  6                   (SELECT JSON {'teamId' IS t.team_id,
  7                                 'team'   IS t.name WITH NOCHECK}
  8                      FROM team t WITH NOINSERT NOUPDATE NODELETE
  9                      WHERE t.team_id = d.team_id),
 10                 'race'     IS
 11                   [ SELECT JSON {'driverRaceMapId' IS drm.driver_race_map_id,
 12                                  UNNEST
 13                                    (SELECT JSON {'raceId' IS r.race_id,
 14                                                  'name'   IS r.name}
 15                                       FROM race r WITH NOINSERT NOUPDATE NODELETE
 16                                       WHERE r.race_id = drm.race_id),
 17                                  'finalPosition'   IS drm.position}
 18                  FROM driver_race_map drm WITH INSERT UPDATE NODELETE
 19                  WHERE drm.driver_id = d.driver_id ]}
 20      FROM driver d WITH INSERT UPDATE DELETE;

View created.

SQL>
```
チーム情報をJSON形式で提供するビューteam_dvを作成します。
```
SQL> CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW team_dv AS
  2    SELECT JSON {'_id'     IS t.team_id,
  3                 'name'    IS t.name,
  4                 'points'  IS t.points,
  5                 'driver'  IS
  6                   [ SELECT JSON {'driverId' IS d.driver_id,
  7                                  'name'     IS d.name,
  8                                  'points'   IS d.points WITH NOCHECK}
  9                       FROM driver d WITH INSERT UPDATE
 10                       WHERE d.team_id = t.team_id ]}
 11      FROM team t WITH INSERT UPDATE DELETE;

View created.

SQL>
```

# 3. JSON Relational Duality Viewをコレクションとして操作するための準備
JSON Relational Duality Viewを Oracle Database API for MongoDBで使用するために、JSON Relational Duality ViewをベースにしたSODAコレクションを作成します。
```
SQL> DECLARE
  2    col SODA_COLLECTION_T;
  3  BEGIN
  4    col := DBMS_SODA.CREATE_DUALV_COLLECTION('team_dv', 'TEAM_DV');
  5    col := DBMS_SODA.CREATE_DUALV_COLLECTION('driver_dv', 'DRIVER_DV');
  6    col := DBMS_SODA.CREATE_DUALV_COLLECTION('race_dv', 'RACE_DV');
  7  END;
  8  /

PL/SQL procedure successfully completed.

SQL>
```
以上で、MongoDB ShellでJSON Ralational Duality Viewをコレクションとして操作するための準備が整いました。

# 4. MongoDB ShellからOracle Database 23c Freeへの接続
MongoDB Shellからmongo_testユーザとしてOracle Database 23c Freeに接続します。
```
[oracle@oracle23c ~]$ mongosh --tlsAllowInvalidCertificates 'mongodb://mongo_test:MyPassword1!@localhost:27017/mongo_test?authMechanism=PLAIN&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true'
Current Mongosh Log ID:	64622ff7a1919d2a55f3ec18
Connecting to:		mongodb://<credentials>@localhost:27017/mongo_test?authMechanism=PLAIN&authSource=%24external&ssl=true&retryWrites=false&loadBalanced=true&serverSelectionTimeoutMS=2000&tlsAllowInvalidCertificates=true&appName=mongosh+1.8.2
Using MongoDB:		4.2.14
Using Mongosh:		1.8.2

For mongosh info see: https://docs.mongodb.com/mongodb-shell/

mongo_test>
```
コレクションの一覧を確認してみます。
```
mongo_test> show collections
driver_dv
race_dv
team_dv
mongo_test> 
```
先ほど作成してSODAコレクションを作成した3つのJSON Relational Duality View（driver_dv、team_dv、race_dv）がコレクションとして表示されました。

find()メソッドを使用して、各コレクションの内容を確認してみます。
```
mongo_test> db.driver_dv.find();

mongo_test>
mongo_test> db.race_dv.find();

mongo_test>
mongo_test> db.team_dv.find();

mongo_test>
```
この時点では、どのコレクションにもデータはまだ入っていません。

# 5. JSON-Relational Duality Viewをコレクションとして操作する

insertOne()メソッドを使用して、コレクションteam_dvに２名のドライバー情報を含むドキュメントを追加してみます。
```
mongo_test> db.team_dv.insertOne(
... {
...   "_id": 2,
...   "name": "Mercedes",
...   "points": 0,
...   "driver": [
...     {
...       "driverId": 105,
...       "name": "George Russell",
...       "points": 0
...     },
...     {
...       "driverId": 106,
...       "name": "Lewis Hamilton",
...       "points": 0
...     }
...   ]
... }
... );
{ acknowledged: true, insertedId: 2 }
mongo_test>
```
コレクションteam_dvの内容を確認してみます。
```
mongo_test> db.team_dv.find();
[
  {
    _id: 2,
    name: 'Mercedes',
    points: 0,
    driver: [
      { driverId: 105, name: 'George Russell', points: 0 },
      { driverId: 106, name: 'Lewis Hamilton', points: 0 }
    ],
    _metadata: {
      etag: Binary(Buffer.from("536001f31a8718819aeef28ec20d8677", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d0952f", "hex"), 0)
    }
  }
]
mongo_test>
```
先ほど追加したドキュメントにメタデータ（etag、asof）が付加されて格納されていることが確認できました。

コレクションdriver_dvの内容を確認してみます。
```
mongo_test> db.driver_dv.find();
[
  {
    _id: 105,
    name: 'George Russell',
    points: 0,
    teamId: 2,
    team: 'Mercedes',
    race: [],
    _metadata: {
      etag: Binary(Buffer.from("a8bb1825f6218ec0d300671173540597", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3aa95", "hex"), 0)
    }
  },
  {
    _id: 106,
    name: 'Lewis Hamilton',
    points: 0,
    teamId: 2,
    team: 'Mercedes',
    race: [],
    _metadata: {
      etag: Binary(Buffer.from("d3ff3213793e306204bb5e5060368e41", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3aa95", "hex"), 0)
    }
  }
]
mongo_test> 
```
コレクションteam_dvにドキュメントを追加することによって、ドキュメント内のドライバーの情報がコレクションdriver_dvに追加されることが確認できました。

SQL*Plusからmongo_testユーザとしてOracle Databse 23c Freeに接続し、Duality Viewのベースとなっているリレーショナル表の内容を確認してみます。

```
SQL> col name for a30
SQL> SELECT * FROM team;

   TEAM_ID NAME 			                  POINTS
---------- ------------------------------ ----------
	     2 Mercedes				                   0

SQL> SELECT * FROM driver;

 DRIVER_ID NAME 			                  POINTS	TEAM_ID
---------- ------------------------------ ---------- ----------
       105 George Russell			               0	      2
       106 Lewis Hamilton			               0	      2

SQL> SELECT * FROM race;

no rows selected

SQL> SELECT * FROM driver_race_map;

no rows selected

SQL>
```
コレクションteam_dvにドキュメントを1つ追加したことによって、team表に1レコード、driver表に2レコード追加されたことが確認できました。

次に、insertMany(）メソッドを使用して、コレクションteam_dvに複数のドキュメントを追加してみます。
```
mongo_test> db.team_dv.insertMany(
...   [
...     {
...       "_id": 301,
...       "name": "Red Bull",
...       "points": 0,
...       "driver": [
...         {
...           "driverId": 101,
...           "name": "Max Verstappen",
...           "points": 0
...         },
...         {
...           "driverId": 102,
...           "name": "Sergio Perez",
...           "points": 0
...         }
...       ]
...     },
...     {
...       "_id": 302,
...       "name": "Ferrari",
...       "points": 0,
...       "driver": [
...         {
...           "driverId": 103,
...           "name": "Charles Leclerc",
...           "points": 0
...         },
...         {
...           "driverId": 104,
...           "name": "Carlos Sainz Jr",
...           "points": 0
...         }
...       ]
...     }
...   ]
... );
{ acknowledged: true, insertedIds: { '0': 301, '1': 302 } }
mongo_test>
```
コレクションteam_dvの内容を確認してみます。
```
mongo_test> db.team_dv.find();
[
  {
    _id: 2,
    name: 'Mercedes',
    points: 0,
    driver: [
      { driverId: 105, name: 'George Russell', points: 0 },
      { driverId: 106, name: 'Lewis Hamilton', points: 0 }
    ],
    _metadata: {
      etag: Binary(Buffer.from("536001f31a8718819aeef28ec20d8677", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3aa74", "hex"), 0)
    }
  },
  {
    _id: 301,
    name: 'Red Bull',
    points: 0,
    driver: [
      { driverId: 101, name: 'Max Verstappen', points: 0 },
      { driverId: 102, name: 'Sergio Perez', points: 0 }
    ],
    _metadata: {
      etag: Binary(Buffer.from("9f0f855a39f4ae8fecd7e6a123855fd1", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3aa74", "hex"), 0)
    }
  },
  {
    _id: 302,
    name: 'Ferrari',
    points: 0,
    driver: [
      { driverId: 103, name: 'Charles Leclerc', points: 0 },
      { driverId: 104, name: 'Carlos Sainz Jr', points: 0 }
    ],
    _metadata: {
      etag: Binary(Buffer.from("7b62699fececff392143ba7a335e5e47", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3aa74", "hex"), 0)
    }
  }
]
mongo_test>
```
insertMany()メソッドを使用して追加したドキュメントにメタデータが付与されて格納されていることが確認できました。

コレクションdriver_dvの内容を確認してみます。
```
mongo_test> db.driver_dv.find();
[
  {
    _id: 105,
    name: 'George Russell',
    points: 0,
    teamId: 2,
    team: 'Mercedes',
    race: [],
    _metadata: {
      etag: Binary(Buffer.from("a8bb1825f6218ec0d300671173540597", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3aa95", "hex"), 0)
    }
  },
  {
    _id: 106,
    name: 'Lewis Hamilton',
    points: 0,
    teamId: 2,
    team: 'Mercedes',
    race: [],
    _metadata: {
      etag: Binary(Buffer.from("d3ff3213793e306204bb5e5060368e41", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3aa95", "hex"), 0)
    }
  },
  {
    _id: 101,
    name: 'Max Verstappen',
    points: 0,
    teamId: 301,
    team: 'Red Bull',
    race: [],
    _metadata: {
      etag: Binary(Buffer.from("f9d9815dff27879f61386cfd1622b065", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3aa95", "hex"), 0)
    }
  },
  {
    _id: 102,
    name: 'Sergio Perez',
    points: 0,
    teamId: 301,
    team: 'Red Bull',
    race: [],
    _metadata: {
      etag: Binary(Buffer.from("9865a29dee5f5754674a9b1d2ec58730", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3aa95", "hex"), 0)
    }
  },
  {
    _id: 103,
    name: 'Charles Leclerc',
    points: 0,
    teamId: 302,
    team: 'Ferrari',
    race: [],
    _metadata: {
      etag: Binary(Buffer.from("c47a91f57bf3cf45f0d8284240399a90", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3aa95", "hex"), 0)
    }
  },
  {
    _id: 104,
    name: 'Carlos Sainz Jr',
    points: 0,
    teamId: 302,
    team: 'Ferrari',
    race: [],
    _metadata: {
      etag: Binary(Buffer.from("363ec6bbf0fadd913b219482959da39d", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3aa95", "hex"), 0)
    }
  }
]
mongo_test> 
```
insertMany()メソッドを使用してコレクションteam_dvにドライバー情報を含むドキュメントを追加したことによって、ドキュメントに含まれるドライバーの情報がコレクションdriver_dvに追加されたことが確認できました。

Duality Viewのベースとなっているリレーショナル表の内容を確認してみます。
```
SQL> set linesize 200
SQL> col name for a30
SQL> SELECT * FROM team;

   TEAM_ID NAME 			                  POINTS
---------- ------------------------------ ----------
	     2 Mercedes				                   0
       301 Red Bull				                   0
       302 Ferrari				                   0

SQL> SELECT * FROM driver;

 DRIVER_ID NAME 			                  POINTS	TEAM_ID
---------- ------------------------------ ---------- ----------
       105 George Russell			               0	      2
       106 Lewis Hamilton			               0	      2
       101 Max Verstappen			               0	    301
       102 Sergio Perez 			               0	    301
       103 Charles Leclerc			               0	    302
       104 Carlos Sainz Jr			               0	    302

6 rows selected.

SQL> SELECT * FROM race;

no rows selected

SQL> SELECT * FROM driver_race_map;

no rows selected

SQL>
```
コレクションteam_dvにドキュメントを2つ追加したことによって、team表に2レコード、driver表に4レコード追加されたことがわかりました。

insertMany()メソッドを使用して、コレクションrace_dvに3つのドキュメントを追加します。
```
mongo_test> db.race_dv.insertMany(
...   [
...     {
...       "_id": 201,
...       "name": "Bahrain Grand Prix",
...       "laps": 57,
...       "date": "2022-03-20T00:00:00",
...       "podium": {}
...     },
...     {
...       "_id": 202,
...       "name": "Saudi Arabian Grand Prix",
...       "laps": 50,
...       "date": "2022-03-27T00:00:00",
...       "podium": {}
...     },
...     {
...       "_id": 203,
...       "name": "Australian Grand Prix",
...       "laps": 58,
...       "date": "2022-04-09T00:00:00",
...       "podium": {}
...     }
...   ]
... );
{ acknowledged: true, insertedIds: { '0': 201, '1': 202, '2': 203 } }
mongo_test>
```
コレクションrace_dvの内容を確認します。
```
mongo_test> db.race_dv.find();
[
  {
    _id: 201,
    name: 'Bahrain Grand Prix',
    laps: 57,
    date: ISODate("2022-03-20T00:00:00.000Z"),
    podium: {},
    result: [],
    _metadata: {
      etag: Binary(Buffer.from("2e8dc09543dd25dc7d588fb9734d962b", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3aee7", "hex"), 0)
    }
  },
  {
    _id: 202,
    name: 'Saudi Arabian Grand Prix',
    laps: 50,
    date: ISODate("2022-03-27T00:00:00.000Z"),
    podium: {},
    result: [],
    _metadata: {
      etag: Binary(Buffer.from("7e056a845212bfde19e0c0d0cd549ea0", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3aee7", "hex"), 0)
    }
  },
  {
    _id: 203,
    name: 'Australian Grand Prix',
    laps: 58,
    date: ISODate("2022-04-09T00:00:00.000Z"),
    podium: {},
    result: [],
    _metadata: {
      etag: Binary(Buffer.from("ea6e1194c012970ca07116ee1ef167e8", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3aee7", "hex"), 0)
    }
  }
]
mongo_test>
```
insertMany()メソッドを使用して追加したドキュメントにメタデータが付与されて格納されていることが確認できました。

Duality Viewのベースとなっているリレーショナル表の内容を確認してみます。
```
SQL> col name for a30
SQL> SELECT * FROM team;

   TEAM_ID NAME 			                  POINTS
---------- ------------------------------ ----------
	     2 Mercedes				                   0
       301 Red Bull				                   0
       302 Ferrari				                   0

SQL> SELECT * FROM driver;

 DRIVER_ID NAME 			                  POINTS	TEAM_ID
---------- ------------------------------ ---------- ----------
       105 George Russell			               0	      2
       106 Lewis Hamilton			               0	      2
       101 Max Verstappen			               0	    301
       102 Sergio Perez 			               0	    301
       103 Charles Leclerc			               0	    302
       104 Carlos Sainz Jr			               0	    302

6 rows selected.

SQL> SELECT * FROM race;

   RACE_ID NAME 				                LAPS RACE_DATE PODIUM
---------- ------------------------------ ---------- --------- --------------------------------------------------------------------------------
       201 Bahrain Grand Prix			          57 20-MAR-22 {}
       202 Saudi Arabian Grand Prix		          50 27-MAR-22 {}
       203 Australian Grand Prix		          58 09-APR-22 {}

SQL> SELECT * FROM driver_race_map;

no rows selected

SQL>
```
コレクションrace_dvにドキュメントを3つ追加したことによって、race表に3レコード追加されたことがわかりました。

replaceOneメソッドを使用して、コレクションrace_dv内の_idの値が201のドキュメントを、result（レース結果）を含む内容に変更（置換）してみます。
```
mongo_test> db.race_dv.replaceOne(
...   {_id:201},
...   {
...     "name": "Bahrain Grand Prix",
...     "laps": 57,
...     "date": "2022-03-20T00:00:00",
...     "podium": {
...       "winner": {
...         "name": "Charles Leclerc",
...         "time": "01:37:33.584"
...       },
...       "firstRunnerUp": {
...         "name": "Carlos Sainz Jr",
...         "time": "01:37:39.182"
...       },
...       "secondRunnerUp": {
...         "name": "Lewis Hamilton",
...         "time": "01:37:43.259"
...       }
...     },
...     "result": [
...       {
...         "driverRaceMapId": 3,
...         "position": 1,
...         "driverId": 103,
...         "name": "Charles Leclerc"
...       },
...       {
...         "driverRaceMapId": 4,
...         "position": 2,
...         "driverId": 104,
...         "name": "Carlos Sainz Jr"
...       },
...       {
...         "driverRaceMapId": 9,
...         "position": 3,
...         "driverId": 106,
...         "name": "Lewis Hamilton"
...       },
...       {
...         "driverRaceMapId": 10,
...         "position": 4,
...         "driverId": 105,
...         "name": "George Russell"
...       }
...     ]
...   }
... );
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
mongo_test> 
```
コレクションrace_dvの内容を確認します。
```
mongo_test> db.race_dv.find();
[
  {
    _id: 201,
    name: 'Bahrain Grand Prix',
    laps: 57,
    date: ISODate("2022-03-20T00:00:00.000Z"),
    podium: {
      winner: { name: 'Charles Leclerc', time: '01:37:33.584' },
      firstRunnerUp: { name: 'Carlos Sainz Jr', time: '01:37:39.182' },
      secondRunnerUp: { name: 'Lewis Hamilton', time: '01:37:43.259' }
    },
    result: [
      {
        driverRaceMapId: 3,
        position: 1,
        driverId: 103,
        name: 'Charles Leclerc'
      },
      {
        driverRaceMapId: 4,
        position: 2,
        driverId: 104,
        name: 'Carlos Sainz Jr'
      },
      {
        driverRaceMapId: 9,
        position: 3,
        driverId: 106,
        name: 'Lewis Hamilton'
      },
      {
        driverRaceMapId: 10,
        position: 4,
        driverId: 105,
        name: 'George Russell'
      }
    ],
    _metadata: {
      etag: Binary(Buffer.from("20f7d9f0c69ac5f959dca819f9116848", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b1a1", "hex"), 0)
    }
  },
  {
    _id: 202,
    name: 'Saudi Arabian Grand Prix',
    laps: 50,
    date: ISODate("2022-03-27T00:00:00.000Z"),
    podium: {},
    result: [],
    _metadata: {
      etag: Binary(Buffer.from("7e056a845212bfde19e0c0d0cd549ea0", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b1a1", "hex"), 0)
    }
  },
  {
    _id: 203,
    name: 'Australian Grand Prix',
    laps: 58,
    date: ISODate("2022-04-09T00:00:00.000Z"),
    podium: {},
    result: [],
    _metadata: {
      etag: Binary(Buffer.from("ea6e1194c012970ca07116ee1ef167e8", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b1a1", "hex"), 0)
    }
  }
]
mongo_test>
```
コレクションrace_dv内の_idの値が201のドキュメントが、レース結果を含む内容に変更（置換）されました。

コレクションteam_dvの内容を確認してみます。
```
mongo_test> db.team_dv.find();
[
  {
    _id: 2,
    name: 'Mercedes',
    points: 27,
    driver: [
      { driverId: 105, name: 'George Russell', points: 12 },
      { driverId: 106, name: 'Lewis Hamilton', points: 15 }
    ],
    _metadata: {
      etag: Binary(Buffer.from("855840b905c8cafa99fb9cbf813992e5", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b22e", "hex"), 0)
    }
  },
  {
    _id: 301,
    name: 'Red Bull',
    points: 0,
    driver: [
      { driverId: 101, name: 'Max Verstappen', points: 0 },
      { driverId: 102, name: 'Sergio Perez', points: 0 }
    ],
    _metadata: {
      etag: Binary(Buffer.from("9f0f855a39f4ae8fecd7e6a123855fd1", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b22e", "hex"), 0)
    }
  },
  {
    _id: 302,
    name: 'Ferrari',
    points: 43,
    driver: [
      { driverId: 103, name: 'Charles Leclerc', points: 25 },
      { driverId: 104, name: 'Carlos Sainz Jr', points: 18 }
    ],
    _metadata: {
      etag: Binary(Buffer.from("c5dd30f04da1a6a390bfab12b7d4f700", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b22e", "hex"), 0)
    }
  }
]
mongo_test>
```
コレクションrace_dv内のドキュメントをレース結果を含む内容に変更したことによって、コレクションteam_dv内の各ドキュメントのpointsおよび各ドライバーのpoints（driver.points）が更新されていることが確認できました。

コレクションdriver_dvの内容を確認してみます。
```
mongo_test> db.driver_dv.find();
[
  {
    _id: 105,
    name: 'George Russell',
    points: 12,
    teamId: 2,
    team: 'Mercedes',
    race: [
      {
        driverRaceMapId: 10,
        raceId: 201,
        name: 'Bahrain Grand Prix',
        finalPosition: 4
      }
    ],
    _metadata: {
      etag: Binary(Buffer.from("2e67a3a752d750692757480b648521b7", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b257", "hex"), 0)
    }
  },
  {
    _id: 106,
    name: 'Lewis Hamilton',
    points: 15,
    teamId: 2,
    team: 'Mercedes',
    race: [
      {
        driverRaceMapId: 9,
        raceId: 201,
        name: 'Bahrain Grand Prix',
        finalPosition: 3
      }
    ],
    _metadata: {
      etag: Binary(Buffer.from("466f2676916d9db3b0b35c87192da2af", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b257", "hex"), 0)
    }
  },
  {
    _id: 101,
    name: 'Max Verstappen',
    points: 0,
    teamId: 301,
    team: 'Red Bull',
    race: [],
    _metadata: {
      etag: Binary(Buffer.from("f9d9815dff27879f61386cfd1622b065", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b257", "hex"), 0)
    }
  },
  {
    _id: 102,
    name: 'Sergio Perez',
    points: 0,
    teamId: 301,
    team: 'Red Bull',
    race: [],
    _metadata: {
      etag: Binary(Buffer.from("9865a29dee5f5754674a9b1d2ec58730", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b257", "hex"), 0)
    }
  },
  {
    _id: 103,
    name: 'Charles Leclerc',
    points: 25,
    teamId: 302,
    team: 'Ferrari',
    race: [
      {
        driverRaceMapId: 3,
        raceId: 201,
        name: 'Bahrain Grand Prix',
        finalPosition: 1
      }
    ],
    _metadata: {
      etag: Binary(Buffer.from("5ec1d86761320f1d79e3c8e0995cf672", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b257", "hex"), 0)
    }
  },
  {
    _id: 104,
    name: 'Carlos Sainz Jr',
    points: 18,
    teamId: 302,
    team: 'Ferrari',
    race: [
      {
        driverRaceMapId: 4,
        raceId: 201,
        name: 'Bahrain Grand Prix',
        finalPosition: 2
      }
    ],
    _metadata: {
      etag: Binary(Buffer.from("1a7a20f98f841760fecdcd43265a3cef", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b257", "hex"), 0)
    }
  }
]
mongo_test> 
```
コレクションrace_dv内のドキュメントをレース結果を含む内容に変更したことによって、コレクションdriver_dv内の各ドキュメントのpointsおよびraceの値が更新されていることが確認できました。

Duality Viewのベースとなっているリレーショナル表の内容を確認してみます。
```
SQL> SELECT * FROM race;

   RACE_ID NAME 				                LAPS RACE_DATE PODIUM
---------- ------------------------------ ---------- --------- --------------------------------------------------------------------------------
       201 Bahrain Grand Prix			          57 20-MAR-22 {"winner":{"name":"Charles Leclerc","time":"01:37:33.584"},"firstRunnerUp":{"name":"Carlos Sainz Jr","time":"01:37:39.182"},"secondRunnerUp":{"name":"Lewis Hamilton","time":"01:37:43.259"}}
       202 Saudi Arabian Grand Prix		          50 27-MAR-22 {}
       203 Australian Grand Prix		          58 09-APR-22 {}

SQL> SELECT * FROM driver_race_map;

DRIVER_RACE_MAP_ID    RACE_ID  DRIVER_ID   POSITION
------------------ ---------- ---------- ----------
		         3	      201	     103	      1
		         4	      201	     104	      2
		         9	      201	     106	      3
		        10	      201	     105	      4

SQL> SELECT * FROM team;

   TEAM_ID NAME 			                  POINTS
---------- ------------------------------ ----------
	     2 Mercedes				                  27
       301 Red Bull				                   0
       302 Ferrari				                  43

SQL> SELECT * FROM driver;

 DRIVER_ID NAME 			                  POINTS	TEAM_ID
---------- ------------------------------ ---------- ----------
       105 George Russell			              12	      2
       106 Lewis Hamilton			              15	      2
       101 Max Verstappen			               0	    301
       102 Sergio Perez 			               0	    301
       103 Charles Leclerc			              25	    302
       104 Carlos Sainz Jr			              18	    302

6 rows selected.

SQL> 
```
コレクションrace_dv内のドキュメントをレース結果を含む内容に変更したことによって、以下の変更があったことがわかりました。
・race表のpodium列の値が更新された
・driver_race_map表に行が追加された
・team表のpoints列の値が更新された
・driver表のpoints列の値が更新された

team表およびdriver表のpoints列の値が更新されたのは、1.で作成したトリガーdriver_race_map_triggerによるものです。
トリガーdriver_race_map_triggerによってteam表およびdriver表のpoints列の値が更新され、コレクションteam_dvのpoints、およびコレクションdriver_dvのdriver.pointsの値に反映されています。

updateOne()メソッドを使用して、コレクションrace_dv内にあるドキュメントのうち、nameの値が"Bahrain Grand Prix"であるドキュメントのnameの値を"Blue Air Bahrain Grand Prix"に変更してみます。
```
mongo_test> db.race_dv.updateOne(
...   {name : "Bahrain Grand Prix"},
...   {$set : {name : "Blue Air Bahrain Grand Prix"}}
... );
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
mongo_test>
```
コレクションrace_dvの内容を確認してみます。
```
mongo_test> db.race_dv.find();
[
  {
    _id: 201,
    name: 'Blue Air Bahrain Grand Prix',
    laps: 57,
    date: ISODate("2022-03-20T00:00:00.000Z"),
    podium: {
      winner: { name: 'Charles Leclerc', time: '01:37:33.584' },
      firstRunnerUp: { name: 'Carlos Sainz Jr', time: '01:37:39.182' },
      secondRunnerUp: { name: 'Lewis Hamilton', time: '01:37:43.259' }
    },
    result: [
      {
        driverRaceMapId: 3,
        position: 1,
        driverId: 103,
        name: 'Charles Leclerc'
      },
      {
        driverRaceMapId: 4,
        position: 2,
        driverId: 104,
        name: 'Carlos Sainz Jr'
      },
      {
        driverRaceMapId: 9,
        position: 3,
        driverId: 106,
        name: 'Lewis Hamilton'
      },
      {
        driverRaceMapId: 10,
        position: 4,
        driverId: 105,
        name: 'George Russell'
      }
    ],
    _metadata: {
      etag: Binary(Buffer.from("f6906a8f7a131c127faef32ca43af97a", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b84d", "hex"), 0)
    }
  },
  {
    _id: 202,
    name: 'Saudi Arabian Grand Prix',
    laps: 50,
    date: ISODate("2022-03-27T00:00:00.000Z"),
    podium: {},
    result: [],
    _metadata: {
      etag: Binary(Buffer.from("7e056a845212bfde19e0c0d0cd549ea0", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b84d", "hex"), 0)
    }
  },
  {
    _id: 203,
    name: 'Australian Grand Prix',
    laps: 58,
    date: ISODate("2022-04-09T00:00:00.000Z"),
    podium: {},
    result: [],
    _metadata: {
      etag: Binary(Buffer.from("ea6e1194c012970ca07116ee1ef167e8", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b84d", "hex"), 0)
    }
  }
]
mongo_test> 
```
コレクションrace_dv内のドキュメントのうち、nameの値が"Bahrain Grand Prix"であるドキュメント（_idの値が201のドキュメント）のnameの値が"Blue Air Bahrain Grand Prix"に変更されました。

コレクションdriver_dvの内容を確認してみます
```
mongo_test> db.driver_dv.find();
[
  {
    _id: 105,
    name: 'George Russell',
    points: 12,
    teamId: 2,
    team: 'Mercedes',
    race: [
      {
        driverRaceMapId: 10,
        raceId: 201,
        name: 'Blue Air Bahrain Grand Prix',
        finalPosition: 4
      }
    ],
    _metadata: {
      etag: Binary(Buffer.from("13f8dc14f9205fce16b7e911c36b6332", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b857", "hex"), 0)
    }
  },
  {
    _id: 106,
    name: 'Lewis Hamilton',
    points: 15,
    teamId: 2,
    team: 'Mercedes',
    race: [
      {
        driverRaceMapId: 9,
        raceId: 201,
        name: 'Blue Air Bahrain Grand Prix',
        finalPosition: 3
      }
    ],
    _metadata: {
      etag: Binary(Buffer.from("6ea0aa1fa6d8996c44b375d0fb65e5f5", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b857", "hex"), 0)
    }
  },
  {
    _id: 101,
    name: 'Max Verstappen',
    points: 0,
    teamId: 301,
    team: 'Red Bull',
    race: [],
    _metadata: {
      etag: Binary(Buffer.from("f9d9815dff27879f61386cfd1622b065", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b857", "hex"), 0)
    }
  },
  {
    _id: 102,
    name: 'Sergio Perez',
    points: 0,
    teamId: 301,
    team: 'Red Bull',
    race: [],
    _metadata: {
      etag: Binary(Buffer.from("9865a29dee5f5754674a9b1d2ec58730", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b857", "hex"), 0)
    }
  },
  {
    _id: 103,
    name: 'Charles Leclerc',
    points: 25,
    teamId: 302,
    team: 'Ferrari',
    race: [
      {
        driverRaceMapId: 3,
        raceId: 201,
        name: 'Blue Air Bahrain Grand Prix',
        finalPosition: 1
      }
    ],
    _metadata: {
      etag: Binary(Buffer.from("d44402d6a4a1837186419926384e1d47", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b857", "hex"), 0)
    }
  },
  {
    _id: 104,
    name: 'Carlos Sainz Jr',
    points: 18,
    teamId: 302,
    team: 'Ferrari',
    race: [
      {
        driverRaceMapId: 4,
        raceId: 201,
        name: 'Blue Air Bahrain Grand Prix',
        finalPosition: 2
      }
    ],
    _metadata: {
      etag: Binary(Buffer.from("df16113d412db39a4c455dc9f1543dc9", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b857", "hex"), 0)
    }
  }
]
mongo_test> 
```
コレクションrace_dv内にあるドキュメントのnameの値を変更することによって、コレクションdriver_dv内にある各ドキュメントのrace.nameの値が更新されていることが確認できました。

コレクションteam_dvの内容を確認してみます。
```
mongo_test> db.team_dv.find();
[
  {
    _id: 2,
    name: 'Mercedes',
    points: 27,
    driver: [
      { driverId: 105, name: 'George Russell', points: 12 },
      { driverId: 106, name: 'Lewis Hamilton', points: 15 }
    ],
    _metadata: {
      etag: Binary(Buffer.from("855840b905c8cafa99fb9cbf813992e5", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b868", "hex"), 0)
    }
  },
  {
    _id: 301,
    name: 'Red Bull',
    points: 0,
    driver: [
      { driverId: 101, name: 'Max Verstappen', points: 0 },
      { driverId: 102, name: 'Sergio Perez', points: 0 }
    ],
    _metadata: {
      etag: Binary(Buffer.from("9f0f855a39f4ae8fecd7e6a123855fd1", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b868", "hex"), 0)
    }
  },
  {
    _id: 302,
    name: 'Ferrari',
    points: 43,
    driver: [
      { driverId: 103, name: 'Charles Leclerc', points: 25 },
      { driverId: 104, name: 'Carlos Sainz Jr', points: 18 }
    ],
    _metadata: {
      etag: Binary(Buffer.from("c5dd30f04da1a6a390bfab12b7d4f700", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d3b868", "hex"), 0)
    }
  }
]
mongo_test> 
```
コレクションteam_dv内のドキュメントにはレース名に関する情報が無いため、変更されていません。

Duality Viewのベースとなっているリレーショナル表の内容を確認してみます。
```
SQL> SELECT * FROM team;

   TEAM_ID NAME 			                  POINTS
---------- ------------------------------ ----------
	     2 Mercedes				                  27
       301 Red Bull				                   0
       302 Ferrari				                  43

SQL> SELECT * FROM driver;

 DRIVER_ID NAME 			                  POINTS	TEAM_ID
---------- ------------------------------ ---------- ----------
       105 George Russell			              12	      2
       106 Lewis Hamilton			              15	      2
       101 Max Verstappen			               0	    301
       102 Sergio Perez 			               0	    301
       103 Charles Leclerc			              25	    302
       104 Carlos Sainz Jr			              18	    302

6 rows selected.

SQL> SELECT * FROM race;

   RACE_ID NAME 				                LAPS RACE_DATE PODIUM
---------- ------------------------------ ---------- --------- --------------------------------------------------------------------------------
       201 Blue Air Bahrain Grand Prix		      57 20-MAR-22 {"winner":{"name":"Charles Leclerc","time":"01:37:33.584"},"firstRunnerUp":{"name":"Carlos Sainz Jr","time":"01:37:39.182"},"secondRunnerUp":{"name":"Lewis Hamilton","time":"01:37:43.259"}}
       202 Saudi Arabian Grand Prix		          50 27-MAR-22 {}
       203 Australian Grand Prix		          58 09-APR-22 {}

SQL> SELECT * FROM driver_race_map;

DRIVER_RACE_MAP_ID    RACE_ID  DRIVER_ID   POSITION
------------------ ---------- ---------- ----------
		         3	      201	     103	      1
		         4	      201	     104	      2
		         9	      201	     106	      3
		        10	      201	     105	      4

SQL> 
```
コレクションrace_dv内のドキュメントのnameの値を更新したことよって、race表のname列の値が変更されたことがわかりました。

updateOne()メソッドを使用して、コレクションdriver_dv内にある_idの値が102のドキュメントのteamの値を'Ferrari'に変更してみます。
```
mongo_test> db.driver_dv.updateOne({"_id" : 102} , {$set : {"team" : "Ferrari"}});
2023-05-16T10:57:26.572Z WARNING     oracle.soda.rdbms.impl.SODAUtils$1OracleSQLException
MongoServerError: ORA-40940: Cannot update field 'team' corresponding to column 'NAME' of table 'TEAM' in JSON Relational Duality View 'DRIVER_DV': Missing UPDATE annotation or NOUPDATE annotation specified.
mongo_test>
```
エラーが発生してコレクションdriver_dv内のteamの値を変更できませんでした。
これはdriver_dvのDDLの8行目でNOUPDATEが指定されているためです。
```
SQL> CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW driver_dv AS
  2    SELECT JSON {'_id' IS d.driver_id,
  3                 'name'     IS d.name,
  4                 'points'   IS d.points,
  5                 UNNEST
  6                   (SELECT JSON {'teamId' IS t.team_id,
  7                                 'team'   IS t.name WITH NOCHECK}
  8                      FROM team t WITH NOINSERT NOUPDATE NODELETE -- ※この部分
  9                      WHERE t.team_id = d.team_id),
 10                 'race'     IS
 11                   [ SELECT JSON {'driverRaceMapId' IS drm.driver_race_map_id,
 12                                  UNNEST
 13                                    (SELECT JSON {'raceId' IS r.race_id,
 14                                                  'name'   IS r.name}
 15                                       FROM race r WITH NOINSERT NOUPDATE NODELETE
 16                                       WHERE r.race_id = drm.race_id),
 17                                  'finalPosition'   IS drm.position}
 18                  FROM driver_race_map drm WITH INSERT UPDATE NODELETE
 19                  WHERE drm.driver_id = d.driver_id ]}
 20      FROM driver d WITH INSERT UPDATE DELETE;
```
deleteOne()メソッドを使用して、コレクションrace_dv内にある_idの値が202のドキュメントを削除してみます。
```
mongo_test> db.race_dv.deleteOne({_id:202});
{ acknowledged: true, deletedCount: 1 }
mongo_test>
```
コレクションrace_dvの内容を確認してみます。
```
mongo_test> db.race_dv.find();
[
  {
    _id: 201,
    name: 'Blue Air Bahrain Grand Prix',
    laps: 57,
    date: ISODate("2022-03-20T00:00:00.000Z"),
    podium: {
      winner: { name: 'Charles Leclerc', time: '01:37:33.584' },
      firstRunnerUp: { name: 'Carlos Sainz Jr', time: '01:37:39.182' },
      secondRunnerUp: { name: 'Lewis Hamilton', time: '01:37:43.259' }
    },
    result: [
      {
        driverRaceMapId: 3,
        position: 1,
        driverId: 103,
        name: 'Charles Leclerc'
      },
      {
        driverRaceMapId: 4,
        position: 2,
        driverId: 104,
        name: 'Carlos Sainz Jr'
      },
      {
        driverRaceMapId: 9,
        position: 3,
        driverId: 106,
        name: 'Lewis Hamilton'
      },
      {
        driverRaceMapId: 10,
        position: 4,
        driverId: 105,
        name: 'George Russell'
      }
    ],
    _metadata: {
      etag: Binary(Buffer.from("f6906a8f7a131c127faef32ca43af97a", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d6dcb7", "hex"), 0)
    }
  },
  {
    _id: 203,
    name: 'Australian Grand Prix',
    laps: 58,
    date: ISODate("2022-04-09T00:00:00.000Z"),
    podium: {},
    result: [],
    _metadata: {
      etag: Binary(Buffer.from("ea6e1194c012970ca07116ee1ef167e8", "hex"), 0),
      asof: Binary(Buffer.from("0000000000d6dcb7", "hex"), 0)
    }
  }
]
mongo_test> 
```
コレクションrace_dv内にあった_idの値が202のドキュメントが削除されました。

Duality Viewのベースとなっているリレーショナル表の内容を確認してみます。
```
SQL> SELECT * FROM race;

   RACE_ID NAME 				                LAPS RACE_DATE PODIUM
---------- ------------------------------ ---------- --------- --------------------------------------------------------------------------------
       201 Blue Air Bahrain Grand Prix		      57 20-MAR-22 {"winner":{"name":"Charles Leclerc","time":"01:37:33.584"},"firstRunnerUp":{"name":"Carlos Sainz Jr","time":"01:37:39.182"},"secondRunnerUp":{"name":"Lewis Hamilton","time":"01:37:43.259"}}
       203 Australian Grand Prix		          58 09-APR-22 {}

SQL>
```
コレクションrace_dvから_idの値が202のドキュメントを削除したことによって、race表内のrace_idが202のレコードが削除されたことがわかりました。

# 参考情報
・[JSON-Relational Duality Developer's Guide](https://docs.oracle.com/en/database/oracle/oracle-database/23/jsnvu/index.html)
・[Oracle Sample Projects : JSON Relational Duality](https://github.com/oracle-samples/oracle-db-examples/tree/main/json-relational-duality)