技术标签: fabric 区块链 fabric
使用 CouchDB
将CouchDB用作Hyperledger Fabric的状态数据库所需的步骤。
教程分为以下几步:
- 在Hyperledger Fabric中启用CouchDB
- 创建一个索引
- 将索引添加到您的chaincode文件夹
- 部署智能合约
- 查询CouchDB状态数据库
- 使用最佳做法进行查询和索引
- 通过分页查询CouchDB状态数据库
- 更新索引
- 删除索引
要更深入地了解CouchDB,请参考CouchDB作为状态数据库,有关Fabric账本的更多信息,请参考Ledger主题。 请遵循以下教程,以获取有关如何在区块链网络中利用CouchDB的详细信息。
在整个教程中,我们将使用资产转移账本查询示例作为我们的用例,以演示如何将CouchDB与Fabric一起使用,包括对状态数据库执行JSON查询。 您应该已经完成了“安装示例,二进制文件和Docker映像”任务。
为什么是Couch DB?
Fabric支持两种类型的peer状态数据库。 LevelDB是peer节点中嵌入的默认状态数据库。 LevelDB将链码数据存储为简单的键值对,并且仅支持键,键范围和组合键查询。 CouchDB是一个可选的备用状态数据库,它使您可以将账本上的数据建模为JSON并针对数据值(而不是键)发出丰富的查询。 CouchDB支持还允许您使用链码部署索引,以提高查询效率,并使您能够查询大型数据集。
为了利用CouchDB的好处,即基于内容的JSON查询,您的数据必须以JSON格式建模。设置网络之前,您必须决定使用LevelDB还是CouchDB。由于数据兼容性问题,不支持将peer从使用LevelDB切换到CouchDB。网络上的所有peer必须使用相同的数据库类型。如果混合使用JSON和二进制数据值,则仍然可以使用CouchDB,但是只能根据键,键范围和组合键查询来查询二进制值。
在Fabric中启用CouchDB
CouchDB作为独立的数据库进程与peer同时运行。在设置,管理和操作方面还有其他注意事项。 CouchDB的Docker映像可用,我们建议将其与peer在同一服务器上运行。您将需要为每个peer设置一个CouchDB容器,并通过更改core.yaml中的配置以指向CouchDB容器来更新每个peer容器。 core.yaml文件必须位于环境变量FABRIC_CFG_PATH指定的目录中:
- 对于Docker部署,core.yaml是预先配置的,位于peer容器FABRIC_CFG_PATH文件夹中。但是,在使用Docker环境时,您可以传递环境变量以覆盖core.yaml属性,例如CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS来设置CouchDB地址。
- 对于本机二进制部署,发布工件分发中包含core.yaml。
编辑core.yaml的stateDatabase部分。将CouchDB指定为stateDatabase并填写关联的couchDBConfig属性。有关更多信息,请参见CouchDB配置。
创建索引
为什么索引重要?
索引允许查询数据库,而不必每次查询都检查每一行,从而使它们运行得更快,更有效。 通常,为频繁出现的查询条件构建索引,从而可以更有效地查询数据。 为了利用CouchDB的主要优点(对JSON数据执行丰富查询的能力),不需要索引,但是强烈建议使用索引来提高性能。 另外,如果查询中需要排序,则CouchDB需要包含排序字段的索引。
注意:没有索引的JSON查询可能会起作用,但会在peer日志中引发一条警告,指出未找到该索引。 但是,如果富查询包含排序规范,则需要该字段的索引;否则,该字段将为空。 否则,查询将失败并且将引发错误。
为了演示构建索引,我们将使用资产转移账本查询示例中的数据。 在此示例中,资产数据结构定义为 :
type Asset struct {
DocType string `json:"docType"` //docType is used to distinguish the various types of objects in state database
ID string `json:"ID"` //the field tags are needed to keep case from bouncing around
Color string `json:"color"`
Size int `json:"size"`
Owner string `json:"owner"`
AppraisedValue int `json:"appraisedValue"`
}
在此结构中,属性(docType,ID,color,size,owner,appraisedValue)定义了与资产关联的账本数据。 docType属性是一种模式,可在链码中使用以区分链码名称空间中可能需要单独查询的不同数据类型。 使用CouchDB时,每个链码都表示为自己的CouchDB数据库,也就是说,每个链码都具有自己的**命名空间。
关于Asset数据结构,docType用于标识此JSON文档代表资产。 chaincode名称空间中可能还会存在其他JSON文档类型。 任何JSON字段都可以在CouchDB JSON查询中使用。
定义在链码查询中使用的索引时,每个索引都必须在其自己的文本文件中以扩展名* .json定义,并且索引定义的格式必须为CouchDB索引JSON格式。
要定义索引,需要三项信息:
- fields: these are the fields to query
- name: name of the index
- type: always “json” in this context
如下:
{
"index": {
"fields": ["foo"]
},
"name" : "foo-index",
"type" : "json"
}
可选地,可以在索引定义上指定设计文档属性ddoc。 设计文档是旨在包含索引的CouchDB构造。 索引可以分组到设计文档中以提高效率,但是CouchDB建议每个设计文档使用一个索引 。
小技巧:
定义索引时,最好将ddoc属性和值以及索引名称包括在内。 包含此属性很重要,以确保您以后可以根据需要更新索引。 此外,它还使您能够显式指定要在查询中使用的索引。
这是资产转移账本查询样本中索引定义为索引名称为indexOwner的另一个示例,它使用多个字段docType和owner,并包含ddoc属性:
{
"index":{
"fields":["docType","owner"] // Names of the fields to be queried
},
"ddoc":"indexOwnerDoc", // (optional) Name of the design document in which the index will be created.
"name":"indexOwner",
"type":"json"
}
在上面的示例中,如果设计文档indexOwnerDoc不存在,则在部署索引时会自动创建它。 可以使用在字段列表中指定的一个或多个属性来构造索引,并且可以指定属性的任何组合。 一个属性可以存在于同一docType的多个索引中。 在以下示例中,index1仅包括属性所有者,index2包括属性所有者和颜色,而index3包括属性所有者,颜色和大小。 另外,请注意,按照CouchDB建议的做法,每个索引定义都有其自己的ddoc值 。
{
"index":{
"fields":["owner"] // Names of the fields to be queried
},
"ddoc":"index1Doc", // (optional) Name of the design document in which the index will be created.
"name":"index1",
"type":"json"
}
{
"index":{
"fields":["owner", "color"] // Names of the fields to be queried
},
"ddoc":"index2Doc", // (optional) Name of the design document in which the index will be created.
"name":"index2",
"type":"json"
}
{
"index":{
"fields":["owner", "color", "size"] // Names of the fields to be queried
},
"ddoc":"index3Doc", // (optional) Name of the design document in which the index will be created.
"name":"index3",
"type":"json"
}
通常,您应该对索引字段进行建模,以匹配将在查询过滤器和排序中使用的字段。 有关以JSON格式构建索引的更多详细信息,请参阅CouchDB文档。
关于索引的最后一句话,Fabric使用称为索引预热的模式对数据库中的文档进行索引。 CouchDB通常在下一次查询之前不会索引新的或更新的文档。 Fabric通过在提交每个数据块后请求索引更新来确保索引保持“热”状态。 这样可以确保查询速度很快,因为它们不必在运行查询之前就对文档进行索引。 每次将新记录添加到状态数据库时,此过程都会使索引保持最新并刷新。
将索引添加到您的chaincode文件夹
最终确定索引后,需要通过将其放在适当的元数据文件夹中,将其与链代码打包以进行部署。 您可以使用peer生命周期chaincode命令打包并安装chaincode。 JSON索引文件必须位于路径META-INF下
下面的资产转移账本查询示例说明了索引如何与链码打包在一起。
此示例包括一个名为indexOwnerDoc的索引,以支持资产所有者的查询:
{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}
启动网络
先down掉:
./network.sh down
如果您以前没有阅读过本教程,则需要供应链码依赖项,然后才能将其部署到网络中。 运行以下命令:
cd ../asset-transfer-ledger-queries/chaincode-go
GO111MODULE=on go mod vendor
cd ../../test-network
在test-network目录中,使用以下命令使用CouchDB部署测试网络:
./network.sh up createChannel -s couchdb
这将创建两个使用CouchDB作为状态数据库的结构peer节点。 它还将创建一个排序节点和一个名为mychannel的通道。
部署智能合约
您可以使用测试网络脚本将资产转移账本查询智能合约部署到通道。 运行以下命令以将智能合约部署到mychannel:
./network.sh deployCC -ccn ledger -ccp ../asset-transfer-ledger-queries/chaincode-go/ -ccl go -ccep "OR('Org1MSP.peer','Org2MSP.peer')"
请注意,我们正在使用-ccep标志来部署智能合约,其认可策略为“ OR(‘Org1MSP.peer’,‘Org2MSP.peer’)”。 这样一来,任何一个组织都可以创建资产而无需得到另一个组织的认可。
验证索引已部署
一旦链码已安装到peer并部署到通道,索引将被部署到每个peer的CouchDB状态数据库。 您可以通过检查Docker容器中的peer日志来验证CouchDB索引是否已成功创建。
要查看peerDocker容器中的日志,请打开一个新的Terminal窗口,然后运行以下命令grep以确认创建索引的消息。
docker logs peer0.org1.example.com 2>&1 | grep "CouchDB index"
可以看到如下输出:
[couchdb] createIndex -> INFO 072 Created CouchDB index [indexOwner] in state database [mychannel_ledger] using design document [_design/indexOwnerDoc]
查询CouchDB数据库
现在已经在JSON文件中定义了索引并将其与chaincode一起部署,chaincode函数可以对CouchDB状态数据库执行JSON查询。
在查询中指定索引名称是可选的。 如果未指定,并且要查询的字段已经存在索引,则将自动使用现有索引。
小技巧:
优良作法是使用use_index关键字在查询中显式包括索引名称。 没有它,CouchDB可能会选择不太理想的索引。 另外,在测试过程中,CouchDB可能根本不使用索引,并且您可能无法意识到索引的数量。 由于CouchDB未使用索引,因此仅在更高的卷上,您可能会发现性能降低。
在链码中创建查询
您可以使用链码中定义的查询对账本上的数据执行JSON查询。 资产转移账本查询示例包括两个JSON查询功能:
-
QueryAssets
ad hoc JSON查询的示例。 这是一个查询,可以将选择器JSON查询字符串传递到该函数中。 该查询对于需要在运行时动态构建自己的查询的客户端应用程序很有用。 有关查询选择器的更多信息,请参考CouchDB选择器语法。
-
QueryAssetsByOwner
参数化查询的示例,该查询在链码中定义,但允许传递查询参数。在这种情况下,该函数接受单个参数,即资产所有者。 然后,它使用JSON查询语法在状态数据库中查询与docType和“所有者”匹配的JSON文档。
使用peer命令运行查询
在没有客户端应用程序的情况下,我们可以使用peer命令测试链码中定义的查询。 我们将使用peer chaincode query命令来使用Assets索引indexOwner并使用QueryAssets函数查询“ tom”拥有的所有资产。
在查询数据库之前,我们应该添加一些数据。 以Org1身份运行以下命令,以创建“ tom”拥有的资产:
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n ledger -c '{"Args":["CreateAsset","asset1","blue","5","tom","35"]}'
下一步,查询tom拥有的所有资产。
// Rich Query with index name explicitly specified:
peer chaincode query -C mychannel -n ledger -c '{"Args":["QueryAssets", "{\"selector\":{\"docType\":\"asset\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}'
深入研究上面的查询命令,需要关注三个参数:
-
QueryAssets
Assets链码中的函数名称。 如您在下面的链码函数中看到的那样,QueryAssets()调用getQueryResultForQueryString(),然后将queryString传递给getQueryResult()填充程序API,该API对状态数据库执行JSON查询。
func (t *SimpleChaincode) QueryAssets(ctx contractapi.TransactionContextInterface, queryString string) ([]*Asset, error) {
return getQueryResultForQueryString(ctx, queryString)
}
{"selector":{"docType":"asset","owner":"tom"}
这是一个ad hoc 选择器字符串的示例,该字符串查询所有者属性值为tom的资产类型的所有文档。
"use_index":["_design/indexOwnerDoc", "indexOwner"]
指定设计文档名称indexOwnerDoc和索引名称indexOwner。 在此示例中,选择器查询显式包含通过使用use_index关键字指定的索引名称。 回顾上面创建索引的索引定义,它包含一个设计文档“ ddoc”:“ indexOwnerDoc”。 使用CouchDB,如果您计划在查询中显式包括索引名称,则索引定义必须包含ddoc值,因此可以使用use_index关键字进行引用。
查询成功运行,并且索引与以下结果一起使用:
[{"docType":"asset","ID":"asset1","color":"blue","size":5,"owner":"tom","appraisedValue":35}]
使用最佳做法进行查询和索引
使用索引的查询将更快地完成,而不必扫描CouchDB中的整个数据库。 了解索引将使您能够编写查询以提高性能,并帮助您的应用程序处理大量数据。
计划使用链码安装的索引也很重要。 您应该为每个链码仅安装几个支持大多数查询的索引。 添加太多索引,或在索引中使用过多字段,都会降低网络性能。 这是因为在提交每个块后都会更新索引。 需要通过“索引预热”更新的索引越多,完成交易所需的时间就越长。
本节中的示例将帮助演示查询如何使用索引以及哪种类型的查询将具有最佳性能。 编写查询时,请记住以下几点:
- 索引中的所有字段也必须在查询的选择器或排序部分中,才能使用索引。
- 更复杂的查询将具有较低的性能,并且不太可能使用索引。
- 您应该避免使用会导致全表扫描或全索引扫描的运算符,例如$ or,$ in和$ regex。
在本教程的上一部分中,您针对资产链代码发出了以下查询:
// Example one: query fully supported by the index
export CHANNEL_NAME=mychannel
peer chaincode query -C $CHANNEL_NAME -n ledger -c '{"Args":["QueryAssets", "{\"selector\":{\"docType\":\"asset\",\"owner\":\"tom\"}, \"use_index\":[\"indexOwnerDoc\", \"indexOwner\"]}"]}'
资产转移账本查询链代码已与indexOwnerDoc索引一起安装:
{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}
请注意,查询中的两个字段docType和owner都包含在索引中,从而使其成为完全受支持的查询。 结果,该查询将能够使用索引中的数据,而不必搜索整个数据库。 像这样的完全受支持的查询将比链码中的其他查询返回得更快。
如果您在上面的查询中添加了额外的字段,它仍将使用索引。 但是,该查询将另外必须扫描数据库中的多余字段,从而导致更长的响应时间。 例如,下面的查询仍将使用索引,但返回的时间将比前一个示例更长。
// Example two: query fully supported by the index with additional data
peer chaincode query -C $CHANNEL_NAME -n ledger -c '{"Args":["QueryAssets", "{\"selector\":{\"docType\":\"asset\",\"owner\":\"tom\",\"color\":\"blue\"}, \"use_index\":[\"/indexOwnerDoc\", \"indexOwner\"]}"]}'
不包含索引中所有字段的查询将不得不扫描整个数据库。 例如,下面的查询搜索所有者,而不指定所拥有项目的类型。 由于ownerIndexDoc包含owner和docType字段,因此该查询将无法使用索引。
// Example three: query not supported by the index
peer chaincode query -C $CHANNEL_NAME -n ledger -c '{"Args":["QueryAssets", "{\"selector\":{\"owner\":\"tom\"}, \"use_index\":[\"indexOwnerDoc\", \"indexOwner\"]}"]}'
通常,更复杂的查询将具有更长的响应时间,并且被索引支持的机会也较低。 $ or,$ in和$ regex等运算符通常会导致查询扫描完整索引或根本不使用索引。
例如,下面的查询包含一个$或一个词,它将搜索tom拥有的每个资产和每个项目。
// Example four: query with $or supported by the index
peer chaincode query -C $CHANNEL_NAME -n ledger -c '{"Args":["QueryAssets", "{\"selector\":{\"$or\":[{\"docType\":\"asset\"},{\"owner\":\"tom\"}]}, \"use_index\":[\"indexOwnerDoc\", \"indexOwner\"]}"]}'
该查询仍将使用索引,因为它会搜索indexOwnerDoc中包含的字段。 但是,查询中的$ or条件要求扫描索引中的所有项目,从而导致更长的响应时间。
以下是索引不支持的复杂查询的示例。
// Example five: Query with $or not supported by the index
peer chaincode query -C $CHANNEL_NAME -n ledger -c '{"Args":["QueryAssets", "{\"selector\":{\"$or\":[{\"docType\":\"asset\",\"owner\":\"tom\"},{\"color\":\"yellow\"}]}, \"use_index\":[\"indexOwnerDoc\", \"indexOwner\"]}"]}'
该查询将搜索tom拥有的所有资产或其他任何黄色项目。 此查询将不使用索引,因为它将需要搜索整个表以满足$ or条件。 根据您账本中的数据量,此查询将需要很长时间才能响应或可能会超时。
虽然遵循最佳实践进行查询很重要,但是使用索引并不是收集大量数据的解决方案。 区块链数据结构经过优化以验证和确认交易,不适合数据分析或报告。 如果要构建仪表板作为应用程序的一部分或分析网络中的数据,最佳做法是查询可复制peer数据的链下数据库。 这将使您能够理解区块链上的数据,而不会降低网络性能或中断交易。
您可以使用应用程序中的阻止或链式代码事件将交易数据写入链下数据库或分析引擎。 对于接收到的每个块,块侦听器应用程序将循环访问这些块交易,并使用来自每个有效交易的rwset的键/值写入来构建数据存储。 基于peer通道的事件服务提供可重播事件,以确保下游数据存储的完整性。 有关如何使用事件侦听器将数据写入外部数据库的示例,请访问结构样本中的脱链数据样本。
通过分页查询CouchDB状态数据库
当CouchDB查询返回较大的结果集时,可以使用一组API,这些API可以由链码调用以对结果列表进行分页。 分页提供了一种机制,可以通过指定页面大小和起点(用于指示从何处开始结果集的书签)来对结果集进行分区。 客户端应用程序迭代地调用执行查询的链码,直到没有更多结果返回为止。 有关更多信息,请参阅有关使用CouchDB进行分页的主题。
我们将使用资产转移账本查询样本函数QueryAssetsWithPagination来演示如何在链码和客户端应用程序中实现分页。
- QueryAssetsWithPagination –
带分页的临时JSON查询示例。 这是一个查询,其中可以将选择器字符串传递到类似于以上示例的函数中。 在这种情况下,查询中还包括pageSize和书签。
为了演示分页,需要更多数据。 本示例假定您已经从上方添加了asset1。 在peer容器中运行以下命令,以创建“ tom”拥有的另外四个资产,以创建“ tom”拥有的总共五个资产:
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n ledger -c '{"Args":["CreateAsset","asset2","yellow","5","tom","35"]}'
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n ledger -c '{"Args":["CreateAsset","asset3","green","6","tom","20"]}'
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n ledger -c '{"Args":["CreateAsset","asset4","purple","7","tom","20"]}'
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n ledger -c '{"Args":["CreateAsset","asset5","blue","8","tom","40"]}'
除了上一个示例中用于查询的参数之外,QueryAssetsWithPagination还添加了页面大小和书签。 PageSize指定每个查询要返回的记录数。 书签是一个“ anchor”,用于告知ouchDB从何处开始页面。 (每页结果返回一个唯一的书签。)
QueryAssetsWithPagination
如您在下面的链码函数中看到的那样,QueryAssetsWithPagination()调用getQueryResultForQueryStringWithPagination(),然后将queryString以及书签和pagesize传递给GetQueryResultWithPagination()匀场API,该API对状态数据库执行分页JSON查询。
func (t *SimpleChaincode) QueryAssetsWithPagination(
ctx contractapi.TransactionContextInterface,
queryString,
bookmark string,
pageSize int) ([]*Asset, error) {
return getQueryResultForQueryStringWithPagination(ctx, queryString, int32(pageSize), bookmark)
}
下面的示例是一个peer命令,该命令以pageSize为3且未指定书签的方式调用QueryAssetsWithPagination。
提示:如果未指定书签,则查询从记录的“第一”页开始。
// Rich Query with index name explicitly specified and a page size of 3:
peer chaincode query -C mychannel -n ledger -c '{"Args":["QueryAssetsWithPagination", "{\"selector\":{\"docType\":\"asset\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}","","3"]}'
收到以下响应(为清楚起见添加了回车),由于pagsize设置为3,因此返回了五项资产中的三项:
[{"docType":"asset","ID":"asset1","color":"blue","size":5,"owner":"tom","appraisedValue":35},
{"docType":"asset","ID":"asset2","color":"yellow","size":5,"owner":"tom","appraisedValue":35},
{"docType":"asset","ID":"asset3","color":"green","size":6,"owner":"tom","appraisedValue":20}]
注意:书签由CouchDB为每个查询唯一生成,并在结果集中代表一个占位符。 在查询的后续迭代中传递返回的书签,以检索下一组结果。
以下是一个peer命令,以pageSize为3来调用QueryAssetsWithPagination。注意,这次,该查询包括从上一个查询返回的书签。
peer chaincode query -C $CHANNEL_NAME -n ledger -c '{"Args":["QueryAssetsWithPagination", "{\"selector\":{\"docType\":\"asset\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}","g1AAAABLeJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqz5yYWJeWkGoOkOWDSOSANIFk2iCyIyVySn5uVBQAGEhRz","3"]}'
收到以下响应(为清楚起见添加了回车)。 检索到最后两个记录:
[{"Key":"asset4", "Record":{"color":"purple","docType":"asset","name":"asset4","size":"7","owner":"tom","appraisedValue":20}},
{"Key":"asset5", "Record":{"color":"blue","docType":"asset","name":"asset5","size":"8","owner":"tom","appraisedValue":40}}]
[{"ResponseMetadata":{"RecordsCount":"2",
"Bookmark":"g1AAAABLeJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqz5yYWJeWkmoKkOWDSOSANIFk2iCyIyVySn5uVBQAGYhR1"}}]
最后一个命令是一个peer命令,用于调用QueryAssetsWithPagination,其页面大小为3,并使用上一个查询的书签。
peer chaincode query -C $CHANNEL_NAME -n ledger -c '{"Args":["QueryAssetsWithPagination", "{\"selector\":{\"docType\":\"asset\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}","g1AAAABLeJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqz5yYWJeWkmoKkOWDSOSANIFk2iCyIyVySn5uVBQAGYhR1","3"]}'
收到以下响应(为清楚起见添加了回车)。 没有记录返回,表明已检索到所有页面:
[]
[{"ResponseMetadata":{"RecordsCount":"0",
"Bookmark":"g1AAAABLeJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqz5yYWJeWkmoKkOWDSOSANIFk2iCyIyVySn5uVBQAGYhR1"}}]
有关客户端应用程序如何使用分页迭代结果集的示例,请在资产转移账本查询示例中搜索getQueryResultForQueryStringWithPagination函数。
更新索引
可能有必要随时间更新索引。 在安装的链码的后续版本中可能存在相同的索引。 为了更新索引,原始索引定义必须包含设计文档ddoc属性和索引名称。 要更新索引定义,请使用相同的索引名称,但要更改索引定义。 只需编辑索引JSON文件,然后从索引中添加或删除字段即可。 Fabric仅支持索引类型JSON。 不支持更改索引类型。 当链码定义提交到通道后,更新的索引定义将重新部署到peer的状态数据库中。 更改索引名称或ddoc属性将导致创建新索引,并且原始索引在CouchDB中将保持不变,直到将其删除。
注意:
如果状态数据库中有大量数据,则重建索引将花费一些时间,在此期间,链码调用会导致查询失败或超时。
迭代索引定义
如果您可以在开发环境中访问peer的CouchDB状态数据库,则可以迭代地测试各种索引以支持链码查询。 但是,对链码的任何更改都需要重新部署。 使用CouchDB Fauxton界面或命令行curl实用程序来创建和更新索引。
注意:
Fauxton界面是一个Web UI,用于创建索引,更新索引以及将索引部署到CouchDB。 如果要尝试使用此界面,可以在Assets示例中找到索引的Fauxton版本的格式示例。 如果您已经使用CouchDB部署了测试网络,则可以通过打开浏览器并导航至http来加载Fauxton界面: http://localhost:5984/_utils
.
另外,如果您不喜欢使用Fauxton UI,下面是curl命令的示例,可用于在数据库mychannel_ledger上创建索引:
// Index for docType, owner.
// Example curl command line to define index in the CouchDB channel_chaincode database
curl -i -X POST -H "Content-Type: application/json" -d
"{\"index\":{\"fields\":[\"docType\",\"owner\"]},
\"name\":\"indexOwner\",
\"ddoc\":\"indexOwnerDoc\",
\"type\":\"json\"}" http://hostname:port/mychannel_ledger/_index
注意:
如果使用的是通过CouchDB配置的测试网络,则将hostname:port替换为localhost:5984。
删除索引
索引删除不是由Fabric工具管理的。 如果需要删除索引,请对数据库手动执行curl命令或使用Fauxton界面删除它。 curl命令用于删除索引的格式为:
curl -X DELETE http://localhost:5984/{database_name}/_index/{design_doc}/json/{index_name} -H "accept: */*" -H "Host: localhost:5984"
要删除本教程中使用的索引,curl命令将是:
curl -X DELETE http://localhost:5984/mychannel_ledger/_index/indexOwnerDoc/json/indexOwner -H "accept: */*" -H "Host: