Hyperledger織物のページング例


この記事では、以前に作成したHyperledger Fabric-TotalQueryLimitの記事で述べたページングを含む例を作成してみます.
https://github.com/hyperledger/fabric-samples/blob/release-1.4/chaincode/fabcar/go/fabcar.go
例fabcarを織物−試料に用いた.
// 기존 코드
func (s *SmartContract) queryAllCars(APIstub shim.ChaincodeStubInterface) sc.Response {

	startKey := "CAR0"
	endKey := "CAR999"

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

	// 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 shim.Error(err.Error())
		}
		// 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("- queryAllCars:\n%s\n", buffer.String())

	return shim.Success(buffer.Bytes())
}
ページングを適用するポイントは、getStateByRange()またはGetStateByPartialCompositeKey()です.
この文書はfabcarのgetStateByRange()に適用されます.
func (stub *ChaincodeStub) GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error) 

func (stub *ChaincodeStub) GetStateByRangeWithPagination(startKey, endKey string, pageSize int32,
	bookmark string) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error)
まず、2つの関数のパラメータと戻り値をチェックします.
StartKeyはEndKeyと同じですが、ページングにはpageSizeとbookmarkが必要です.
戻り値にも差があり、ページングはpbです.さらにQueryResponseMetadataを返します.
type QueryResponseMetadata struct {
	FetchedRecordsCount  int32    `protobuf:"varint,1,opt,name=fetched_records_count,json=fetchedRecordsCount,proto3" json:"fetched_records_count,omitempty"`
	Bookmark             string   `protobuf:"bytes,2,opt,name=bookmark,proto3" json:"bookmark,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}
func (m *QueryResponseMetadata) GetFetchedRecordsCount() int32 {
	if m != nil {
		return m.FetchedRecordsCount
	}
	return 0
}

func (m *QueryResponseMetadata) GetBookmark() string {
	if m != nil {
		return m.Bookmark
	}
	return ""
}
Paginationのように、次のリクエストからどのデータを受信し、bookmarkを戻り値に変換します.
このように運用する.
// first query ( bookmark = "" ) 
Iterator1, meta1, err := GetStateByRangeWithPaination(startKey, endKey, int32(5), "")
if err != nil {
	return err
}

nextBookmark := meta1.GetBookmark()

Iterator2, meta2, err := GetStateByRangeWithPagination(startKey, endKey, int32(5), nextBookmark)
if err != nil {
	return err
}

nextBoomark = meta2.GetBookmark()

.
.
.
GetStateByRangeWithPaginationについてある程度理解しましたが、Fabcarに適用してみましょう.
func (s *SmartContract) queryAllCarsWithPagination(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {

	//PageSize int32, Bookmark String
    	tempInt := strconv.Atoi(args[1])
        
    	PageSize := int32(tempInt)
    	Bookmark := args[2]

	startKey := "CAR0"
	endKey := "CAR999"

	resultsIterator, meta, err := APIstub.GetStateByRangeWithPagination(startKey, endKey, PageSize, Bookmark)
	if err != nil {
		return shim.Error(err.Error())
	}
	defer resultsIterator.Close()

	// 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 shim.Error(err.Error())
		}
		// 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("{\"nextBookmark\":")
		buffer.WriteString("\"")
        	buffer.WriteString(meta.GetBookmark())
		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("- queryAllCarsWithPagination:\n%s\n", buffer.String())

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

// 마지막 page인 경우나, 잘못된 PageSzie, Bookmark 등 에러 제어는 생략
Hyperleger Fabricを学習してチェーンコードを記述すると、多くのFabric-samplesが見つかりますが、例にない他の機能を実装しようとすると、資料が少なすぎて気まずい思いをします.
この場合,hyperleger/fabric githubに入り,コードの内部動作を確認し,開発を行うのが最善の方法である.