hello world

随笔 - 2, 文章 - 63, 评论 - 0, 引用 - 0
数据加载中……

fabric CouchDB使用-marbles链码码翻译

源码

关键代码梳理
  1. 创建索引
    //  ==== 创建颜色的索引 ====
        
    //  索引是一个状态数据库中的正常键值对
        
    //  该键是一个复合键,其中首先列出了要进行范围查询的元素。(本例中是颜色,需要把颜色放在首位,结构是:indexName~color~name)
        
    //  这将使基于匹配indexName~.~*的复合键的非常有效的状态范围查询成为可能。
        
    //  该处代码是创建的地方,使用的地方详见: transferMarblesBasedOnColor  方法
        indexName := "color~name"
        colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{marble.Color, marble.Name})
        if err != nil {
            return shim.Error(err.Error())
        }

        // 将索引项保存到状态。只需要索引的key,不需要存储大理石的marble
        
    // 注意-传递“nil”值将有效地从state中删除密钥,因此我们将空字符作为值传递
        value := []byte{0x00}
        stub.PutState(colorNameIndexKey, value)
  2. 删除索引
    // maintain the index
        indexName := "color~name"
        colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{marbleJSON.Color, marbleJSON.Name})
        if err != nil {
            return shim.Error(err.Error())
        }

        //  在状态中删除索引的记录项
        err = stub.DelState(colorNameIndexKey)
        if err != nil {
            return shim.Error("Failed to delete state:" + err.Error())
        }
  3. 索引查询
    Note that we don't get the value (2nd return variable), we'll just get the marble name from the composite key
    GetStateByPartialCompositeKey不会返回对象的全部数据,只会返回索引key和
    // Query the color~name index by color
        
    // This will execute a key range query on all keys starting with 'color'
        coloredMarbleResultsIterator, err := stub.GetStateByPartialCompositeKey("color~name", []string{color})
        if err != nil {
            return shim.Error(err.Error())
        }
        defer coloredMarbleResultsIterator.Close()


翻译后的源码
/*
 SPDX-License-Identifier: Apache-2.0
*/

// ====CHAINCODE EXECUTION SAMPLES (CLI) ==================

// ==== Invoke marbles ====
// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["initMarble","marble1","blue","35","tom"]}'
// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["initMarble","marble2","red","50","tom"]}'
// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["initMarble","marble3","blue","70","tom"]}'
// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["transferMarble","marble2","jerry"]}'
// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["transferMarblesBasedOnColor","blue","jerry"]}'
// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["delete","marble1"]}'

// ==== Query marbles ====
// peer chaincode query -C myc1 -n marbles -c '{"Args":["readMarble","marble1"]}'
// peer chaincode query -C myc1 -n marbles -c '{"Args":["getMarblesByRange","marble1","marble3"]}'
// peer chaincode query -C myc1 -n marbles -c '{"Args":["getHistoryForMarble","marble1"]}'

// 富查询 (需要 CouchDB 状态数据库):
// peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarblesByOwner","tom"]}'
// peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"owner\":\"tom\"}}"]}'

// 具有分页的富查询 (需要 CouchDB 状态数据库):
// peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarblesWithPagination","{\"selector\":{\"owner\":\"tom\"}}","3",""]}'

// 支持富查询的索引
//
// 为了使JSON查询高效,CouchDB中需要索引,在进行具有排序的JSON查询时也是需要索引的。
// 对于Hyperledger Fabric 1.1,索引可以放在: META-INF/statedb/couchdb/index目录中。
// 每个索引必须以自己的方式定义, 扩展名为*.json的文本文件,索引定义格式为JSON,如下所示
// CouchDB索引JSON语法,如下所示:
// http://docs.couchdb.org/en/2.1.1/api/database/find.html#db-index
//
// marbles02 例子展示了索引的使用
// 索引文件定义在 META-INF/statedb/couchdb/indexes/indexOwner.json.
// 对于部署在生产环境上的链码,建议在将索引的定义和链码放在一起,这样安装链码以及在通道上初始化时就能
// 作为一个整体自动建立索引。
// 有关更多详细信息,请参阅Hyperledger Fabric文档。
//
// 通过CouchDB Fauxton接口(CouchDB Fauxton interface)或者curl命令行工具(a command line curl utility)可以创建或者修改索引。
// 这样就可以在生产环境中有访问权限的CouchDB节点上,不断尝试修改各种索引以支持链码的查询操作。
// 通过这样更新的方式,也会更新到META-INF/statedb/couchdb/indexes中,支持整体打包和部署。
//
// 在下面的示例中,您可以找到支持marbles02链码查询的索引定义,以及可以在开发环境中用于在CouchDB Fauxton接口或
// curl命令行实用程序中创建索引的语法。

// 例子中 hostname:port 的值在具体执行时需要替换一下,根据所处环境可能有如下两种情况:
// 1、从另外一个docker容器或者其他独立环境访问CouchDB docker容器时:
// http://couchdb:5984/
//
// 2、从同一个CouchDB docker容器访问时:
// http://127.0.0.1:5984/

// 索引 docType, owner.
//
// 定义索引
// curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[\"docType\",\"owner\"]},\"name\":\"indexOwner\",\"ddoc\":\"indexOwnerDoc\",\"type\":\"json\"}" http://hostname:port/myc1_marbles/_index
//

// 索引 docType, owner, size (descending order).
//
// 定义索引
// curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[{\"size\":\"desc\"},{\"docType\":\"desc\"},{\"owner\":\"desc\"}]},\"ddoc\":\"indexSizeSortDoc\", \"name\":\"indexSizeSortDesc\",\"type\":\"json\"}" http://hostname:port/myc1_marbles/_index

// 指定了索引文档名和索引名的 富查询:
//   peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"docType\":\"marble\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}'

// 只指定索引文档名 富查询::
//   peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"docType\":{\"$eq\":\"marble\"},\"owner\":{\"$eq\":\"tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}'

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "strconv"
    "strings"
    "time"

    "github.com/hyperledger/fabric/core/chaincode/shim"
    pb "github.com/hyperledger/fabric/protos/peer"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

type marble struct {
    ObjectType string `json:"docType"` // docType用于区分状态数据库中的各种对象类型
    Name       string `json:"name"`    //the fieldtags are needed to keep case from bouncing around
    Color      string `json:"color"`
    Size       int    `json:"size"`
    Owner      string `json:"owner"`
}

// ===================================================================================
// Main
// ===================================================================================
func main() {
    err := shim.Start(new(SimpleChaincode))
    if err != nil {
        fmt.Printf("Error starting Simple chaincode: %s", err)
    }
}

// Init初始化链码
// ===========================
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
    return shim.Success(nil)
}

// Invoke - 调用的入口点
// ========================================
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
    function, args := stub.GetFunctionAndParameters()
    fmt.Println("invoke is running " + function)

    // Handle different functions
    if function == "initMarble" { // 创建一个新的 marble
        return t.initMarble(stub, args)
    } else if function == "transferMarble" { // 改变一个 marble 的 owner
        return t.transferMarble(stub, args)
    } else if function == "transferMarblesBasedOnColor" { //转移所有某种颜色的 marble
        return t.transferMarblesBasedOnColor(stub, args)
    } else if function == "delete" { //删除一个 marble
        return t.delete(stub, args)
    } else if function == "readMarble" { //读取一个 marble
        return t.readMarble(stub, args)
    } else if function == "queryMarblesByOwner" { // 通过 owner 查询 marbles (富查询)
        return t.queryMarblesByOwner(stub, args)
    } else if function == "queryMarbles" { //查询 marbles (参数是 couchdb的语句)
        return t.queryMarbles(stub, args)
    } else if function == "getHistoryForMarble" { //获取一个 marble 的历史纪录
        return t.getHistoryForMarble(stub, args)
    } else if function == "getMarblesByRange" { //范围查询 marbles
        return t.getMarblesByRange(stub, args)
    } else if function == "getMarblesByRangeWithPagination" { // 分页范围查询
        return t.getMarblesByRangeWithPagination(stub, args)
    } else if function == "queryMarblesWithPagination" { // 分页查询
        return t.queryMarblesWithPagination(stub, args)
    }

    fmt.Println("invoke did not find func: " + function) //error
    return shim.Error("Received unknown function invocation")
}

// ============================================================
// initMarble - 创建一个新的 marble, 保存到链码的世界状态中
// ============================================================
func (t *SimpleChaincode) initMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    var err error

    //   0       1       2     3
    
// "asdf", "blue", "35", "bob"
    if len(args) != 4 {
        return shim.Error("Incorrect number of arguments. Expecting 4")
    }

    // ==== Input sanitation ====
    fmt.Println("- start init marble")
    if len(args[0]) <= 0 {
        return shim.Error("1st argument must be a non-empty string")
    }
    if len(args[1]) <= 0 {
        return shim.Error("2nd argument must be a non-empty string")
    }
    if len(args[2]) <= 0 {
        return shim.Error("3rd argument must be a non-empty string")
    }
    if len(args[3]) <= 0 {
        return shim.Error("4th argument must be a non-empty string")
    }
    marbleName := args[0]
    color := strings.ToLower(args[1])
    owner := strings.ToLower(args[3])
    size, err := strconv.Atoi(args[2])
    if err != nil {
        return shim.Error("3rd argument must be a numeric string")
    }

    // ==== 检查 marble 是否已存在 ====
    marbleAsBytes, err := stub.GetState(marbleName)
    if err != nil {
        return shim.Error("Failed to get marble: " + err.Error())
    } else if marbleAsBytes != nil {
        fmt.Println("This marble already exists: " + marbleName)
        return shim.Error("This marble already exists: " + marbleName)
    }

    // ==== 创建 marble 对象并序列化 JSON 格式====
    objectType := "marble"
    marble := &marble{objectType, marbleName, color, size, owner}
    marbleJSONasBytes, err := json.Marshal(marble)
    if err != nil {
        return shim.Error(err.Error())
    }
    // 如果不想使用结构体的json序列化,也可以手动构造json字符串
    
//marbleJSONasString := `{"docType":"Marble",  "name": "` + marbleName + `", "color": "` + color + `", "size": ` + strconv.Itoa(size) + `, "owner": "` + owner + `"}`
    
//marbleJSONasBytes := []byte(str)

    
// === 保存 marble 到世界状态中 ===
    err = stub.PutState(marbleName, marbleJSONasBytes)
    if err != nil {
        return shim.Error(err.Error())
    }

    //  ==== 创建颜色的索引 ====
    
//  索引是一个状态数据库中的正常键值对
    
//  该键是一个复合键,其中首先列出了要进行范围查询的元素。(本例中是颜色,需要把颜色放在首位,结构是:indexName~color~name)
    
//  这将使基于匹配indexName~.~*的复合键的非常有效的状态范围查询成为可能。
    
//  该处代码是创建的地方,使用的地方详见: transferMarblesBasedOnColor  方法
    indexName := "color~name"
    colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{marble.Color, marble.Name})
    if err != nil {
        return shim.Error(err.Error())
    }

    // 将索引项保存到状态。只需要索引的key,不需要存储大理石的marble
    
// 注意-传递“nil”值将有效地从state中删除密钥,因此我们将空字符作为值传递
    value := []byte{0x00}
    stub.PutState(colorNameIndexKey, value)

    // ==== Marble saved and indexed. Return success ====
    fmt.Println("- end init marble")
    return shim.Success(nil)
}

// ===============================================
// readMarble - read a marble from chaincode state
// ===============================================
func (t *SimpleChaincode) readMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    var name, jsonResp string
    var err error

    if len(args) != 1 {
        return shim.Error("Incorrect number of arguments. Expecting name of the marble to query")
    }

    name = args[0]
    valAsbytes, err := stub.GetState(name) //get the marble from chaincode state
    if err != nil {
        jsonResp = "{\"Error\":\"Failed to get state for " + name + "\"}"
        return shim.Error(jsonResp)
    } else if valAsbytes == nil {
        jsonResp = "{\"Error\":\"Marble does not exist: " + name + "\"}"
        return shim.Error(jsonResp)
    }

    return shim.Success(valAsbytes)
}

// ==================================================
// delete - remove a marble key/value pair from state
// ==================================================
func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    var jsonResp string
    var marbleJSON marble
    if len(args) != 1 {
        return shim.Error("Incorrect number of arguments. Expecting 1")
    }
    marbleName := args[0]

    // to maintain the color~name index, we need to read the marble first and get its color
    valAsbytes, err := stub.GetState(marbleName) //get the marble from chaincode state
    if err != nil {
        jsonResp = "{\"Error\":\"Failed to get state for " + marbleName + "\"}"
        return shim.Error(jsonResp)
    } else if valAsbytes == nil {
        jsonResp = "{\"Error\":\"Marble does not exist: " + marbleName + "\"}"
        return shim.Error(jsonResp)
    }

    err = json.Unmarshal([]byte(valAsbytes), &marbleJSON)
    if err != nil {
        jsonResp = "{\"Error\":\"Failed to decode JSON of: " + marbleName + "\"}"
        return shim.Error(jsonResp)
    }

    err = stub.DelState(marbleName) //remove the marble from chaincode state
    if err != nil {
        return shim.Error("Failed to delete state:" + err.Error())
    }

    // maintain the index
    indexName := "color~name"
    colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{marbleJSON.Color, marbleJSON.Name})
    if err != nil {
        return shim.Error(err.Error())
    }

    //  在状态中删除索引的记录项
    err = stub.DelState(colorNameIndexKey)
    if err != nil {
        return shim.Error("Failed to delete state:" + err.Error())
    }
    return shim.Success(nil)
}

// ===========================================================
// transfer a marble by setting a new owner name on the marble
// ===========================================================
func (t *SimpleChaincode) transferMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {

    //   0       1
    
// "name", "bob"
    if len(args) < 2 {
        return shim.Error("Incorrect number of arguments. Expecting 2")
    }

    marbleName := args[0]
    newOwner := strings.ToLower(args[1])
    fmt.Println("- start transferMarble ", marbleName, newOwner)

    marbleAsBytes, err := stub.GetState(marbleName)
    if err != nil {
        return shim.Error("Failed to get marble:" + err.Error())
    } else if marbleAsBytes == nil {
        return shim.Error("Marble does not exist")
    }

    marbleToTransfer := marble{}
    err = json.Unmarshal(marbleAsBytes, &marbleToTransfer) //unmarshal it aka JSON.parse()
    if err != nil {
        return shim.Error(err.Error())
    }
    marbleToTransfer.Owner = newOwner //change the owner

    marbleJSONasBytes, _ := json.Marshal(marbleToTransfer)
    err = stub.PutState(marbleName, marbleJSONasBytes) //rewrite the marble
    if err != nil {
        return shim.Error(err.Error())
    }

    fmt.Println("- end transferMarble (success)")
    return shim.Success(nil)
}

// ===========================================================================================
// 从一个给定的查询结果的迭代器构建一个JSON数组
// ===========================================================================================
func constructQueryResponseFromIterator(resultsIterator shim.StateQueryIteratorInterface) (*bytes.Buffer, error) {
    // buffer is a JSON array containing QueryResults
    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("]")

    return &buffer, nil
}

// ===========================================================================================
// addPaginationMetadataToQueryResults adds QueryResponseMetadata, which contains pagination
// info, to the constructed query results
// ===========================================================================================
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
}

// ===========================================================================================
// 使用开始键和结束键进行范围查询

// 只读方法的结果通常不会提交给 ordering. 如果为只读结果提交给排序,
// 或者如果查询用于更新事务并且提交给 ordering, 然后提交对等点将重新执行,以保证结果集在认可时间和提交时间之间是稳定的。
// 如果结果集在背书之间发生了更改,交易会无效
// 因此,范围查询是根据查询结果执行更新事务的安全选项。!!!!  区别于 GetQueryResult

// ===========================================================================================
func (t *SimpleChaincode) getMarblesByRange(stub shim.ChaincodeStubInterface, args []string) pb.Response {

    if len(args) < 2 {
        return shim.Error("Incorrect number of arguments. Expecting 2")
    }

    startKey := args[0]
    endKey := args[1]

    resultsIterator, err := stub.GetStateByRange(startKey, endKey)
    if err != nil {
        return shim.Error(err.Error())
    }
    defer resultsIterator.Close()

    buffer, err := constructQueryResponseFromIterator(resultsIterator)
    if err != nil {
        return shim.Error(err.Error())
    }

    fmt.Printf("- getMarblesByRange queryResult:\n%s\n", buffer.String())

    return shim.Success(buffer.Bytes())
}

// ==== Example: GetStateByPartialCompositeKey/RangeQuery =========================================
// transferMarblesBasedOnColor will transfer marbles of a given color to a certain new owner.
// 根据局部的复合键(前缀)返回所有匹配的键值 使用 color~name 'index'.
// 会重新验证,所以安全
// ===========================================================================================
func (t *SimpleChaincode) transferMarblesBasedOnColor(stub shim.ChaincodeStubInterface, args []string) pb.Response {

    //   0       1
    
// "color", "bob"
    if len(args) < 2 {
        return shim.Error("Incorrect number of arguments. Expecting 2")
    }

    color := args[0]
    newOwner := strings.ToLower(args[1])
    fmt.Println("- start transferMarblesBasedOnColor ", color, newOwner)

    // Query the color~name index by color
    
// This will execute a key range query on all keys starting with 'color'
    coloredMarbleResultsIterator, err := stub.GetStateByPartialCompositeKey("color~name", []string{color})
    if err != nil {
        return shim.Error(err.Error())
    }
    defer coloredMarbleResultsIterator.Close()

    // Iterate through result set and for each marble found, transfer to newOwner
    var i int
    for i = 0; coloredMarbleResultsIterator.HasNext(); i++ {
        // Note that we don't get the value (2nd return variable), we'll just get the marble name from the composite key
        responseRange, err := coloredMarbleResultsIterator.Next()
        if err != nil {
            return shim.Error(err.Error())
        }

        // get the color and name from color~name composite key
        objectType, compositeKeyParts, err := stub.SplitCompositeKey(responseRange.Key)
        if err != nil {
            return shim.Error(err.Error())
        }
        returnedColor := compositeKeyParts[0]
        returnedMarbleName := compositeKeyParts[1]
        fmt.Printf("- found a marble from index:%s color:%s name:%s\n", objectType, returnedColor, returnedMarbleName)

        // Now call the transfer function for the found marble.
        
// Re-use the same function that is used to transfer individual marbles
        response := t.transferMarble(stub, []string{returnedMarbleName, newOwner})
        // if the transfer failed break out of loop and return error
        if response.Status != shim.OK {
            return shim.Error("Transfer failed: " + response.Message)
        }
    }

    responsePayload := fmt.Sprintf("Transferred %d %s marbles to %s", i, color, newOwner)
    fmt.Println("- end transferMarblesBasedOnColor: " + responsePayload)
    return shim.Success([]byte(responsePayload))
}

// =======Rich queries =========================================================================
// Two examples of rich queries are provided below (parameterized query and ad hoc query).
// Rich queries pass a query string to the state database.
// Rich queries are only supported by state database implementations
//  that support rich query (e.g. CouchDB).
// The query string is in the syntax of the underlying state database.
// With rich queries there is no guarantee that the result set hasn't changed between
//  endorsement time and commit time, aka 'phantom reads'.
// Therefore, rich queries should not be used in update transactions, unless the
// application handles the possibility of result set changes between endorsement and commit time.
// Rich queries can be used for point-in-time queries against a peer.
// ============================================================================================

// ===== Example: Parameterized rich query =================================================
// queryMarblesByOwner queries for marbles based on a passed in owner.
// This is an example of a parameterized query where the query logic is baked into the chaincode,
// and accepting a single query parameter (owner).
// Only available on state databases that support rich query (e.g. CouchDB)
// =========================================================================================
func (t *SimpleChaincode) queryMarblesByOwner(stub shim.ChaincodeStubInterface, args []string) pb.Response {

    //   0
    
// "bob"
    if len(args) < 1 {
        return shim.Error("Incorrect number of arguments. Expecting 1")
    }

    owner := strings.ToLower(args[0])

    queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"marble\",\"owner\":\"%s\"}}", owner)

    queryResults, err := getQueryResultForQueryString(stub, queryString)
    if err != nil {
        return shim.Error(err.Error())
    }
    return shim.Success(queryResults)
}

// ===== Example: Ad hoc rich query ========================================================
// queryMarbles uses a query string to perform a query for marbles.
// Query string matching state database syntax is passed in and executed as is.
// Supports ad hoc queries that can be defined at runtime by the client.
// If this is not desired, follow the queryMarblesForOwner example for parameterized queries.
// Only available on state databases that support rich query (e.g. CouchDB)
// =========================================================================================
func (t *SimpleChaincode) queryMarbles(stub shim.ChaincodeStubInterface, args []string) pb.Response {

    //   0
    
// "queryString"
    if len(args) < 1 {
        return shim.Error("Incorrect number of arguments. Expecting 1")
    }

    queryString := args[0]

    queryResults, err := getQueryResultForQueryString(stub, queryString)
    if err != nil {
        return shim.Error(err.Error())
    }
    return shim.Success(queryResults)
}

// =========================================================================================
// getQueryResultForQueryString executes the passed in query string.
// Result set is built and returned as a byte array containing the JSON results.
// =========================================================================================
func getQueryResultForQueryString(stub shim.ChaincodeStubInterface, queryString string) ([]byte, error) {

    fmt.Printf("- getQueryResultForQueryString queryString:\n%s\n", queryString)

    resultsIterator, err := stub.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
}

// ====== 分页查询 =========================================================================
// 分页提供了一种方法来检索具有定义的页面大小和起点(书签)。空字符串书签定义查询的第一个“页面”结果。
// 分页查询返回可用于检索下一页结果的下一个查询。分页查询扩展包含页面大小和书签的丰富查询和范围查询。
// 本例中提供了两个示例。
// 第一个是getMarblesByRangeWithPagination它执行分页范围查询。
// 第二个例子是富查询的分页查询。
// =========================================================================================

// ====== 分页范围查询 ===============================================
// getMarblesByRangeWithPagination根据开始和结束键、页面大小和书签执行范围查询。
// 获取的记录数将等于或小于页面大小。分页范围查询仅对只读事务有效。
// ===========================================================================================
func (t *SimpleChaincode) getMarblesByRangeWithPagination(stub shim.ChaincodeStubInterface, args []string) pb.Response {

    if len(args) < 4 {
        return shim.Error("Incorrect number of arguments. Expecting 4")
    }

    startKey := args[0]
    endKey := args[1]
    //return type of ParseInt is int64
    pageSize, err := strconv.ParseInt(args[2], 10, 32)
    if err != nil {
        return shim.Error(err.Error())
    }
    bookmark := args[3]

    resultsIterator, responseMetadata, err := stub.GetStateByRangeWithPagination(startKey, endKey, int32(pageSize), bookmark)
    if err != nil {
        return shim.Error(err.Error())
    }
    defer resultsIterator.Close()

    buffer, err := constructQueryResponseFromIterator(resultsIterator)
    if err != nil {
        return shim.Error(err.Error())
    }

    bufferWithPaginationInfo := addPaginationMetadataToQueryResults(buffer, responseMetadata)

    fmt.Printf("- getMarblesByRange queryResult:\n%s\n", bufferWithPaginationInfo.String())

    return shim.Success(buffer.Bytes())
}

// ===== 分页富查询 ========================================================
// queryMarblesWithPagination使用查询字符串、页面大小和书签来执行marble查询。
// 查询字符串匹配状态数据库语法按原样传入和执行。获取的记录数将等于或小于指定的页大小。
// 支持可由客户端在运行时定义的特殊查询。
// 如果不需要,请按照queryMarblesForOwner示例进行参数化查询。
// 仅在支持富查询(例如CouchDB)分页查询的状态数据库上可用,仅对只读事务有效。
// =========================================================================================
func (t *SimpleChaincode) queryMarblesWithPagination(stub shim.ChaincodeStubInterface, args []string) pb.Response {

    //   0
    
// "queryString"
    if len(args) < 3 {
        return shim.Error("Incorrect number of arguments. Expecting 3")
    }

    queryString := args[0]
    //return type of ParseInt is int64
    pageSize, err := strconv.ParseInt(args[1], 10, 32)
    if err != nil {
        return shim.Error(err.Error())
    }
    bookmark := args[2]

    queryResults, err := getQueryResultForQueryStringWithPagination(stub, queryString, int32(pageSize), bookmark)
    if err != nil {
        return shim.Error(err.Error())
    }
    return shim.Success(queryResults)
}

// =========================================================================================
// getQueryResultForQueryStringWithPagination executes the passed in query string with
// pagination info. Result set is built and returned as a byte array containing the JSON results.
// =========================================================================================
func getQueryResultForQueryStringWithPagination(stub shim.ChaincodeStubInterface, queryString string, pageSize int32, bookmark string) ([]byte, error) {

    fmt.Printf("- getQueryResultForQueryString queryString:\n%s\n", queryString)

    resultsIterator, responseMetadata, err := stub.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("- getQueryResultForQueryString queryResult:\n%s\n", bufferWithPaginationInfo.String())

    return buffer.Bytes(), nil
}

func (t *SimpleChaincode) getHistoryForMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {

    if len(args) < 1 {
        return shim.Error("Incorrect number of arguments. Expecting 1")
    }

    marbleName := args[0]

    fmt.Printf("- start getHistoryForMarble: %s\n", marbleName)

    resultsIterator, err := stub.GetHistoryForKey(marbleName)
    if err != nil {
        return shim.Error(err.Error())
    }
    defer resultsIterator.Close()

    // buffer is a JSON array containing historic values for the marble
    var buffer bytes.Buffer
    buffer.WriteString("[")

    bArrayMemberAlreadyWritten := false
    for resultsIterator.HasNext() {
        response, err := resultsIterator.Next()
        if err != nil {
            return shim.Error(err.Error())
        }
        // Add a comma before array members, suppress it for the first array member
        if bArrayMemberAlreadyWritten == true {
            buffer.WriteString(",")
        }
        buffer.WriteString("{\"TxId\":")
        buffer.WriteString("\"")
        buffer.WriteString(response.TxId)
        buffer.WriteString("\"")

        buffer.WriteString(", \"Value\":")
        // if it was a delete operation on given key, then we need to set the
        
//corresponding value null. Else, we will write the response.Value
        
//as-is (as the Value itself a JSON marble)
        if response.IsDelete {
            buffer.WriteString("null")
        } else {
            buffer.WriteString(string(response.Value))
        }

        buffer.WriteString(", \"Timestamp\":")
        buffer.WriteString("\"")
        buffer.WriteString(time.Unix(response.Timestamp.Seconds, int64(response.Timestamp.Nanos)).String())
        buffer.WriteString("\"")

        buffer.WriteString(", \"IsDelete\":")
        buffer.WriteString("\"")
        buffer.WriteString(strconv.FormatBool(response.IsDelete))
        buffer.WriteString("\"")

        buffer.WriteString("}")
        bArrayMemberAlreadyWritten = true
    }
    buffer.WriteString("]")

    fmt.Printf("- getHistoryForMarble returning:\n%s\n", buffer.String())

    return shim.Success(buffer.Bytes())
}

posted on 2019-12-27 17:18 听风 阅读(614) 评论(0)  编辑  收藏 所属分类: 区块链


只有注册用户登录后才能发表评论。


网站导航: