状態データベースの問合せ
このトピックでは、ブロックチェーン台帳の現在のステート・データが格納されているステート・データベースを問い合せる方法を理解するのに役立つ情報について説明します。
状態データベースとは
ブロックチェーン・レジャーの現在の状態データは、状態データベースに格納されます。
Oracle Blockchain Platformチェーンコードを開発するときに、リッチ問合せを実行することで状態データベースからデータを抽出できます。Oracle Blockchain Platformでは、SQLのリッチ問合せ構文およびCouchDB検索式を使用することで、リッチ問合せがサポートされています。「SQLリッチ問合せの構文」および「CouchDBリッチ問合せの構文」を参照してください。
Hyperledger Fabricでは、SQLリッチ問合せがサポートされません。Oracle Blockchain PlatformネットワークにHyperledger Fabric参加者が含まれる場合は、次の操作を実行する必要があります:
-
チェーンコードにSQLリッチ問合せ構文が含まれる場合、それらのチェーンコードがOracle Blockchain Platformを使用するメンバー・ピアにのみインストールされます。
-
チェーンコードをOracle Blockchain PlatformおよびHyperledger Fabricピアにインストールする必要がある場合、チェーンコードにCouchDB構文を使用し、Hyperledger Fabricピアがその状態データベース・リポジトリとしてCouchDBを使用するように設定されていることを確認します。Oracle Blockchain Platformでは、CouchDBを処理できます。
Oracle Blockchain PlatformとBerkeley DBの連携の仕組み
Oracle Blockchain Platformでは、Oracle Berkeley DBを状態データベースとして使用します。Oracle Blockchain Platformは、SQLite拡張に基づいてBerkeley DBにリレーショナル表を作成します。このアーキテクチャは、SQLリッチ問合せを検証するための堅牢でパフォーマンスの高い方法を提供します。
チャネル・チェーンコードごとに、Oracle Blockchain PlatformよってBerkeley DB表が作成されます。この表には状態情報データが格納されており、JSON形式のデータを使用しているかどうかに応じて、少なくともkeyという名前のキー列と、valueまたはvalueJsonという値列が含まれています。
| 列名 | 入力してください | 説明 |
|---|---|---|
key |
テキスト | 状態表のキー列。 |
value |
テキスト | 状態表の値列。 |
valueJson |
テキスト | 状態表のJSON形式の値列。 |
valueJson列とvalue列は、相互に排他的であることに注意してください。したがって、チェーンコードがキーにJSON値を割り当てた場合、valueJson列にその値が保持され、value列はnullに設定されます。チェーンコードで非JSON値がキーに割り当てられる場合、valueJson列はnullに設定され、value列はその値を保持します。
状態データベースの例
Car Dealerサンプルの状態データベースのキーとその値の例を次に示します:
| キー | 値 | valueJson |
|---|---|---|
| abg1234 | null | {"docType": "vehiclePart", "serialNumber": "abg1234", "assembler": "panama-parts", "assemblyDate": 1502688979, "name": "airbag 2020", "owner": "Detroit Auto", "recall": false, "recallDate": 1502688979} |
| abg1235 | null | {"docType": "vehiclePart", "serialNumber": "abg1235", "assembler": "panama-parts", "assemblyDate": 1502688979, "name": "airbag 4050", "owner": "Detroit Auto", "recall": false, "recallDate": 1502688979} |
| ser1236 | null | {"docType": "vehiclePart", "serialNumber": "ser1236", "assembler": "panama-parts", "assemblyDate": 1502688979, "name": "seatbelt 10020", "owner": "Detroit Auto", "recall": false, "recallDate": 150268979} |
| bra1238 | null | {"docType": "vehiclePart"、 "serialNumber": "bra1238"、 "アセンブラ": "bobs-bits"、 "assemblyDate": 1502688979、 "name": "brakepad 4200"、 "owner": "Detroit Auto"、 "recall": false、 "recallDate": 1502688979} |
| dtrt10001 | null | {"docType": "vehicle", "chassisNumber": "dtrt10001", "manufacturer": "Detroit Auto", "model": "a coupe", "assemblyDate": 1502688979, "airbagSerialNumber": "abg1235", "owner": "Sam Dealer", "recall": false, "recallDate": 150268979 |
サポートされるリッチ問合せ構文
Oracle Blockchain Platformでは、SQLリッチ問合せとCouchDBリッチ問合せの2種類のリッチ問合せ構文がサポートされ、状態データベースの問合せに使用できます。
SQLリッチ問合せの構文
Berkeley DB JSON拡張は、SQL関数の形式です。
始める前に
次の点に注意してください。
- 問合せの実行元チャネル・チェーンコード(<STATE>)にのみアクセスできます。
- SELECT文のみがサポートされます。
- 状態データベース表を変更できません。
- リッチ問合せ式には、SELECT文を1つのみ指定できます。
- このトピックの例は、リッチ問合せを作成できる方法をいくつか示しています。SQLデータベースを問い合せる通常の完全なSQL構文に対するアクセス権があります。
- JSON1拡張(SQLite拡張)に対するアクセス権があります。「JSON1 Extension」および「SQL As Understood by SQLite」を参照してください。
チェーン・コードの記述およびテストの詳細は、「チェーン・コードの開発」を参照。
問合せにおける状態データベースの参照方法
状態データベース表名は、Oracle Blockchain Platformによって内部的に管理されるため、チェーンコードの記述時に状態データベースの物理名を知っている必要はありません。
かわりに、<STATE>別名を使用して表名を参照する必要があります。たとえば、select key, value from <STATE>です。
<STATE>別名は大文字小文字を区別しないので、<state>、<STATE>あるいは<StAtE>のようなものを使用できます。
すべてのキーの取得
次の構文を使用します:
SELECT key FROM <STATE>たとえば、この構文を使用してCar Dealerサンプルを問い合せると、次のキーのリストが取得されます。
キー
abg1234
abg1235
ser1236
bra1238
dtrt10001
すべてのキーおよび値をキーのアルファベット順に取得
次の構文を使用します:
SELECT key AS serialNumber, valueJson AS details FROM <state> ORDER BY keyたとえば、この構文を使用してCar Dealerサンプルを問い合せると、次の結果が得られます。
| serialNumber | 詳細 |
|---|---|
| abg1234 | {"docType": "vehiclePart", "serialNumber": "abg1234", "assembler": "panama-parts", "assemblyDate": 1502688979, "name": "airbag 2020", "owner": "Detroit Auto", "recall": false, "recallDate": 1502688979} |
| abg1235 | {"docType": "vehiclePart", "serialNumber": "abg1235", "assembler": "panama-parts", "assemblyDate": 1502688979, "name": "airbag 4050", "owner": "Detroit Auto", "recall": false, "recallDate": 1502688979} |
| bra1238 | {"docType": "vehiclePart"、 "serialNumber": "bra1238"、 "アセンブラ": "bobs-bits"、 "assemblyDate": 1502688979、 "name": "brakepad 4200"、 "owner": "Detroit Auto"、 "recall": false、 "recallDate": 1502688979} |
| dtrt10001 | {"docType": "vehicle", "chassisNumber": "dtrt10001", "manufacturer": "Detroit Auto", "model": "a coupe", "assemblyDate": 1502688979, "airbagSerialNumber": "abg1235", "owner": "Sam Dealer", "recall": false, "recallDate": 150268979 |
| ser1236 | {"docType": "vehiclePart", "serialNumber": "ser1236", "assembler": "panama-parts", "assemblyDate": 1502688979, "name": "seatbelt 10020", "owner": "Detroit Auto", "recall": false, "recallDate": 150268979} |
"abg"で始まるすべてのキーおよび値の取得
次の構文を使用します:
SELECT key AS serialNumber, valueJson AS details FROM <state> WHERE key LIKE 'abg%'SELECT key, value FROM <STATE>たとえば、この構文を使用してCar Dealerサンプルを問い合せると、次の結果が得られます:
| serialNumber | 詳細 |
|---|---|
| abg1234 | {"docType": "vehiclePart", "serialNumber": "abg1234", "assembler": "panama-parts", "assemblyDate": "1502688979", "name": "airbag 2020", "owner": "Detroit Auto", "recall": "false", "recallDate": "1502688979"} |
| abg1235 | {"docType": "vehiclePart", "serialNumber": "abg1235", "assembler": "panama-parts", "assemblyDate": "1502688979", "name": "airbag 4050", "owner": "Detroit Auto", "recall": "false", "recallDate": "1502688979"} |
"Detroit Auto"が所有する自動車部品を含む値を持つすべてのキーの取得
次の構文を使用します:
SELECT key FROM <state> WHERE json_extract(valueJson, '$.docType') = 'vehiclePart' AND json_extract(valueJson, '$.owner') = 'Detroit Auto'
たとえば、この構文を使用してCar Dealerサンプルを問い合せると、次のキーのリストが取得されます:
キー
abg1234
abg1235
ser1236
bra1238
"Sam Dealer"が所有するすべての車のモデルおよびメーカーの取得
次の構文を使用します:
SELECT json_extract(valueJson, '$.model') AS model, json_extract(valueJson, '$.manufacturer') AS manufacturer FROM <state> WHERE json_extract(valueJson, '$.docType') = 'vehicle' AND json_extract(valueJson, '$.owner') = 'Sam Dealer'
たとえば、この構文を使用してCar Dealerサンプルを問い合せると、次の結果が得られます:
| モデル | 製造元 |
|---|---|
| クーペ型自動車 | 自動デトロイト |
状態値がJSON配列の場合、次の構文を使用して、Sam Dealerが所有するすべての車のモデルと製造元を取得できます:
SELECT json_extract(j.value, '$.model') AS model, json_extract(j.value, '$.manufacturer') AS manufacturer FROM <state> s, json_each(json_extract(s.valueJson,'$')) j WHERE json_valid(j.value) AND json_extract(j.value, '$.owner') = 'Sam Dealer'
CouchDBリッチ問合せ構文
このトピックの情報は、CouchDB構文が含まれるチェーンコードをOracle Blockchain Platformに移行する場合またはOracle Blockchain Platformネットワークに参加しているHyperledger Fabricピアにインストールするチェーンコードを記述する必要がある場合に使用します。
新しいチェーンコードを記述する場合、Oracleでは、SQLリッチ問合せを使用して、Oracle Blockchain PlatformとBerkeley DBが提供するパフォーマンス上の利点を活用することをお薦めします。
チェーン・コードの記述およびテストの詳細は、「チェーン・コードの開発」をご覧ください。
サポートされていない問合せパラメータおよびセレクタ構文
Oracle Blockchain Platformでは、use_indexパラメータはサポートされていません。これを使用した場合、Oracle Blockchain Platformではこのパラメータが無視され、問題のStateDBに定義されている索引が自動的に選択されます。
| パラメータ | 入力してください | 説明 |
|---|---|---|
| use_index | json | 特定の索引を使用するように問合せに指示します。 |
自動車のすべてのモデル、メーカーおよび所有者の取得と所有者による並替え
次の式を使用します。
{
"fields": ["model", "manufacturer", "owner"],
"sort": [
"owner"
]
}"Sam Dealer"が所有するすべての車のモデルおよびメーカーの取得
次の式を使用します。
{
"fields": ["model", "manufacturer"],
"selector": {
"docType" : "vehicle",
"owner" : "Sam Dealer"
}
}状態データベースの索引
状態データベースには大量のデータが格納されていることがあります。このような場合、Oracle Blockchain Platformでは索引を使用してデータ・アクセスを向上させます。
デフォルトの索引
チェーンコードをデプロイすると、Oracle Blockchain Platformによって索引が2つ作成されます。
-
キー索引: キー列に対して作成されます。
-
値索引: 値列に対して作成されます。
カスタム・インデックス
場合によっては、カスタム索引の作成が必要になる場合があります。これらの索引を定義するには、状態表のコンテキストで解決できる任意の式を使用します。Berkeley DBに対して作成されたカスタム索引は、SQLite構文に依存しますが、それ以外の場合は、Hyperledger Fabricが提供するのと同じCouchDB実装に従います。
カスタム索引を使用すると、大規模データ・セットに対するWHEREおよびORDER BY文のパフォーマンスを大幅に向上させることができます。カスタム索引を使用するとデータ挿入が遅くなるため、慎重に使用してください。
各カスタム索引は、複合索引をサポートする式の配列として定義され、1つのファイルにJSONドキュメントとして表現されます。ファイルごとに1つの索引があります。このファイルは、indexesというディレクトリ構造のstatedb/relationaldb/indexesというフォルダ内にあるチェーンコードとともにパッケージする必要があります。詳細は、「How to add CouchDB index during chaincode installation」をご覧ください。
カスタム索引の例
この項のカスタム索引の例では、Car Dealerサンプルを使用しています。
例1: この例では、WHERE式のコンテキストで json_extract式の使用を索引付けしています。
{"indexExpressions": ["json_extract(valueJson, '$.owner')"]}
たとえば:
SELECT … FROM … ORDER BY json_extract(valueJson, '$.owner')
例2: この例では、WHERE式およびORDER BY式のコンテキストで2つのjson_extract式の複合使用をインデックス化しています。
{"indexExpressions": ["json_extract(valueJson, '$.docType')", "json_extract(valueJson, '$.owner')"]}
たとえば:
SELECT … FROM … WHERE json_extract(valueJson, '$.docType') = 'vehiclePart' AND json_extract(valueJson, '$.owner') = 'Detroit Auto'
例3: この例では、例1で説明した索引と例2で説明した索引の、2つのインデックスが作成されます。各JSON構造は、個別のファイルに含める必要があります。各ファイルには、単一の索引(例1のような単純索引、または例2のような複合索引)を記述します。
索引1: {"indexExpressions": ["json_extract(valueJson, '$.owner')"]}
索引2: {"indexExpressions": ["json_extract(valueJson, '$owner')", "json_extract(valueJson, '$.docType')"]}
次の例では、索引2は問合せのWHERE部分のAND式に適用されている一方、索引1はORDER BY式に適用されています。
SELECT … FROM … WHERE json_extract(valueJson, '$.docType') = 'vehiclePart' AND json_extract(valueJson, '$.owner') = 'Detroit Auto' ORDER BY json_extract(valueJson, '$.owner')
JSONドキュメントの形式
JSONドキュメントは次の形式である必要があります:
{"indexExpressions": [expr1, ..., exprN]}
たとえば:
{"indexExpressions": ["json_extract(valueJson, '$.owner')"]}
リッチ問合せの検証の相違点
場合によっては、CouchDBリッチ問合せを含む標準のHyperledger Fabricと、Oracle Berkeley DBリッチ問合せの動作が異なることがあります。
CouchDBが含まれる標準のHyperledger Fabricでは、問合せによって返された各キーと値の各ペアはトランザクションの読取りセットに追加され、検証時に問合せを再実行せずに検証されます。Berkeley DBでは、返されたキーと値のペアは読取りセットに追加されませんが、リッチ問合せの結果はMerkleツリーでハッシュされ、検証時に問合せの再実行に対して検証されます。
ネイティブHyperledger Fabricでは、リッチ問合せに対するデータ保護は提供されません。ただし、Berkeley DBには、Merkleツリー・ハッシュ値を読取りセットに追加し、リッチ問合せを再実行して検証段階でMerkleツリー値を再計算することでリッチ問合せを保護および検証する機能が含まれています。Berkeley DBを使用するOracle Blockchain Platformでは検証がより正確であるため、チェーンコードの呼出しに頻繁なファントム読取りをフラグ付けされることがあります。