查询状态数据库

本主题包含的信息可帮助您了解如何查询存储区块链账本当前状态数据的状态数据库。

什么是状态数据库?

区块链账本的当前状态数据存储在状态数据库中。

开发 Oracle Blockchain Platform 链代码时,您可以通过执行富查询从状态数据库中提取数据。Oracle Blockchain Platform 使用 SQL 富查询语法和 CouchDB 查找表达式来支持富查询。请参阅 SQL Rich Query SyntaxCouchDB Rich Query Syntax

Hyperledger Fabric 不支持 SQL 丰富的查询。如果您的 Oracle Blockchain Platform 网络包含超级账本架构参与者,则您需要确保执行以下操作:

  • 如果链代码包含 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 在 Berkeley DB 中基于 SQLite 扩展创建关系表。此体系结构提供了一种强大的高性能方法来验证 SQL 富查询。

对于每个渠道链代码,Oracle Blockchain Platform 将创建一个 Berkeley DB 表。此表存储状态信息数据,并至少包含一个名为 key 的键列和一个名为 valuevalueJson 的值列,具体取决于您是否使用 JSON 格式数据。

Column Name(列名) 类型 说明
key TEXT 状态表的关键字列。
value TEXT State 表的值列。
valueJson TEXT 状态表的 JSON 格式值列。

请注意,valueJsonvalue 列是互斥的。因此,如果链代码为键分配了 JSON 值,则 valueJson 列将存储该值,并且值列将设置为 null。如果链代码为键分配了非 JSON 值,则 valueJson 列将设置为 null,并且值列将保存该值。

状态数据库示例

以下是 Car Dealer 示例状态数据库中的键及其值的示例:

value 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": 1502688979}
bra1238 null {"docType": "vehiclePart", "serialNumber": "bra1238", "assembler": "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": 1502688979

控制台中的丰富查询

管理员可以从控制台运行和分析丰富的查询。

  1. 转到控制台并选择渠道选项卡。
  2. 在通道表中,找到要运行查询的通道,单击通道 More Actions(更多操作)按钮,然后单击 Analyze Rich Query(分析富查询)。显示分析富查询对话框。
  3. 要针对状态数据库运行富查询,请选择查询执行
    1. 对于链代码,选择部署到要查询的渠道的链代码。
    2. 对于对等,选择要查询的对等方。
      只有当前组织中运行所选链代码的对等节点可用。
    3. 对于富查询,输入要运行和分析的富查询。
      查询格式必须遵循富查询语法。有关富查询语法的更多信息,请参见 Supported Rich Query Syntax
    4. 对于结果行限制,将滑块移动到要提取的最大结果行数。最多可以提取 50 行结果。
  4. 要获取查询的执行计划,请选择查询计划说明。查询执行计划是为运行查询而执行的操作序列。
    1. 对于链代码,选择部署到要查询的渠道的链代码。
    2. 对于对等,选择要查询的对等方。
    3. 对于收集,选择状态数据库或专用数据收集。
    4. 对于富查询,输入富查询。
      此查询不需要 explain 关键字。
      例如:select * from <state>
  5. 单击执行结果字段显示查询结果表或执行计划。要将结果表导出为 .csv 文件,请单击导出
    结果表大小限制为 1 MB。您可能需要细化查询以避免超出此限制。

支持的富查询语法

Oracle Blockchain Platform 支持两种类型的富查询语法,可用于查询状态数据库:SQL 富查询和 CouchDB 富查询。

SQL 富查询语法

Berkeley DB JSON 扩展以 SQL 函数的形式提供。

使用须知

请注意以下信息:

  • 您只能访问正在从中执行查询的渠道链代码 (<STATE>)。
  • 仅支持 SELECT 语句。
  • 无法修改状态数据库表。
  • 富查询表达式只能有一个 SELECT 语句。
  • 本主题中的示例只是编写富查询的几种方式。您可以访问查询 SQL 数据库时常用的完整 SQL 语法。
  • 您有权访问 JSON1 扩展(SQLite 扩展)。请参阅 JSON1 扩展SQL As Understanding 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", "assembler": "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": 1502688979
ser1236 {"docType": "vehiclePart", "serialNumber": "ser1236", "assembler": "panama-parts", "assemblyDate": 1502688979, "name": "seatbelt 10020", "owner": "Detroit Auto", "recall": false, "recallDate": 1502688979}

检索以 "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 经销商”拥有的所有汽车的型号和制造商

使用以下语法:

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 网络的超级账本架构对等节点上,请使用本主题中的信息。

如果您正在编写新的链代码,Oracle 建议您使用 SQL 丰富的查询来利用 Oracle Blockchain Platform with Berkeley DB 提供的性能优势。

如果您需要有关编写和测试链代码的更多信息,请参阅开发链代码

不支持的查询参数和选择器语法

Oracle Blockchain Platform 不支持 use_index 参数。如果使用,Oracle Blockchain Platform 将忽略此参数,并自动选取在相关 StateDB 上定义的索引。

Parameter (参数) 类型 说明
use_index json 指示查询使用特定索引。

检索汽车的所有型号、制造商和车主,并按车主订购

使用此表达式:

{ 
  "fields": ["model", "manufacturer", "owner"], 
  "sort": [   
    "owner" 
   ]
}

检索“Sam 经销商”拥有的所有汽车的型号和制造商

使用此表达式:

{ 
  "fields": ["model", "manufacturer"], 
  "selector": {   
    "docType"  : "vehicle",
     "owner" : "Sam Dealer" 
  }
}

状态数据库索引

状态数据库可以包含大量数据。在这种情况下,Oracle Blockchain Platform 使用索引来改善数据访问。

默认索引

部署链代码后,Oracle Blockchain Platform 将创建两个索引。

  • 键索引 - 在键列上创建。

  • 值索引 - 在值列上创建。

定制索引

在某些情况下,您可能需要创建定制索引。可以使用可以在状态表的上下文中解析的任何表达式定义这些索引。针对 Berkeley DB 创建的定制索引依赖于 SQLite 语法,但是它们遵循 Hyperledger Fabric 提供的相同 CouchDB 实现。

请注意,您可以使用定制索引来大幅提高 WHERE 和 ORDER BY 语句对大型数据集的性能。由于使用定制索引会减慢数据插入速度,因此应明智地使用它们。

每个自定义索引都定义为支持复合索引的表达式数组,在一个文件中以 JSON 文档表示(请注意,每个文件有一个索引)。必须将此文件与链代码打包在以下目录结构中名为 "indexes" 的文件夹中:statedb/relationaldb/indexes。请参见 How to add CouchDB indexes during chaincode installation

定制索引示例

本节中的自定义索引示例使用汽车经销商示例。

示例 1 - 此示例将索引在 WHERE 和 ORDER BY 表达式的上下文中使用 json_extract 表达式。

{"indexExpressions": ["json_extract(valueJson, '$.owner')"]}

例如:

SELECT … FROM … ORDER BY json_extract(valueJson, '$.owner')

示例 2 - 此示例在 WHERE 和 ORDER BY 表达式的上下文中索引了两个 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 中所述的索引。请注意,每个 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 富查询的标准超级账本架构和 Oracle Berkeley DB 富查询的行为方式不同。

在带有 CouchDB 的标准超级账本架构中,查询返回的每个键和值对将添加到事务处理的读集,并在验证时进行验证,而无需重新执行查询。在 Berkeley DB 中,返回的键和值对不会添加到读取集中,但富查询的结果会散列在 Merkle 树中,并在验证时针对重新执行查询进行验证。

本机超级账本架构不为富查询提供数据保护。但是,Berkeley DB 包含通过将 Merkle 树散列值添加到读取集中、重新执行富查询以及在验证阶段重新计算 Merkle 树值来保护和验证富查询的功能。请注意,由于 Berkeley DB 的 Oracle Blockchain Platform 中的验证更准确,因此链代码调用有时会被标记为更频繁的虚拟件读取。