LoginSignup
4
1

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

Last updated at Posted at 2023-05-17

はじめに

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

この記事では、その続きとして、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」の「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に2名のドライバー情報を含むドキュメントを追加してみます。

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
Oracle Sample Projects : JSON Relational Duality

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1