1. 程式人生 > >Hyperledger Fabric &CouchDB 查詢

Hyperledger Fabric &CouchDB 查詢

Hyperledger Fabric(HLF) 使用一個KV資料庫儲存它的狀態。這個物件儲存包含可以使用它的鍵查詢的二進位制資料。fabric 預設使用LevelDB儲存,它包含在 peer 程序中。

當簡單地在你的鏈碼中使用簡單的結構體,你很可能只需要通過他的鍵來查詢資料。但是,如果你有更復雜的資料,無法通過特定的欄位來獲得資料。但是這有一個解決方案(在某此場合)!

CouchDB

CouchDB… 放鬆

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"` }

簡單查詢

例如:

  • 搜尋 tom 擁有的 marbles 儲存
{
    "selector": {
        "ownername": "tom"
    }
}
  • 查詢類似的子文件
{
    "selector": {
        "owner.lastname": "tom"
    }
}
  • 查詢大小大於 1 的 marbles

和 MongoDB一樣,你可以使用 $gt,$lt,eq … 完成的列表在這兒

{
    "selector": {
        "employees": {
            "$gt": 1
        }
    }
}

高階查詢

例如:

  • 在儲存中查詢指定顏色的 marbles
{
    "selector": {
        "marbles": {
            "$elemMatch": {
                "color": "red"
            }
        }
    }
}
  • 在儲存中查詢一系列 marble 顏色
{
    "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將返回一個迭代器。如果它有任何條目,它將返回一個看上去像下面這樣的結構體,KeyValue是顯而易見的,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

28/12 更新

  • Skiplimit 由於效能原因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