Fabric链码开发——富查询

常见的链码使用PutState和GetState就可以进行简单的增加和查找了,再不济就加入DelState。因为fabric底层都是KV数据库,所以这三个接口就可以进行增删改查的基本需求了。但是,这都是单个数据的操作如果要进行功能较为强大的数据操作,只使用这个是完全不够的。查询时一切数据操作的开始,通过官方文档,找到了这两个支持富查询的函数。(富查询只支持在CouchDB 中使用)

源代码

func (s *ChaincodeStub) GetQueryResult(query string) (StateQueryIteratorInterface, error)
func (s *ChaincodeStub) GetQueryResultWithPagination(query string, pageSize int32,bookmark string) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error)

第一个是一个简单的富查询函数,第二个是一个支持富查询的分页查询函数

// GetQueryResult documentation can be found in interfaces.go
func (s *ChaincodeStub) GetQueryResult(query string) (StateQueryIteratorInterface, error) {// Access public data by setting the collection to empty stringcollection := ""// ignore QueryResponseMetadata as it is not applicable for a rich query without paginationiterator, _, err := s.handleGetQueryResult(collection, query, nil)return iterator, err
}
// GetQueryResultWithPagination ...
func (s *ChaincodeStub) GetQueryResultWithPagination(query string, pageSize int32,bookmark string) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) {// Access public data by setting the collection to empty stringcollection := ""metadata, err := createQueryMetadata(pageSize, bookmark)if err != nil {return nil, nil, err}return s.handleGetQueryResult(collection, query, metadata)
}

以上是他们的源码,实际上可以发现,他们都是调用handleGetQueryResult这个函数,分页查询函数知识多了一步创建metadata,将范围打包。之后可以和GetStateByRange(startKey, endKey string) 的函数做比较。

bookmark是书签,内容是一个无序的字符数组,如果为空则会重头查询,如果不为空,则会从会从它所代表的位置开始查询,在每次分页查询的时候都可以得到一个bookmark,也就是说,下一页的查询需要带上一页的bookmark。

func (s *ChaincodeStub) handleGetQueryResult(collection, query string,metadata []byte) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) {//获取查询结果,这一步就使用了metadata进行分页response, err := s.handler.handleGetQueryResult(collection, query, metadata, s.ChannelID, s.TxID)if err != nil {return nil, nil, err}//创建迭代器iterator := s.createStateQueryIterator(response)//进行再次封装responseMetadata, err := createQueryResponseMetadata(response.Metadata)if err != nil {return nil, nil, err}return iterator, responseMetadata, nil
}

和这两个富查询函数一起使用handleGetQueryResult的,还有一个GetPrivateDataQueryResult函数。这个函数看起来是查询私有数据,但私有数据是什么暂且不知道,按下不提。这四个函数都是在shim/stub.go中。

索引及数据结构

富查询目前只有couchDB支持,这是一个比较依赖数据库的功能,那就必须要有考虑一个问题:索引。参考Fabric的中文文档可以建立索引。

特别注意两点:

1.索引是依据需求来的,如果太多的索引会影响查询速率

2.索引的目录是固定的,链码同目录下的META-INF/statedb/couchdb/indexes

一个完整的索引如下:

{"index":{//要查询的字段名称,一般情况下要包含docType,对于docType的解释在后面"fields":["docType","owner"] // Names of the fields to be queried},// (可选)将创建索引的设计文件的名称。如果是一个文件包含一个索引可以不使用,官方建议也是单个索引"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"
}

我在官方文档中还看到了一个数据结构的规范写法

type marble struct {//docType用于区分状态数据库中的各种类型的对象ObjectType string `json:"docType"` //以下的正常的数据结构Name       string `json:"name"` Color      string `json:"color"`Size       int    `json:"size"`Owner      string `json:"owner"`
}

docType 用来识别这个文档的数据类型。 同时在链码数据库中也可能存在其他文档。数据库中的文档对于这些属性值来说都是可查询的。

常用函数

以下是我根据文档中给出的示例总结的富查询常用函数,主要使用getQueryResultForQueryStringgetQueryResultForQueryStringWithPagination,后两个函数是为分页富查询函数使用的。可以写在智能合约中调用。

=========================================================================================
// getQueryResultForQueryString执行传入的查询字符串。
// 结果集被建立并作为一个字节数组返回,其中包含JSON结果。
=========================================================================================
func getQueryResultForQueryString(APIstub shim.ChaincodeStubInterface, queryString string) ([]byte, error) {fmt.Printf("- getQueryResultForQueryString queryString:\n%s\n", queryString)resultsIterator, err := APIstub.GetQueryResult(queryString)if err != nil {return nil, err}defer resultsIterator.Close()buffer, err := constructQueryResponseFromIterator(resultsIterator)if err != nil {return nil, err}fmt.Printf("- getQueryResultForQueryString queryResult:\n%s\n", buffer.String())return buffer.Bytes(), nil
}=========================================================================================
// getQueryResultForQueryStringWithPagination执行传入的查询字符串,其中包括
// 分页信息。结果集被建立并作为一个字节数组返回,其中包含JSON结果。
=========================================================================================
func getQueryResultForQueryStringWithPagination(APIstub shim.ChaincodeStubInterface, queryString string, pageSize int32, bookmark string) ([]byte, error) {fmt.Printf("- getQueryResultForQueryStringWithPagination queryString:\n%s\n", queryString)resultsIterator, responseMetadata, err := APIstub.GetQueryResultWithPagination(queryString, pageSize, bookmark)if err != nil {return nil, err}defer resultsIterator.Close()buffer, err := constructQueryResponseFromIterator(resultsIterator)if err != nil {return nil, err}bufferWithPaginationInfo := addPaginationMetadataToQueryResults(buffer, responseMetadata)fmt.Printf("- getQueryResultForQueryStringWithPagination queryResult:\n%s\n", bufferWithPaginationInfo.String())return buffer.Bytes(), nil
}
=========================================================================================
// constructQueryResponseFromIterator构建一个JSON数组,其中包含来自给定结果迭代器的查询结果。
// 一个给定的结果迭代器=========================================================================================
func constructQueryResponseFromIterator(resultsIterator shim.StateQueryIteratorInterface) (*bytes.Buffer, error) {// buffer is a JSON array containing QueryResultsvar buffer bytes.Bufferbuffer.WriteString("[")bArrayMemberAlreadyWritten := falsefor resultsIterator.HasNext() {queryResponse, err := resultsIterator.Next()if err != nil {return nil, err}// Add a comma before array members, suppress it for the first array memberif bArrayMemberAlreadyWritten == true {buffer.WriteString(",")}buffer.WriteString("{\"Key\":")buffer.WriteString("\"")buffer.WriteString(queryResponse.Key)buffer.WriteString("\"")buffer.WriteString(", \"Record\":")// Record is a JSON object, so we write as-isbuffer.WriteString(string(queryResponse.Value))buffer.WriteString("}")bArrayMemberAlreadyWritten = true}buffer.WriteString("]")return &buffer, nil
}=========================================================================================
// addPaginationMetadataToQueryResults 将包含分页的QueryResponseMetadata
// 的信息,添加到构建的查询结果中。
=========================================================================================
func addPaginationMetadataToQueryResults(buffer *bytes.Buffer, responseMetadata *pb.QueryResponseMetadata) *bytes.Buffer {buffer.WriteString("[{\"ResponseMetadata\":{\"RecordsCount\":")buffer.WriteString("\"")buffer.WriteString(fmt.Sprintf("%v", responseMetadata.FetchedRecordsCount))buffer.WriteString("\"")buffer.WriteString(", \"Bookmark\":")buffer.WriteString("\"")buffer.WriteString(responseMetadata.Bookmark)buffer.WriteString("\"}}]")return buffer
}

下面是我写的一个调用分页函数的合约函数(我把可以在SDK中调用的智能合约函数叫做合约函数)示例

func (s *SmartContract) queryCourseWithPagination(APIstub shim.ChaincodeStubInterface, args []string) pb.Response {fmt.Println("func queryCourseWithPagination start")fmt.Printf("args:%v\n", args)if len(args) != 3 {return shim.Error("arguments num not enough," + strconv.Itoa(len(args)))}queryString := args[0]//return type of ParseInt is int64pageSize, err := strconv.ParseInt(args[1], 10, 32)if err != nil {return shim.Error(err.Error())}bookmark := args[2]//之前是获取参数,在这一步调用分页富查询函数,查询出结果后转为字节流返回queryResults, err := getQueryResultForQueryStringWithPagination(APIstub, queryString, int32(pageSize), bookmark)if err != nil {return shim.Error(err.Error())}return shim.Success(queryResults)
}

chaincode常用包shim链接

Fabric链码开发——富查询相关推荐

  1. HyperLedger Fabric链码开发及测试

    HyperLedger Fabric链码开发及测试 1.链码开发 先设计一个简单的应用场景,假设有这样的业务需求: 可以添加学校,信息包括学校名称.学校ID: 添加该学校的学生,信息包括姓名,用户ID ...

  2. HyperLeger Fabric开发(七)——HyperLeger Fabric链码开发

    HyperLeger Fabric开发(七)--HyperLeger Fabric链码开发 一.链码开发模式 1.链码开发模式简介 Fabric的链码开发调试比较繁琐.在不使用链码开发模式的情况下,链 ...

  3. fabric 启动peer_编写 Fabric 链码的一般准则

    我相信智能合约(链码)是 Hyperledger Fabric 区块链网络的核心.正确开发链码可以真正发挥一个安全区块链的优势,反之则会带来灾难性的后果.在这篇文章里我不打算探讨 Hyperledge ...

  4. 十一、区块链学习-Hyperledger Fabric (基于release-1.0) 链码开发-marbles管理

    链码开发-marbles管理 1. 概述 2. marble弹珠管理 2.1实现功能 2.2chaincode链码 2.3编写测试类 2.4 跑测试类 3 搭建本地测试环境 并测试链码 3.1 挂载链 ...

  5. hyperledger fabric 实战开发——水产品溯源交易平台(二)

    文章目录 前言 一.技术学习 1.Hyperledger fabric 1.1 流程 1.2 配置 1.3 范例解析并自写 1.3 算法实现 二.Web编写 前言 hyperledger fabric ...

  6. Fabric 链码Chaincode 的安装、初始化、调用、升级

    Fabric 链码Chaincode 的安装.初始化.调用.升级 Fabric chaincode 上一篇文章,我们启动了一个Fabric网络,这篇文章来看看在Fabric网络进行应用的开发. 上一篇 ...

  7. Fabric链码常用API文档

    Fabric链码API文档 一.Fabric-shim.ChaincodeInterface ChaincodeInterface 在链码当中我们必须实现ChaincodeInterface接口中定义 ...

  8. 智能合约链码开发和部署

    智能合约链码开发和部署 文章目录 智能合约链码开发和部署 一.环境和语言 二.便签版合约实现 2.1.定义链码对象 2.2.便签对象实现 2.3.Init接口的实现 2.4.invoke接口实现和便签 ...

  9. Hyperledger Fabric 链码(3) 生命周期和API

    1. Chaincode的5个生命周期命令 链码打包 链码安装 eg.peer chaincode install ccpack.out 链码实例化 eg. peer.sh chaincode ins ...

最新文章

  1. 参数定义sql 递归查询子目录
  2. ubuntu软件的卸载和安装
  3. F - Heron and His Triangle UVALive - 8206
  4. 提高.NET编程水平的50个要点(转载)
  5. tomcat session失效时间
  6. 在你迷茫时不如学好一门语言(送给大一的学弟学妹)
  7. 交易撮合引擎原理与实现【含源码】
  8. 蓝桥杯真题:平方和(2019 年省赛)
  9. 用C语言调用.bat批处理命令
  10. null object java_java1.8--Null Object模式
  11. 如何录制网络视频,屏幕录制软件哪个好
  12. 如何打开计算机用户账户控制面板,控制面板无法打开用户帐户
  13. marked扩展语法(增加自定义表情)
  14. python数独解题器_Python编写的超帅数独可视化解题器
  15. metamask连接不上本地私有节点,报错“无法获取链 IC,您的 RPC URL 地址是正确的么”
  16. 安卓下快速搜索文件实现历程{NDK}
  17. VS2017实用调试技巧
  18. 真王服务器文件,《真王》全国争霸赛开赛 3V3跨服登场
  19. 【解的封闭形式】Abel-Ruffini theorem(阿贝尔-鲁菲尼定理)
  20. mac系统常用操作指南

热门文章

  1. DIT-FFT算法的python实现
  2. 路由器的三种分组交换方式
  3. 移动开发工具包Mobile Toolkit Volum 2发布
  4. Silverlight 3.0中文教程
  5. 数字经济时代下,企业税务管理数字化转型如何做?
  6. SSL证书是如何保证网站安全的
  7. React入门小册(三)组件
  8. win10自带输入法在切换中英文的过程中,出现了输入字符所占间距变大的问题
  9. 第一章 计算机基础知识随堂练习,计算机应用基础随堂练习全解
  10. AndroidQ设置中的“运营商视频通话”功能如何默认关闭