用過Fabric的都知道,在Fabric中的狀態數據庫提供了Couchdb和Leveldb兩種實現,一般我們都會使用Couchdb作為狀態數據庫的默認實現,主要是因為Couchdb可以進行富查詢。但是在某些情況下我們只能采用Leveldb的情況下,我們無法使用富查詢,那么怎么辦?我們可以利用Leveldb適合前綴查詢的特點進行前綴查詢,而且由於Leveldb底層結構的特點,進行前綴查詢的效率是特別高的。
要進行前綴查詢,那么我們在PutState的時候要合理設計前綴值,從而能夠利用前綴查詢。以一個會議簽到存證系統為例,我們在Fabric的鏈碼中設計了兩個對象Meeting和CheckinLog。
會議對象Meeting的存證我們設計Key為:“Meeting_”+會議ID,然后PutState將會議的JSON存入Fabric中。
簽到記錄對象CheckinLog的存證,我們設計Key為:”Checkin_”+會議ID+”_”+用戶ID,然后調用PutState將CheckinLog這個對象的Json作為Value存入到Fabric中。
接下來是對某個會議的簽到記錄的查詢了。這么我們知道會議ID的情況下,查詢簽到記錄返回的是一個集合,那么我們可以基於stub.GetStateByRange接口來進行查詢,該操作的核心就是要構造其兩個參數startKey和endKey。以下是代碼實現,主要用到了BytesPrefix函數用於計算endKey,該函數是在github.com/syndtr/goleveldb/leveldb/util有的,我們直接摘抄出來即可。
//根據會議ID,返回本次會議的簽到記錄列表 func QueryCheckinLogsByMeetingId(stub shim.ChaincodeStubInterface, meetingId int) ([]*CheckinLog, error) { startKey:=fmt.Sprintf( "Checkin_%d_",meetingId) endKey:=string(BytesPrefix([]byte(startKey))) resultsIterator, err:= stub.GetStateByRange(startKey,endKey) defer resultsIterator.Close() if err != nil { return nil, err } result:=[]*CheckinLog{} for resultsIterator.HasNext() { queryResponse,err:= resultsIterator.Next() if err != nil { return nil, err } row:=&CheckinLog{} json.Unmarshal(queryResponse.Value,row) result=append(result,row) } return result, nil } //計算GetStateByRange時的endKey,該函數摘自:github.com/syndtr/goleveldb/leveldb/util func BytesPrefix(prefix []byte) []byte { var limit []byte for i := len(prefix) - 1; i >= 0; i-- { c := prefix[i] if c < 0xff { limit = make([]byte, i+1) copy(limit, prefix) limit[i] = c + 1 break } } return limit }
就是這樣的邏輯,我們就可以在Fabric鏈碼中通過前綴進行批量查詢。