1

我正在尝试对 golang 链码中的结果进行排序,但结果是随机的,下面是我的链码示例:

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "time"

    "github.com/hyperledger/fabric/core/chaincode/shim"
    "github.com/hyperledger/fabric/protos/peer"
)

type itemStruct struct {
    ID        string    `json:"id"`
    Status    string    `json:"status"`
    CreatedAt time.Time `json:"created_at"`
}

func createItem(stub shim.ChaincodeStubInterface, args []string) peer.Response {
    if len(args) != 3 {
        return shim.Error(fmt.Sprintf("Expecting %v arguments {id, status, created_at}, but got %v", 3, len(args)))
    }

    itemID := args[0]
    if len(itemID) == 0 {
        return shim.Error("id field is required")
    }
    status := args[1]
    if len(status) == 0 {
        return shim.Error("status field is required")
    }
    createdAt, err := time.Parse(time.RFC3339, args[2])
    if err != nil {
        return shim.Error("created_at is not a valid datetime string")
    }

    item := itemStruct{
        ID:        itemID,
        CreatedAt: createdAt,
    }

    itemAsJSONBytes, err := json.Marshal(item)
    if err != nil {
        return shim.Error(err.Error())
    }

    return shim.Success(itemAsJSONBytes)
}

func getPendingItems(stub shim.ChaincodeStubInterface, args []string) peer.Response {
    var bookmark string

    if len(args) > 0 && len(args[0]) > 0 {
        bookmark = args[0]
    }

    queryString := `{
        "selector": {
            "status": "pending"
        },
        "sort": [
            {"created_at": "desc"}
        ]
    }`
    result, pagination, err := queryWithPagination(stub, queryString, 20, bookmark)
    if err != nil {
        return shim.Error(err.Error())
    }

    return shim.Success(constructResponse(result, pagination).Bytes())
}

func queryWithPagination(stub shim.ChaincodeStubInterface, queryString string, pageSize int32, bookmark string) (map[string]string, string, error) {
    var pagination string
    iterator, meta, err := stub.GetQueryResultWithPagination(queryString, pageSize, bookmark)
    if err != nil {
        return nil, pagination, err
    }
    defer iterator.Close()

    result, err := iterateResult(iterator)
    if err != nil {
        return nil, pagination, err
    }

    pagination = fmt.Sprintf(`{"count": %v, "next_page_token": "%v"}`, meta.FetchedRecordsCount, meta.Bookmark)
    return result, pagination, nil
}

func constructResponse(items map[string]string, pagination string) *bytes.Buffer {
    // buffer is a JSON array containing QueryResults
    var buffer bytes.Buffer

    if len(pagination) > 0 {
        buffer.WriteString(`{"data":`)
    }

    buffer.WriteString(`[`)

    bArrayMemberAlreadyWritten := false
    for _, val := range items {
        // Add a comma before array members, suppress it for the first array member
        if bArrayMemberAlreadyWritten == true {
            buffer.WriteString(",")
        }
        buffer.WriteString(val)
        bArrayMemberAlreadyWritten = true
    }
    buffer.WriteString("]")

    if len(pagination) > 0 {
        buffer.WriteString(`,"pagination":`)
        buffer.WriteString(pagination)
        buffer.WriteString("}")
    }

    return &buffer
}

func iterateResult(iterator shim.StateQueryIteratorInterface) (map[string]string, error) {
    result := map[string]string{}

    for iterator.HasNext() {
        queryResponse, err := iterator.Next()
        if err != nil {
            return nil, err
        }
        result[queryResponse.Key] = string(queryResponse.Value)
    }

    return result, nil
}

// SmartContract : Smart contract struct
type SmartContract struct {
}

// Init : This method is called when chaincode is initialized or updated.
func (s *SmartContract) Init(stub shim.ChaincodeStubInterface) peer.Response {
    return shim.Success(nil)
}

// Invoke : This method is called when any transaction or query fired
func (s *SmartContract) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
    // Retrieve the requested Smart Contract function and arguments
    function, args := stub.GetFunctionAndParameters()

    // Route to the appropriate handler function to interact with the ledger appropriately
    // Transactions
    if function == "createItem" {
        return createItem(stub, args)
    }

    // Queries
    if function == "getItems" {
        return getItems(stub, args)
    }

    return shim.Error("Invalid function")
}

func main() {
    // Create a new Smart Contract
    err := shim.Start(new(SmartContract))

    if err != nil {
        fmt.Printf("Error creating new Smart Contract: %s", err)
    }
}

它根据传递的内容创建可以具有不同状态的资产,并且我定义了一个仅获取pending项目的查询函数。

我已经应用了排序,但结果仍然是随机的,有人可以在这里帮助我并指导我哪里出错了吗?

4

1 回答 1

1

排序字段必须存在于选择器中!

就像是:

queryString := `{
    "selector": {
        "created_at": "$gt": null
        "status": "pending"
    },
    "sort": [
        {"created_at": "desc"}
    ]
}`

或(范围)

 queryString := `{
    "selector": {
        "created_at":  {
        "$gt": "2015-01-01T00:00:00Z",
        "$lt": "2019-01-01T00:00:00Z"
        },
        "status": "pending"
    },
    "sort": [
        {"created_at": "desc"}
    ]
}`

根据文档 - 要使用排序,请确保:

  • 至少有一个排序字段包含在选择器中。
  • 已经定义了一个索引,所有排序字段的顺序相同。
  • 排序数组中的每个对象都有一个键。
  • 如果排序数组中的对象没有单个键,则生成的排序顺序是特定于实现的并且可能会更改。

此外,由于 fyi Find不支持具有不同排序顺序的多个字段,因此方向必须是全部升序或全部降序。

另请参阅CouchDB 排序不起作用

于 2019-03-25T12:44:11.290 回答