はじめに
こちらの記事でMongoDB ShellからOracle Database 23c FreeにアクセスしてJSON Relational Duality Viewをコレクションとして操作してみた際の検証内容を書きました。
この記事では、PythonからOracle Database 23cにOracle Database API for MongoDB経由でアクセスして、JSON Relational Duality Viewに対するドキュメント操作を行ってみました。
PythonからOracle Database API for MongoDBへのアクセスにはpymongoドライバーを使用しました。
1. 事前準備
JSON Relational Duality Viewのベースとなっている各表のデータを削除しておきます。
[oracle@oracle23c ~]$ sqlplus "mongo_test/MyPassword1!"@freepdb1
SQL*Plus: Release 23.0.0.0.0 - Developer-Release on Wed May 17 13:43:24 2023
Version 23.2.0.0.0
Copyright (c) 1982, 2023, Oracle. All rights reserved.
Last Successful login time: Wed May 17 2023 13:43:06 +00:00
Connected to:
Oracle Database 23c Free, Release 23.0.0.0.0 - Developer-Release
Version 23.2.0.0.0
SQL>
SQL> TRUNCATE TABLE driver_race_map;
Table truncated.
SQL> TRUNCATE TABLE race;
Table truncated.
SQL> TRUNCATE TABLE driver;
Table truncated.
SQL> TRUNCATE TABLE team;
Table truncated.
SQL>
PythonからMongoDBにアクセスするためのドライバーpymongoをインストールします。
[opc@23c ~]$ sudo pip3 install pymongo
WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead.
Collecting pymongo
Downloading https://files.pythonhosted.org/packages/10/3b/46541b4ee3000019b8ef5b1847292ddc77f492c162bc4d49c424db7fc97a/pymongo-4.1.1-cp36-cp36m-manylinux1_x86_64.whl (464kB)
100% |████████████████████████████████| 471kB 3.6MB/s
Installing collected packages: pymongo
Successfully installed pymongo-4.1.1
[opc@23c ~]$
2. pymongoを使用してPythonからOracle Database 23c Freeにアクセスする
Pythonをインタラクティブモードで起動します。
[opc@oracle23c ~]$ python
Python 3.6.8 (default, Feb 21 2023, 05:30:22)
[GCC 8.5.0 20210514 (Red Hat 8.5.0-16.0.2)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
MongoDBにアクセスするためのpymongoドライバーとデータ出力を整形するためのpprintモジュールをインポートし、pymongoからMongoClientをインポートします。
>>> import pymongo
>>> import pprint
>>> from pymongo import MongoClient
>>>
MongoClientを使用して、Oracle Database 23c FreeにOracle Database API for MongoDB経由でアクセスします。
>>> client = MongoClient('mongodb://mongo_test:MyPassword1!@localhost:27017/mongo_test?authMechanism=PLAIN&authSource=$external&ssl=true&tlsInsecure=true&retryWrites=false&loadBalanced=true')
>>>
データベースを取得します。
>>> db = client.mongo_test
>>>
コレクションの一覧を取得します。
>>> for collection_name in db.list_collection_names():
... print(collection_name)
...
driver_dv
race_dv
team_dv
>>>
SODAを有効にしたJSON Relational Duality Viewがコレクションとして表示されました。
find()メソッドを使用して、各コレクションの内容を確認します。
>>> for driver in driver_dv.find():
... pprint.pprint(driver)
...
>>> team_dv = db.team_dv
>>> for team in team_dv.find():
... pprint.pprint(team)
...
>>> driver_dv = db.driver_dv
>>> for driver in driver_dv.find():
... pprint.pprint(driver)
...
>>> race_dv = db.race_dv
>>> for race in race_dv.find():
... pprint.pprint(race)
...
>>>
この時点では、どのコレクションにもデータはまだ入っていません。
3. JSON-Relational Duality Viewをコレクションとして操作する
insert_one()メソッドを使用して、コレクションteam_dvに2名のドライバー情報を含むドキュメントを追加してみます。
>>> team = {
... "_id": 2,
... "name": "Mercedes",
... "points": 0,
... "driver": [
... {
... "driverId": 105,
... "name": "George Russell",
... "points": 0
... },
... {
... "driverId": 106,
... "name": "Lewis Hamilton",
... "points": 0
... }
... ]
... }
>>> insert_one_team_result = team_dv.insert_one(team)
>>> insert_one_team_result.inserted_id
2
>>>
コレクションteam_dvの内容を確認してみます。
>>> for team in team_dv.find():
... pprint.pprint(team)
...
{'_id': 2.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7^\xc5',
'etag': b'S`\x01\xf3\x1a\x87\x18\x81\x9a\xee\xf2\x8e\xc2\r\x86w'},
'driver': [{'driverId': 105.0, 'name': 'George Russell', 'points': 0.0},
{'driverId': 106.0, 'name': 'Lewis Hamilton', 'points': 0.0}],
'name': 'Mercedes',
'points': 0.0}
>>>
先ほど追加したドキュメントにメタデータ(etag、asof)が付加されて格納されていることが確認できました。
コレクションdriver_dvの内容を確認してみます。
>>> for driver in driver_dv.find():
... pprint.pprint(driver)
...
{'_id': 105.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7^\xcb',
'etag': b'\xa8\xbb\x18%\xf6!\x8e\xc0\xd3\x00g\x11sT\x05\x97'},
'name': 'George Russell',
'points': 0.0,
'race': [],
'team': 'Mercedes',
'teamId': 2.0}
{'_id': 106.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7^\xcb',
'etag': b'\xd3\xff2\x13y>0b\x04\xbb^P`6\x8eA'},
'name': 'Lewis Hamilton',
'points': 0.0,
'race': [],
'team': 'Mercedes',
'teamId': 2.0}
>>>
コレクション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レコード追加されたことが確認できました。
次に、insert_many()メソッドを使用して、コレクションteam_dvに複数のドキュメントを追加してみます。
>>> teams = [
... {
... "_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
... }
... ]
... }
... ]
>>> insert_many_teams_result = team_dv.insert_many(teams)
>>> insert_many_teams_result.inserted_ids
[301, 302]
>>>
コレクションteam_dvの内容を確認してみます。
>>> for team in team_dv.find():
... pprint.pprint(team)
...
{'_id': 2.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7_N',
'etag': b'S`\x01\xf3\x1a\x87\x18\x81\x9a\xee\xf2\x8e\xc2\r\x86w'},
'driver': [{'driverId': 105.0, 'name': 'George Russell', 'points': 0.0},
{'driverId': 106.0, 'name': 'Lewis Hamilton', 'points': 0.0}],
'name': 'Mercedes',
'points': 0.0}
{'_id': 301.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7_N',
'etag': b'\x9f\x0f\x85Z9\xf4\xae\x8f\xec\xd7\xe6\xa1#\x85_\xd1'},
'driver': [{'driverId': 101.0, 'name': 'Max Verstappen', 'points': 0.0},
{'driverId': 102.0, 'name': 'Sergio Perez', 'points': 0.0}],
'name': 'Red Bull',
'points': 0.0}
{'_id': 302.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7_N',
'etag': b'{bi\x9f\xec\xec\xff9!C\xbaz3^^G'},
'driver': [{'driverId': 103.0, 'name': 'Charles Leclerc', 'points': 0.0},
{'driverId': 104.0, 'name': 'Carlos Sainz Jr', 'points': 0.0}],
'name': 'Ferrari',
'points': 0.0}
>>>
insert_many()メソッドを使用して追加したドキュメントにメタデータが付与されて格納されていることが確認できました。
コレクションdriver_dvの内容を確認してみます。
>>> for driver in driver_dv.find():
... pprint.pprint(driver)
...
{'_id': 105.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7_[',
'etag': b'\xa8\xbb\x18%\xf6!\x8e\xc0\xd3\x00g\x11sT\x05\x97'},
'name': 'George Russell',
'points': 0.0,
'race': [],
'team': 'Mercedes',
'teamId': 2.0}
{'_id': 106.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7_[',
'etag': b'\xd3\xff2\x13y>0b\x04\xbb^P`6\x8eA'},
'name': 'Lewis Hamilton',
'points': 0.0,
'race': [],
'team': 'Mercedes',
'teamId': 2.0}
{'_id': 101.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7_[',
'etag': b'\xf9\xd9\x81]\xff\'\x87\x9fa8l\xfd\x16"\xb0e'},
'name': 'Max Verstappen',
'points': 0.0,
'race': [],
'team': 'Red Bull',
'teamId': 301.0}
{'_id': 102.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7_[',
'etag': b'\x98e\xa2\x9d\xee_WTgJ\x9b\x1d.\xc5\x870'},
'name': 'Sergio Perez',
'points': 0.0,
'race': [],
'team': 'Red Bull',
'teamId': 301.0}
{'_id': 103.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7_[',
'etag': b'\xc4z\x91\xf5{\xf3\xcfE\xf0\xd8(B@9\x9a\x90'},
'name': 'Charles Leclerc',
'points': 0.0,
'race': [],
'team': 'Ferrari',
'teamId': 302.0}
{'_id': 104.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7_[',
'etag': b'6>\xc6\xbb\xf0\xfa\xdd\x91;!\x94\x82\x95\x9d\xa3\x9d'},
'name': 'Carlos Sainz Jr',
'points': 0.0,
'race': [],
'team': 'Ferrari',
'teamId': 302.0}
>>>
insert_many()メソッドを使用してコレクション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レコード追加されたことがわかりました。
insert_many()メソッドを使用して、コレクションrace_dvに3つのドキュメントを追加します。
>>> races = [
... {
... "_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": {}
... }
... ]
>>> insert_many_races_result = race_dv.insert_many(races)
>>> insert_many_races_result.inserted_ids
[201, 202, 203]
>>>
コレクションrace_dvの内容を確認します。
>>> for race in race_dv.find():
... pprint.pprint(race)
...
{'_id': 201.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7`@',
'etag': b'.\x8d\xc0\x95C\xdd%\xdc}X\x8f\xb9sM\x96+'},
'date': datetime.datetime(2022, 3, 20, 0, 0),
'laps': 57.0,
'name': 'Bahrain Grand Prix',
'podium': {},
'result': []}
{'_id': 202.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7`@',
'etag': b'~\x05j\x84R\x12\xbf\xde\x19\xe0\xc0\xd0\xcdT\x9e\xa0'},
'date': datetime.datetime(2022, 3, 27, 0, 0),
'laps': 50.0,
'name': 'Saudi Arabian Grand Prix',
'podium': {},
'result': []}
{'_id': 203.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7`@',
'etag': b'\xean\x11\x94\xc0\x12\x97\x0c\xa0q\x16\xee'
b'\x1e\xf1g\xe8'},
'date': datetime.datetime(2022, 4, 9, 0, 0),
'laps': 58.0,
'name': 'Australian Grand Prix',
'podium': {},
'result': []}
>>>
insert_many()メソッドを使用して追加したドキュメントにメタデータが付与されて格納されていることが確認できました。
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レコード追加されたことがわかりました。
replace_oneメソッドを使用して、コレクションrace_dv内にある_idの値が201のドキュメントを、result(レース結果)を含む内容に変更(置換)してみます。
>>> race = {
... "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"
... }
... ],
... }
>>> replace_one_race_result = race_dv.replace_one({"_id":201},race)
>>> replace_one_race_result.matched_count
1
>>> replace_one_race_result.modified_count
1
>>> replace_one_race_result.raw_result
{'n': 1, 'nModified': 1, 'ok': 1, 'updatedExisting': True}
>>>
コレクションrace_dvの内容を確認します。
>>> for race in race_dv.find():
... pprint.pprint(race)
...
{'_id': 201.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7`\xd4',
'etag': b' \xf7\xd9\xf0\xc6\x9a\xc5\xf9Y\xdc\xa8\x19\xf9\x11hH'},
'date': datetime.datetime(2022, 3, 20, 0, 0),
'laps': 57.0,
'name': 'Bahrain Grand Prix',
'podium': {'firstRunnerUp': {'name': 'Carlos Sainz Jr',
'time': '01:37:39.182'},
'secondRunnerUp': {'name': 'Lewis Hamilton',
'time': '01:37:43.259'},
'winner': {'name': 'Charles Leclerc', 'time': '01:37:33.584'}},
'result': [{'driverId': 103.0,
'driverRaceMapId': 3.0,
'name': 'Charles Leclerc',
'position': 1.0},
{'driverId': 104.0,
'driverRaceMapId': 4.0,
'name': 'Carlos Sainz Jr',
'position': 2.0},
{'driverId': 106.0,
'driverRaceMapId': 9.0,
'name': 'Lewis Hamilton',
'position': 3.0},
{'driverId': 105.0,
'driverRaceMapId': 10.0,
'name': 'George Russell',
'position': 4.0}]}
{'_id': 202.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7`\xd4',
'etag': b'~\x05j\x84R\x12\xbf\xde\x19\xe0\xc0\xd0\xcdT\x9e\xa0'},
'date': datetime.datetime(2022, 3, 27, 0, 0),
'laps': 50.0,
'name': 'Saudi Arabian Grand Prix',
'podium': {},
'result': []}
{'_id': 203.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7`\xd4',
'etag': b'\xean\x11\x94\xc0\x12\x97\x0c\xa0q\x16\xee'
b'\x1e\xf1g\xe8'},
'date': datetime.datetime(2022, 4, 9, 0, 0),
'laps': 58.0,
'name': 'Australian Grand Prix',
'podium': {},
'result': []}
>>>
コレクションrace_dv内の_idの値が201のドキュメントが、レース結果を含む内容に変更(置換)されました。
コレクションteam_dvの内容を確認してみます。
>>> for team in team_dv.find():
... pprint.pprint(team)
...
{'_id': 2.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7`\xe7',
'etag': b'\x85X@\xb9\x05\xc8\xca\xfa\x99\xfb\x9c\xbf'
b'\x819\x92\xe5'},
'driver': [{'driverId': 105.0, 'name': 'George Russell', 'points': 12.0},
{'driverId': 106.0, 'name': 'Lewis Hamilton', 'points': 15.0}],
'name': 'Mercedes',
'points': 27.0}
{'_id': 301.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7`\xe7',
'etag': b'\x9f\x0f\x85Z9\xf4\xae\x8f\xec\xd7\xe6\xa1#\x85_\xd1'},
'driver': [{'driverId': 101.0, 'name': 'Max Verstappen', 'points': 0.0},
{'driverId': 102.0, 'name': 'Sergio Perez', 'points': 0.0}],
'name': 'Red Bull',
'points': 0.0}
{'_id': 302.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7`\xe7',
'etag': b'\xc5\xdd0\xf0M\xa1\xa6\xa3\x90\xbf\xab\x12'
b'\xb7\xd4\xf7\x00'},
'driver': [{'driverId': 103.0, 'name': 'Charles Leclerc', 'points': 25.0},
{'driverId': 104.0, 'name': 'Carlos Sainz Jr', 'points': 18.0}],
'name': 'Ferrari',
'points': 43.0}
>>>
コレクションrace_dv内のドキュメントをレース結果を含む内容に変更したことによって、コレクションteam_dv内の各ドキュメントのpointsおよび各ドライバーのpoints(driver.points)が更新されていることが確認できました。
コレクションdriver_dvの内容を確認してみます。
>>> for driver in driver_dv.find():
... pprint.pprint(driver)
...
{'_id': 105.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7`\xf5',
'etag': b".g\xa3\xa7R\xd7Pi'WH\x0bd\x85!\xb7"},
'name': 'George Russell',
'points': 12.0,
'race': [{'driverRaceMapId': 10.0,
'finalPosition': 4.0,
'name': 'Bahrain Grand Prix',
'raceId': 201.0}],
'team': 'Mercedes',
'teamId': 2.0}
{'_id': 106.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7`\xf5',
'etag': b'Fo&v\x91m\x9d\xb3\xb0\xb3\\\x87\x19-\xa2\xaf'},
'name': 'Lewis Hamilton',
'points': 15.0,
'race': [{'driverRaceMapId': 9.0,
'finalPosition': 3.0,
'name': 'Bahrain Grand Prix',
'raceId': 201.0}],
'team': 'Mercedes',
'teamId': 2.0}
{'_id': 101.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7`\xf5',
'etag': b'\xf9\xd9\x81]\xff\'\x87\x9fa8l\xfd\x16"\xb0e'},
'name': 'Max Verstappen',
'points': 0.0,
'race': [],
'team': 'Red Bull',
'teamId': 301.0}
{'_id': 102.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7`\xf5',
'etag': b'\x98e\xa2\x9d\xee_WTgJ\x9b\x1d.\xc5\x870'},
'name': 'Sergio Perez',
'points': 0.0,
'race': [],
'team': 'Red Bull',
'teamId': 301.0}
{'_id': 103.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7`\xf5',
'etag': b'^\xc1\xd8ga2\x0f\x1dy\xe3\xc8\xe0\x99\\\xf6r'},
'name': 'Charles Leclerc',
'points': 25.0,
'race': [{'driverRaceMapId': 3.0,
'finalPosition': 1.0,
'name': 'Bahrain Grand Prix',
'raceId': 201.0}],
'team': 'Ferrari',
'teamId': 302.0}
{'_id': 104.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7`\xf5',
'etag': b'\x1az \xf9\x8f\x84\x17`\xfe\xcd\xcdC&Z<\xef'},
'name': 'Carlos Sainz Jr',
'points': 18.0,
'race': [{'driverRaceMapId': 4.0,
'finalPosition': 2.0,
'name': 'Bahrain Grand Prix',
'raceId': 201.0}],
'team': 'Ferrari',
'teamId': 302.0}
>>>
コレクション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"に変更してみます。
>>> update_one_race_result = race_dv.update_one({"name" : "Bahrain Grand Prix"}, {"$set" : {"name" : "Blue Air Bahrain Grand Prix"}})
>>> update_one_race_result.matched_count
1
>>> update_one_race_result.modified_count
1
>>> update_one_race_result.raw_result
{'n': 1, 'nModified': 1, 'ok': 1, 'updatedExisting': True}
>>>
コレクションrace_dvの内容を確認してみます。
>>> for race in race_dv.find():
... pprint.pprint(race)
...
{'_id': 201.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7a$',
'etag': b'\xf6\x90j\x8fz\x13\x1c\x12\x7f\xae\xf3,\xa4:\xf9z'},
'date': datetime.datetime(2022, 3, 20, 0, 0),
'laps': 57.0,
'name': 'Blue Air Bahrain Grand Prix',
'podium': {'firstRunnerUp': {'name': 'Carlos Sainz Jr',
'time': '01:37:39.182'},
'secondRunnerUp': {'name': 'Lewis Hamilton',
'time': '01:37:43.259'},
'winner': {'name': 'Charles Leclerc', 'time': '01:37:33.584'}},
'result': [{'driverId': 103.0,
'driverRaceMapId': 3.0,
'name': 'Charles Leclerc',
'position': 1.0},
{'driverId': 104.0,
'driverRaceMapId': 4.0,
'name': 'Carlos Sainz Jr',
'position': 2.0},
{'driverId': 106.0,
'driverRaceMapId': 9.0,
'name': 'Lewis Hamilton',
'position': 3.0},
{'driverId': 105.0,
'driverRaceMapId': 10.0,
'name': 'George Russell',
'position': 4.0}]}
{'_id': 202.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7a$',
'etag': b'~\x05j\x84R\x12\xbf\xde\x19\xe0\xc0\xd0\xcdT\x9e\xa0'},
'date': datetime.datetime(2022, 3, 27, 0, 0),
'laps': 50.0,
'name': 'Saudi Arabian Grand Prix',
'podium': {},
'result': []}
{'_id': 203.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7a$',
'etag': b'\xean\x11\x94\xc0\x12\x97\x0c\xa0q\x16\xee'
b'\x1e\xf1g\xe8'},
'date': datetime.datetime(2022, 4, 9, 0, 0),
'laps': 58.0,
'name': 'Australian Grand Prix',
'podium': {},
'result': []}
>>>
コレクションrace_dv内のドキュメントのうち、nameの値が"Bahrain Grand Prix"であるドキュメント(_idの値が201のドキュメント)のnameの値が"Blue Air Bahrain Grand Prix"に変更されました。
コレクションdriver_dvの内容を確認してみます
>>> for driver in driver_dv.find():
... pprint.pprint(driver)
...
{'_id': 105.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7aP',
'etag': b'\x13\xf8\xdc\x14\xf9 _\xce\x16\xb7\xe9\x11\xc3kc2'},
'name': 'George Russell',
'points': 12.0,
'race': [{'driverRaceMapId': 10.0,
'finalPosition': 4.0,
'name': 'Blue Air Bahrain Grand Prix',
'raceId': 201.0}],
'team': 'Mercedes',
'teamId': 2.0}
{'_id': 106.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7aP',
'etag': b'n\xa0\xaa\x1f\xa6\xd8\x99lD\xb3u\xd0\xfbe\xe5\xf5'},
'name': 'Lewis Hamilton',
'points': 15.0,
'race': [{'driverRaceMapId': 9.0,
'finalPosition': 3.0,
'name': 'Blue Air Bahrain Grand Prix',
'raceId': 201.0}],
'team': 'Mercedes',
'teamId': 2.0}
{'_id': 101.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7aP',
'etag': b'\xf9\xd9\x81]\xff\'\x87\x9fa8l\xfd\x16"\xb0e'},
'name': 'Max Verstappen',
'points': 0.0,
'race': [],
'team': 'Red Bull',
'teamId': 301.0}
{'_id': 102.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7aP',
'etag': b'\x98e\xa2\x9d\xee_WTgJ\x9b\x1d.\xc5\x870'},
'name': 'Sergio Perez',
'points': 0.0,
'race': [],
'team': 'Red Bull',
'teamId': 301.0}
{'_id': 103.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7aP',
'etag': b'\xd4D\x02\xd6\xa4\xa1\x83q\x86A\x99&8N\x1dG'},
'name': 'Charles Leclerc',
'points': 25.0,
'race': [{'driverRaceMapId': 3.0,
'finalPosition': 1.0,
'name': 'Blue Air Bahrain Grand Prix',
'raceId': 201.0}],
'team': 'Ferrari',
'teamId': 302.0}
{'_id': 104.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7aP',
'etag': b'\xdf\x16\x11=A-\xb3\x9aLE]\xc9\xf1T=\xc9'},
'name': 'Carlos Sainz Jr',
'points': 18.0,
'race': [{'driverRaceMapId': 4.0,
'finalPosition': 2.0,
'name': 'Blue Air Bahrain Grand Prix',
'raceId': 201.0}],
'team': 'Ferrari',
'teamId': 302.0}
>>>
コレクションrace_dv内にあるドキュメントのnameの値を変更することによって、コレクションdriver_dv内にある各ドキュメントのレース名(race.name)の値が更新されていることが確認できました。
コレクションteam_dvの内容を確認してみます。
>>> for team in team_dv.find():
... pprint.pprint(team)
...
{'_id': 2.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7a1',
'etag': b'\x85X@\xb9\x05\xc8\xca\xfa\x99\xfb\x9c\xbf'
b'\x819\x92\xe5'},
'driver': [{'driverId': 105.0, 'name': 'George Russell', 'points': 12.0},
{'driverId': 106.0, 'name': 'Lewis Hamilton', 'points': 15.0}],
'name': 'Mercedes',
'points': 27.0}
{'_id': 301.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7a1',
'etag': b'\x9f\x0f\x85Z9\xf4\xae\x8f\xec\xd7\xe6\xa1#\x85_\xd1'},
'driver': [{'driverId': 101.0, 'name': 'Max Verstappen', 'points': 0.0},
{'driverId': 102.0, 'name': 'Sergio Perez', 'points': 0.0}],
'name': 'Red Bull',
'points': 0.0}
{'_id': 302.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7a1',
'etag': b'\xc5\xdd0\xf0M\xa1\xa6\xa3\x90\xbf\xab\x12'
b'\xb7\xd4\xf7\x00'},
'driver': [{'driverId': 103.0, 'name': 'Charles Leclerc', 'points': 25.0},
{'driverId': 104.0, 'name': 'Carlos Sainz Jr', 'points': 18.0}],
'name': 'Ferrari',
'points': 43.0}
>>>
コレクション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列の値が変更されたことがわかりました。
update_one()メソッドを使用して、コレクションdriver_dv内にある_idの値が102のドキュメントのteamの値を'Ferrari'に変更してみます。
>>> update_one_driver_result = driver_dv.update_one({"_id" : 102} , {"$set" : {"team" : "Ferrari"}})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib64/python3.6/site-packages/pymongo/collection.py", line 1018, in update_one
comment=comment,
File "/usr/local/lib64/python3.6/site-packages/pymongo/collection.py", line 816, in _update_retryable
(write_concern or self.write_concern).acknowledged and not multi, _update, session
File "/usr/local/lib64/python3.6/site-packages/pymongo/mongo_client.py", line 1399, in _retryable_write
return self._retry_with_session(retryable, func, s, None)
File "/usr/local/lib64/python3.6/site-packages/pymongo/mongo_client.py", line 1286, in _retry_with_session
return self._retry_internal(retryable, func, session, bulk)
File "/usr/local/lib64/python3.6/site-packages/pymongo/mongo_client.py", line 1320, in _retry_internal
return func(session, sock_info, retryable)
File "/usr/local/lib64/python3.6/site-packages/pymongo/collection.py", line 812, in _update
comment=comment,
File "/usr/local/lib64/python3.6/site-packages/pymongo/collection.py", line 761, in _update
_check_write_command_response(result)
File "/usr/local/lib64/python3.6/site-packages/pymongo/helpers.py", line 216, in _check_write_command_response
_raise_last_write_error(write_errors)
File "/usr/local/lib64/python3.6/site-packages/pymongo/helpers.py", line 189, in _raise_last_write_error
raise WriteError(error.get("errmsg"), error.get("code"), error)
pymongo.errors.WriteError: 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.
, full error: {'index': 0, 'errmsg': "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.\n", 'code': -1}
>>>
エラーが発生してコレクション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;
delete_one()メソッドを使用して、コレクションrace_dv内にある_idの値が203のドキュメントを削除してみます。
>>> delete_one_race_result = race_dv.delete_one({"_id":203})
>>> delete_one_race_result.raw_result
{'n': 1, 'ok': 1}
>>>
コレクションrace_dvの内容を確認してみます。
>>> for race in race_dv.find():
... pprint.pprint(race)
...
{'_id': 201.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7co',
'etag': b'\xf6\x90j\x8fz\x13\x1c\x12\x7f\xae\xf3,\xa4:\xf9z'},
'date': datetime.datetime(2022, 3, 20, 0, 0),
'laps': 57.0,
'name': 'Blue Air Bahrain Grand Prix',
'podium': {'firstRunnerUp': {'name': 'Carlos Sainz Jr',
'time': '01:37:39.182'},
'secondRunnerUp': {'name': 'Lewis Hamilton',
'time': '01:37:43.259'},
'winner': {'name': 'Charles Leclerc', 'time': '01:37:33.584'}},
'result': [{'driverId': 103.0,
'driverRaceMapId': 3.0,
'name': 'Charles Leclerc',
'position': 1.0},
{'driverId': 104.0,
'driverRaceMapId': 4.0,
'name': 'Carlos Sainz Jr',
'position': 2.0},
{'driverId': 106.0,
'driverRaceMapId': 9.0,
'name': 'Lewis Hamilton',
'position': 3.0},
{'driverId': 105.0,
'driverRaceMapId': 10.0,
'name': 'George Russell',
'position': 4.0}]}
{'_id': 202.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7co',
'etag': b'~\x05j\x84R\x12\xbf\xde\x19\xe0\xc0\xd0\xcdT\x9e\xa0'},
'date': datetime.datetime(2022, 3, 27, 0, 0),
'laps': 50.0,
'name': 'Saudi Arabian Grand Prix',
'podium': {},
'result': []}
>>>
コレクションrace_dv内にあった_idの値が203のドキュメントが削除されました。
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 {}
SQL>
コレクションrace_dvから_idの値が202のドキュメントを削除したことによって、race表内のrace_idが202のレコードが削除されたことがわかりました。
コレクションteam_dvとdriver_dvの内容を確認してみます。
>>> for team in team_dv.find():
... pprint.pprint(team)
...
{'_id': 2.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7c\xa0',
'etag': b'\x85X@\xb9\x05\xc8\xca\xfa\x99\xfb\x9c\xbf'
b'\x819\x92\xe5'},
'driver': [{'driverId': 105.0, 'name': 'George Russell', 'points': 12.0},
{'driverId': 106.0, 'name': 'Lewis Hamilton', 'points': 15.0}],
'name': 'Mercedes',
'points': 27.0}
{'_id': 301.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7c\xa0',
'etag': b'\x9f\x0f\x85Z9\xf4\xae\x8f\xec\xd7\xe6\xa1#\x85_\xd1'},
'driver': [{'driverId': 101.0, 'name': 'Max Verstappen', 'points': 0.0},
{'driverId': 102.0, 'name': 'Sergio Perez', 'points': 0.0}],
'name': 'Red Bull',
'points': 0.0}
{'_id': 302.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7c\xa0',
'etag': b'\xc5\xdd0\xf0M\xa1\xa6\xa3\x90\xbf\xab\x12'
b'\xb7\xd4\xf7\x00'},
'driver': [{'driverId': 103.0, 'name': 'Charles Leclerc', 'points': 25.0},
{'driverId': 104.0, 'name': 'Carlos Sainz Jr', 'points': 18.0}],
'name': 'Ferrari',
'points': 43.0}
>>>
>>> for driver in driver_dv.find():
... pprint.pprint(driver)
...
{'_id': 105.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7c\xb3',
'etag': b'\x13\xf8\xdc\x14\xf9 _\xce\x16\xb7\xe9\x11\xc3kc2'},
'name': 'George Russell',
'points': 12.0,
'race': [{'driverRaceMapId': 10.0,
'finalPosition': 4.0,
'name': 'Blue Air Bahrain Grand Prix',
'raceId': 201.0}],
'team': 'Mercedes',
'teamId': 2.0}
{'_id': 106.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7c\xb3',
'etag': b'n\xa0\xaa\x1f\xa6\xd8\x99lD\xb3u\xd0\xfbe\xe5\xf5'},
'name': 'Lewis Hamilton',
'points': 15.0,
'race': [{'driverRaceMapId': 9.0,
'finalPosition': 3.0,
'name': 'Blue Air Bahrain Grand Prix',
'raceId': 201.0}],
'team': 'Mercedes',
'teamId': 2.0}
{'_id': 101.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7c\xb3',
'etag': b'\xf9\xd9\x81]\xff\'\x87\x9fa8l\xfd\x16"\xb0e'},
'name': 'Max Verstappen',
'points': 0.0,
'race': [],
'team': 'Red Bull',
'teamId': 301.0}
{'_id': 102.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7c\xb3',
'etag': b'\x98e\xa2\x9d\xee_WTgJ\x9b\x1d.\xc5\x870'},
'name': 'Sergio Perez',
'points': 0.0,
'race': [],
'team': 'Red Bull',
'teamId': 301.0}
{'_id': 103.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7c\xb3',
'etag': b'\xd4D\x02\xd6\xa4\xa1\x83q\x86A\x99&8N\x1dG'},
'name': 'Charles Leclerc',
'points': 25.0,
'race': [{'driverRaceMapId': 3.0,
'finalPosition': 1.0,
'name': 'Blue Air Bahrain Grand Prix',
'raceId': 201.0}],
'team': 'Ferrari',
'teamId': 302.0}
{'_id': 104.0,
'_metadata': {'asof': b'\x00\x00\x00\x00\x00\xd7c\xb3',
'etag': b'\xdf\x16\x11=A-\xb3\x9aLE]\xc9\xf1T=\xc9'},
'name': 'Carlos Sainz Jr',
'points': 18.0,
'race': [{'driverRaceMapId': 4.0,
'finalPosition': 2.0,
'name': 'Blue Air Bahrain Grand Prix',
'raceId': 201.0}],
'team': 'Ferrari',
'teamId': 302.0}
>>>
コレクションrace_dv内にあった_idの値が203のドキュメントにはレース結果が含まれていなかったため、コレクションteam_dv、driver_dvの内容は変更されませんでした。
delete_many()メソッドを使用して、コレクションrace_dvの全てのドキュメントを削除します。
>>> delete_many_races_result = race_dv.delete_many({})
>>> delete_many_races_result.raw_result
{'n': 2, 'ok': 1}
>>>
コレクションrace_dvの内容を確認してみます。
>>> for race in race_dv.find():
... pprint.pprint(race)
...
>>>
コレクションrace_dvが空になったことが確認できました。
delete_many()メソッドを使用して、コレクションdriver_dvの全てのドキュメントを削除します。
>>> delete_many_driver_result = driver_dv.delete_many({})
>>> delete_many_driver_result.raw_result
{'n': 6, 'ok': 1}
>>>
コレクションdriver_dvの内容を確認してみます。
>>> for driver in driver_dv.find():
... pprint.pprint(driver)
...
>>>
コレクションdriver_dvが空になったことが確認できました。
delete_many()メソッドを使用して、コレクションteam_dvの全てのドキュメントを削除します。
>>> delete_many_team_result = team_dv.delete_many({})
>>> delete_many_team_result.raw_result
{'n': 3, 'ok': 1}
>>>
コレクションteam_dvの内容を確認してみます。
>>> for team in team_dv.find():
... pprint.pprint(team)
...
>>>
コレクションteam_dvが空になったことが確認できました。
4. Duality Viewのベースとなっている表の内容の確認
Duality Viewのベースとなっている各表のデータを確認してみます。
SQL> SELECT * FROM team;
no rows selected
SQL> SELECT * FROM driver;
no rows selected
SQL> SELECT * FROM race;
no rows selected
SQL> SELECT * FROM driver_race_map;
no rows selected
SQL>
Duality Viewのベースとなっている各表の全てのデータが削除されていることが確認できました。
参考情報
・PyMongo 4.3.3 documentation:mongo_client
・PyMongo 4.3.3 documentation:collection – Collection level operations
・PyMongo 4.3.3 documentation:results – Result class definitions
・Python Documentation:pprint