通过值获取世界状态

huangapple go评论81阅读模式
英文:

Getting world state by value

问题

我是新手对于区块链和使用Hyperledger Fabric(目前版本为0.6)创建一个学习性质的应用程序。

我正在将金融交易记录在区块链上,一旦发生交易(基于Web的组件会通知交易发生并调用链码)。

交易的结构看起来像这样:

type Transactions struct { 
    ReferenceNumber string `json:"ReferenceNumber"`
    BillNumber string `json:"BillNumber"`
    BillingCompany string `json:"BillingCompany"`
    Amount string `json:"Amount"`
    Status string `json:"Status"`
}

我将其进行JSON编组,并使用ReferenceNumber作为键保存到状态中。

现在,我可以根据ReferenceNumber从状态中获取交易。但是,如果我想根据例如“Status”来获取状态中的交易,比如有多少笔交易的状态是“reconciled”,有没有办法查询状态而不是基于键而是基于值呢?

英文:

I'm new to blockchain and working with hyperledger fabric (v:0.6 for now) to create an application for learning perspective.

I'm keeping a ledger of financial transactions on blockchain, soon as a transaction takes place (A web based component notifies on transaction occurrence and calls the chaincode).

The structure of transactions looks something like this:

type Transactions struct { 
    ReferenceNumber string `json:"ReferenceNumber"`
    BillNumber string `json:"BillNumber"`
	BillingCompany string `json:"BillingCompany"`
    Amount string `json:"Amount"`
    Status string `json:"Status"`
}

I json marshal this and save it to state with ReferenceNumber as the key.

Now I can get the transaction from state on the basis of ReferenceNumber. But what if I want to get the transaction from state on the basis of let's say 'Status' like how many transactions on the ledger have status as 'reconciled'.

Is there any way to query state not on the basis of key but value?

答案1

得分: 2

世界状态级别的存储是在{键,值}级别上进行的。显然,它只适用于指定键的单个值查找。我认为你所寻找的是更高级别的抽象层次的世界状态 - 称为表构造。

fabric/examples/chaincode/go/asset_management_interactive/asset_management.go中有一个示例,展示了如何创建一个具有所需列的表。在定义用于保存交易的数据结构的主键时,将状态(Status)作为其中之一的键,并且你将能够根据状态检索数据

创建表的一些示例代码如下:

func createTableTwo(stub shim.ChaincodeStubInterface) error {
    var columnDefsTableTwo []*shim.ColumnDefinition
    columnOneTableTwoDef := shim.ColumnDefinition{Name: "colOneTableTwo",
        Type: shim.ColumnDefinition_STRING, Key: true}
    columnTwoTableTwoDef := shim.ColumnDefinition{Name: "colTwoTableTwo",
        Type: shim.ColumnDefinition_INT32, Key: false}
    columnThreeTableTwoDef := shim.ColumnDefinition{Name: "colThreeTableThree",
        Type: shim.ColumnDefinition_INT32, Key: true}
    columnFourTableTwoDef := shim.ColumnDefinition{Name: "colFourTableFour",
        Type: shim.ColumnDefinition_STRING, Key: true}
    columnDefsTableTwo = append(columnDefsTableTwo, &columnOneTableTwoDef)
    columnDefsTableTwo = append(columnDefsTableTwo, &columnTwoTableTwoDef)
    columnDefsTableTwo = append(columnDefsTableTwo, &columnThreeTableTwoDef)
    columnDefsTableTwo = append(columnDefsTableTwo, &columnFourTableTwoDef)
    return stub.CreateTable("tableTwo", columnDefsTableTwo)
}

现在,要将数据插入到该表中,可以按照以下方式进行:

if len(args) < 4 {
    return nil, errors.New("insertRowTableTwo failed. Must include 4 column values")
}

col1Val := args[0]
col2Int, err := strconv.ParseInt(args[1], 10, 32)
if err != nil {
    return nil, errors.New("insertRowTableTwo failed. arg[1] must be convertable to int32")
}
col2Val := int32(col2Int)
col3Int, err := strconv.ParseInt(args[2], 10, 32)
if err != nil {
    return nil, errors.New("insertRowTableTwo failed. arg[2] must be convertable to int32")
}
col3Val := int32(col3Int)
col4Val := args[3]

var columns []*shim.Column
col1 := shim.Column{Value: &shim.Column_String_{String_: col1Val}}
col2 := shim.Column{Value: &shim.Column_Int32{Int32: col2Val}}
col3 := shim.Column{Value: &shim.Column_Int32{Int32: col3Val}}
col4 := shim.Column{Value: &shim.Column_String_{String_: col4Val}}
columns = append(columns, &col1)
columns = append(columns, &col2)
columns = append(columns, &col3)
columns = append(columns, &col4)

row := shim.Row{Columns: columns}
ok, err := stub.InsertRow("tableTwo", row)
if err != nil {
    return nil, fmt.Errorf("insertRowTableTwo operation failed. %s", err)
}
if !ok {
    return nil, errors.New("insertRowTableTwo operation failed. Row with given key already exists")
}

现在,要查询此数据而不指定所有键,请按照以下方式进行:

if len(args) < 1 {
    return nil, errors.New("getRowsTableTwo failed. Must include at least key values")
}

var columns []shim.Column

col1Val := args[0]
col1 := shim.Column{Value: &shim.Column_String_{String_: col1Val}}
columns = append(columns, col1)

if len(args) > 1 {
    col2Int, err := strconv.ParseInt(args[1], 10, 32)
    if err != nil {
        return nil, errors.New("getRowsTableTwo failed. arg[1] must be convertable to int32")
    }
    col2Val := int32(col2Int)
    col2 := shim.Column{Value: &shim.Column_Int32{Int32: col2Val}}
    columns = append(columns, col2)
}

rowChannel, err := stub.GetRows("tableTwo", columns)
if err != nil {
    return nil, fmt.Errorf("getRowsTableTwo operation failed. %s", err)
}

var rows []shim.Row
for {
    select {
    case row, ok := <-rowChannel:
        if !ok {
            rowChannel = nil
        } else {
            rows = append(rows, row)
        }
    }
    if rowChannel == nil {
        break
    }
}

jsonRows, err := json.Marshal(rows)
if err != nil {
    return nil, fmt.Errorf("getRowsTableTwo operation failed. Error marshaling JSON: %s", err)
}

return jsonRows, nil

在插入数据后,API stub.GetRows("tableTwo", columns) 允许你在不指定所有键列的情况下检索数据。

以上代码摘自之前在Fabric GitHub存储库中的文件,路径为gerrit/src/github.com/hyperledger/fabric/bddtests/chaincode/go/table/table.go。

希望对你有所帮助。

英文:

Worldstate level storage works at the {key,value} level. And as obvious its only intended for a single value lookup for a specified key. I think what you are looking for calls for a next level higher level of Abstraction of WorldState - called Table constructs.
fabric/examples/chaincode/go/asset_management_interactive/asset_management.go has an example on how to create a table with the columns you want. While defining the primary keys of your data structure to hold the transaction, you include Status as one of the keys and you would be able to retrieve data on the basis of Status as well.

Some sample code to create the table is as below

func createTableTwo(stub shim.ChaincodeStubInterface) error {
var columnDefsTableTwo []*shim.ColumnDefinition
columnOneTableTwoDef := shim.ColumnDefinition{Name: &quot;colOneTableTwo&quot;,
Type: shim.ColumnDefinition_STRING, Key: true}
columnTwoTableTwoDef := shim.ColumnDefinition{Name: &quot;colTwoTableTwo&quot;,
Type: shim.ColumnDefinition_INT32, Key: false}
columnThreeTableTwoDef := shim.ColumnDefinition{Name: &quot;colThreeTableThree&quot;,
Type: shim.ColumnDefinition_INT32, Key: true}
columnFourTableTwoDef := shim.ColumnDefinition{Name: &quot;colFourTableFour&quot;,
Type: shim.ColumnDefinition_STRING, Key: true}
columnDefsTableTwo = append(columnDefsTableTwo, &amp;columnOneTableTwoDef)
columnDefsTableTwo = append(columnDefsTableTwo, &amp;columnTwoTableTwoDef)
columnDefsTableTwo = append(columnDefsTableTwo, &amp;columnThreeTableTwoDef)
columnDefsTableTwo = append(columnDefsTableTwo, &amp;columnFourTableTwoDef)
return stub.CreateTable(&quot;tableTwo&quot;, columnDefsTableTwo)
}

Now to insert data into this table, as shown

if len(args) &lt; 4 {
return nil, errors.New(&quot;insertRowTableTwo failed. Must include 4 column values&quot;)
}
col1Val := args[0]
col2Int, err := strconv.ParseInt(args[1], 10, 32)
if err != nil {
return nil, errors.New(&quot;insertRowTableTwo failed. arg[1] must be convertable to int32&quot;)
}
col2Val := int32(col2Int)
col3Int, err := strconv.ParseInt(args[2], 10, 32)
if err != nil {
return nil, errors.New(&quot;insertRowTableTwo failed. arg[2] must be convertable to int32&quot;)
}
col3Val := int32(col3Int)
col4Val := args[3]
var columns []*shim.Column
col1 := shim.Column{Value: &amp;shim.Column_String_{String_: col1Val}}
col2 := shim.Column{Value: &amp;shim.Column_Int32{Int32: col2Val}}
col3 := shim.Column{Value: &amp;shim.Column_Int32{Int32: col3Val}}
col4 := shim.Column{Value: &amp;shim.Column_String_{String_: col4Val}}
columns = append(columns, &amp;col1)
columns = append(columns, &amp;col2)
columns = append(columns, &amp;col3)
columns = append(columns, &amp;col4)
row := shim.Row{Columns: columns}
ok, err := stub.InsertRow(&quot;tableTwo&quot;, row)
if err != nil {
return nil, fmt.Errorf(&quot;insertRowTableTwo operation failed. %s&quot;, err)
}
if !ok {
return nil, errors.New(&quot;insertRowTableTwo operation failed. Row with given key already exists&quot;)
}

Now to query this data by not specifying all the keys, do as below

if len(args) &lt; 1 {
return nil, errors.New(&quot;getRowsTableTwo failed. Must include at least key values&quot;)
}
var columns []shim.Column
col1Val := args[0]
col1 := shim.Column{Value: &amp;shim.Column_String_{String_: col1Val}}
columns = append(columns, col1)
if len(args) &gt; 1 {
col2Int, err := strconv.ParseInt(args[1], 10, 32)
if err != nil {
return nil, errors.New(&quot;getRowsTableTwo failed. arg[1] must be convertable to int32&quot;)
}
col2Val := int32(col2Int)
col2 := shim.Column{Value: &amp;shim.Column_Int32{Int32: col2Val}}
columns = append(columns, col2)
}
rowChannel, err := stub.GetRows(&quot;tableTwo&quot;, columns)
if err != nil {
return nil, fmt.Errorf(&quot;getRowsTableTwo operation failed. %s&quot;, err)
}
var rows []shim.Row
for {
select {
case row, ok := &lt;-rowChannel:
if !ok {
rowChannel = nil
} else {
rows = append(rows, row)
}
}
if rowChannel == nil {
break
}
}
jsonRows, err := json.Marshal(rows)
if err != nil {
return nil, fmt.Errorf(&quot;getRowsTableTwo operation failed. Error marshaling JSON: %s&quot;, err)
}
return jsonRows, nil

Once you have inserted the data, the API stub.GetRows("tableTwo", columns) lets you retrieve it without specifying all the key columns.

Above code is quoted from a file which was present in the Fabric github repo earlier in the following path gerrit/src/github.com/hyperledger/fabric/bddtests/chaincode/go/table/table.go

Hope this helps.

答案2

得分: 0

在Hyperledger Fabric v1.0中,你可以将数据建模为JSON,并利用CouchDB作为状态数据库。在这种情况下,你可以直接查询JSON的任何字段。

marbles02 chaincode中有一个这样的数据模式示例。

v1.0中还有其他各种分类账和状态数据库选项

英文:

In Hyperledger Fabric v1.0 you can model your data as JSON and utilize CouchDB as the state database. In this case you can directly query on any field of the JSON.

There is an example of such data patterns in the marbles02 chaincode.

There are various other ledger and state database options in v1.0.

huangapple
  • 本文由 发表于 2017年3月8日 16:38:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/42666568.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定