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 聽風 閱讀(651) 評論(0)  編輯  收藏 所屬分類: 區塊鏈


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 金华市| 绥中县| 肃北| 白朗县| 霍州市| 任丘市| 唐海县| 思南县| 河东区| 纳雍县| 根河市| 剑河县| 永宁县| 锦州市| 安福县| 京山县| 彭山县| 大邑县| 青川县| 思南县| 孝感市| 锦屏县| 金沙县| 巫溪县| 泰宁县| 上杭县| 丰镇市| 华宁县| 遂溪县| 始兴县| 开原市| 隆昌县| 高碑店市| 商南县| 宜丰县| 梧州市| 崇礼县| 衢州市| 武汉市| 拉孜县| 嫩江县|