Hyperledger Fabric(HLF) 使用一个KV数据库存储它的状态。这个对象存储包含可以使用它的键查询的二进制数据。fabric 默认使用LevelDB存储,它包含在 peer 进程中。
当简单地在你的链码中使用简单的结构体,你很可能只需要通过他的键来查询数据。但是,如果你有更复杂的数据,无法通过特定的字段来获得数据。但是这有一个解决方案(在某此场合)!
CouchDB是另一种类型的KV存储,它可以很容易在 fabric 中插件。LevelDB 和 CouchDB 都可以存储二进制数据并可以使用链码交互。但是CoudbDB也支持丰富的存储功能,当然,你需要存储JSON文档。*”first network"*例子工程也饮食一个CouchDB配置。可以在HLF wiki上找到该配置。
CouchDB感觉像一个拥有MongoDB查询能力的KV存储。虽然HLF 团队已经从 1.0-alpha 版本改善它们的文档,
但我似乎找不到更多关于这个丰富的查询如何工作的例子。经过搜索后,我发现唯一的例子是在 HLF 的GitHub上它本身的 marbles 链码简单查询,因此有这偏文章。
让我们使用 marbles 项目(添加一些额外的功能以使它更复杂)作为我们查询的例子。所有的查询公仅是被解析为字符串的 JSON,这可能通过使用在线工具完成。整个CouchDB查询文档可以在这儿找到。
最后将附有包含链码的链码完整实现
type marble struct {
Name string `json:"name"`
Color string `json:"color"`
Size int `json:"size"`
Owner string `json:"owner"`
}
type marbleStore struct {
ObjectType string `json:"docType"`
Storename string `json:"storename"`
Ownername string `json:"ownername"`
Owner owner `json:"owner"`
Employees int `json:"employees"`
Marbles []marble `json:"marbles"`
}
type owner struct {
Name string `json:"name"`
}
例如:
{
"selector": {
"ownername": "tom"
}
}
{
"selector": {
"owner.lastname": "tom"
}
}
和 MongoDB一样,你可以使用 $gt
,$lt
,eq
… 完成的列表在这儿
{
"selector": {
"employees": {
"$gt": 1
}
}
}
例如:
{
"selector": {
"marbles": {
"$elemMatch": {
"color": "red"
}
}
}
}
{
"selector": {
"marbles": {
"$elemMatch": {
"color": {
"$in": ["red", "green"]
}
}
}
}
}
除了 “selector” 属性以外,CouchDB 还有一些其他简洁的属性可以帮助你进行查询。
{
"selector": {
"ownername": "tom"
},
"limit": 5,
"skip": 1,
"sort": [{"owner": "desc"}, {"storename": "desc" }],
"fields": ["storename","marbles"]
}
limit: 和其他任何查询语言一样,你可以限制返回记录的条数
skip: 你可以跳过前面 x 条记录
sort: 你可以按 “asc" 或 ”desc" 顺序对每个字段进行排序
fields: 你可以过滤掉不必要或禁止的字段
func getQueryResultForQueryString(stub shim.ChaincodeStubInterface, queryString string)([] byte, error) {
fmt.Printf("- getQueryResultForQueryString queryString:\n%s\n", queryString)
resultsIterator, err: = stub.GetQueryResult(queryString)
defer resultsIterator.Close()
if err != nil {
return nil, err
}
// buffer is a JSON array containing QueryRecords
var buffer bytes.Buffer
buffer.WriteString("[")
bArrayMemberAlreadyWritten: = false
for resultsIterator.HasNext() {
queryResponse,
err: = resultsIterator.Next()
if err != nil {
return nil, err
}
// Add a comma before array members, suppress it for the first array member
if 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-is
buffer.WriteString(string(queryResponse.Value))
buffer.WriteString("}")
bArrayMemberAlreadyWritten = true
}
buffer.WriteString("]")
fmt.Printf("- getQueryResultForQueryString queryResult:\n%s\n", buffer.String())
return buffer.Bytes(), nil
}
由GitHub 托管的 getQueryResultForQueryString.go 查看原文
这个函数直接来自 marbles 示例。当你将上面的查询传递给它时,请确保你转义(使用工具做这些)变成一个字符串,所以它看起来像这样:
"{\"selector\":{\"owner\":\"tom\"}}"
函数GetQueryResult将返回一个迭代器。如果它有任何条目,它将返回一个看上去像下面这样的结构体,Key和Value是显而易见的,Namespace是它从中查询的通道。
type queryResponse struct {
Key string
Value string
Namespace string
}
所以,在结尾,这个函数将产生像这样json字段:
[{
"Key": "marblestore1",
"Record": {
"owner": "tom",
..., // the ... represent the other properties
"employees": 2
}
]
如果你需要更复杂的查询,这些丰富查询将真的有用。但是与GetStateByRange不 同,GetQueryResult查询查询在难阶段不会重新执行。这意味着,对于丰富查询,它并不确保结果集在链码执行和提交时间之间是稳定的,因此,丰富查询不适合在更新事务中使用,除非你的应用程序可以确保结果集在链码执行时间和提交时间之间是稳定的,或可以处理后续交易潜在的变化 。这是要考虑的事情。
为链码编写测试的重要说明:用于测试的 MockStub 并未实现 GetQueryResult 方法。所以你你必须做这些,否则你的测试将失败。
func (stub *MockStub) GetQueryResult(query string)(StateQueryIteratorInterface, error){
// Not implemented since the mock engine does not have a query engine.
// However, a very simple query engine that support string matching
// could be implemetned to test that the framework supports queries
return nil, errors.New("Not Implemented")
}
MockStub.go GetQueryResult function
Skip 和 limit 由于性能原因https://jira.hyperledger.org/browse/FAB-2809()被覆盖 – 感谢 Alex Lamb 指出这些
为了使用 sort,你必须手动地在你们的 CouchDB 实例中添加索引。例如,使用 curl 命令,但你也可以使用 CouchDB 接口。
// example curl definition for use with command line
curl -i -X POST -H “Content-Type: application/json” -d “{\”index\”:{\”fields\”:[{\”data.size\”:\”desc\”},{\”chaincodeid\”:\”desc\”},{\”data.docType\”:\”desc\”},{\”data.owner\”:\”desc\”}]},\”ddoc\”:\”indexSizeSortDoc\”, \”name\”:\”indexSizeSortDesc\”,\”type\”:\”json\”}” http://couchdbhostname:port/myc1/_index
CoucbDB 文档:http://docs.couchdb.org/en/2.1.0/api/database/find.html?highlight=find
Hyperledger Fabric & couchdb, fantastic queries and where to find them