rpc: add sort_order option to tx_search (#4342)

I have added order_by which can be "asc" or "desc" (should be in string format) in the tx_search RPC method.

Fixes: #3333

Author: @princesinha19
This commit is contained in:
Anton Kaliaev
2020-01-27 21:20:56 +04:00
committed by GitHub
parent 59a922d38a
commit d90dc9db26
12 changed files with 95 additions and 65 deletions
+3 -1
View File
@@ -348,13 +348,15 @@ func (c *baseRPCClient) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
return result, nil
}
func (c *baseRPCClient) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) {
func (c *baseRPCClient) TxSearch(query string, prove bool, page, perPage int, orderBy string) (
*ctypes.ResultTxSearch, error) {
result := new(ctypes.ResultTxSearch)
params := map[string]interface{}{
"query": query,
"prove": prove,
"page": page,
"per_page": perPage,
"order_by": orderBy,
}
_, err := c.caller.Call("tx_search", params, result)
if err != nil {
+1 -1
View File
@@ -69,7 +69,7 @@ type SignClient interface {
Commit(height *int64) (*ctypes.ResultCommit, error)
Validators(height *int64, page, perPage int) (*ctypes.ResultValidators, error)
Tx(hash []byte, prove bool) (*ctypes.ResultTx, error)
TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error)
TxSearch(query string, prove bool, page, perPage int, orderBy string) (*ctypes.ResultTxSearch, error)
}
// HistoryClient provides access to data from genesis to now in large chunks.
+3 -2
View File
@@ -160,8 +160,9 @@ func (c *Local) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
return core.Tx(c.ctx, hash, prove)
}
func (c *Local) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) {
return core.TxSearch(c.ctx, query, prove, page, perPage)
func (c *Local) TxSearch(query string, prove bool, page, perPage int, orderBy string) (
*ctypes.ResultTxSearch, error) {
return core.TxSearch(c.ctx, query, prove, page, perPage, orderBy)
}
func (c *Local) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
+33 -13
View File
@@ -418,7 +418,7 @@ func TestTxSearch(t *testing.T) {
c := getHTTPClient()
_, _, tx := MakeTxKV()
bres, err := c.BroadcastTxCommit(tx)
require.Nil(t, err, "%+v", err)
require.Nil(t, err)
txHeight := bres.Height
txHash := bres.Hash
@@ -430,8 +430,8 @@ func TestTxSearch(t *testing.T) {
// now we query for the tx.
// since there's only one tx, we know index=0.
result, err := c.TxSearch(fmt.Sprintf("tx.hash='%v'", txHash), true, 1, 30)
require.Nil(t, err, "%+v", err)
result, err := c.TxSearch(fmt.Sprintf("tx.hash='%v'", txHash), true, 1, 30, "asc")
require.Nil(t, err)
require.Len(t, result.Txs, 1)
ptx := result.Txs[0]
@@ -448,33 +448,53 @@ func TestTxSearch(t *testing.T) {
}
// query by height
result, err = c.TxSearch(fmt.Sprintf("tx.height=%d", txHeight), true, 1, 30)
require.Nil(t, err, "%+v", err)
result, err = c.TxSearch(fmt.Sprintf("tx.height=%d", txHeight), true, 1, 30, "asc")
require.Nil(t, err)
require.Len(t, result.Txs, 1)
// query for non existing tx
result, err = c.TxSearch(fmt.Sprintf("tx.hash='%X'", anotherTxHash), false, 1, 30)
require.Nil(t, err, "%+v", err)
result, err = c.TxSearch(fmt.Sprintf("tx.hash='%X'", anotherTxHash), false, 1, 30, "asc")
require.Nil(t, err)
require.Len(t, result.Txs, 0)
// query using a compositeKey (see kvstore application)
result, err = c.TxSearch("app.creator='Cosmoshi Netowoko'", false, 1, 30)
require.Nil(t, err, "%+v", err)
result, err = c.TxSearch("app.creator='Cosmoshi Netowoko'", false, 1, 30, "asc")
require.Nil(t, err)
if len(result.Txs) == 0 {
t.Fatal("expected a lot of transactions")
}
// query using a compositeKey (see kvstore application) and height
result, err = c.TxSearch("app.creator='Cosmoshi Netowoko' AND tx.height<10000", true, 1, 30)
require.Nil(t, err, "%+v", err)
result, err = c.TxSearch("app.creator='Cosmoshi Netowoko' AND tx.height<10000", true, 1, 30, "asc")
require.Nil(t, err)
if len(result.Txs) == 0 {
t.Fatal("expected a lot of transactions")
}
// query a non existing tx with page 1 and txsPerPage 1
result, err = c.TxSearch("app.creator='Cosmoshi Neetowoko'", true, 1, 1)
require.Nil(t, err, "%+v", err)
result, err = c.TxSearch("app.creator='Cosmoshi Neetowoko'", true, 1, 1, "asc")
require.Nil(t, err)
require.Len(t, result.Txs, 0)
// broadcast another transaction to make sure we have at least two.
_, _, tx2 := MakeTxKV()
_, err = c.BroadcastTxCommit(tx2)
require.Nil(t, err)
// chech sorting
result, err = c.TxSearch(fmt.Sprintf("tx.height >= 1"), false, 1, 30, "asc")
require.Nil(t, err)
for k := 0; k < len(result.Txs)-1; k++ {
require.LessOrEqual(t, result.Txs[k].Height, result.Txs[k+1].Height)
require.LessOrEqual(t, result.Txs[k].Index, result.Txs[k+1].Index)
}
result, err = c.TxSearch(fmt.Sprintf("tx.height >= 1"), false, 1, 30, "desc")
require.Nil(t, err)
for k := 0; k < len(result.Txs)-1; k++ {
require.GreaterOrEqual(t, result.Txs[k].Height, result.Txs[k+1].Height)
require.GreaterOrEqual(t, result.Txs[k].Index, result.Txs[k+1].Index)
}
}
}
+1 -1
View File
@@ -23,7 +23,7 @@ var Routes = map[string]*rpc.RPCFunc{
"block_results": rpc.NewRPCFunc(BlockResults, "height"),
"commit": rpc.NewRPCFunc(Commit, "height"),
"tx": rpc.NewRPCFunc(Tx, "hash,prove"),
"tx_search": rpc.NewRPCFunc(TxSearch, "query,prove,page,per_page"),
"tx_search": rpc.NewRPCFunc(TxSearch, "query,prove,page,per_page,order_by"),
"validators": rpc.NewRPCFunc(Validators, "height,page,per_page"),
"dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, ""),
"consensus_state": rpc.NewRPCFunc(ConsensusState, ""),
+27 -3
View File
@@ -2,9 +2,11 @@ package core
import (
"fmt"
"sort"
"github.com/pkg/errors"
tmmath "github.com/tendermint/tendermint/libs/math"
tmquery "github.com/tendermint/tendermint/libs/pubsub/query"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpctypes "github.com/tendermint/tendermint/rpc/lib/types"
@@ -53,10 +55,11 @@ func Tx(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error
// TxSearch allows you to query for multiple transactions results. It returns a
// list of transactions (maximum ?per_page entries) and the total count.
// More: https://docs.tendermint.com/master/rpc/#/Info/tx_search
func TxSearch(ctx *rpctypes.Context, query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) {
func TxSearch(ctx *rpctypes.Context, query string, prove bool, page, perPage int, orderBy string) (
*ctypes.ResultTxSearch, error) {
// if index is disabled, return error
if _, ok := txIndexer.(*null.TxIndex); ok {
return nil, fmt.Errorf("transaction indexing is disabled")
return nil, errors.New("transaction indexing is disabled")
}
q, err := tmquery.New(query)
@@ -100,5 +103,26 @@ func TxSearch(ctx *rpctypes.Context, query string, prove bool, page, perPage int
}
}
if len(apiResults) > 1 {
switch orderBy {
case "desc":
sort.Slice(apiResults, func(i, j int) bool {
if apiResults[i].Height == apiResults[j].Height {
return apiResults[i].Index > apiResults[j].Index
}
return apiResults[i].Height > apiResults[j].Height
})
case "asc", "":
sort.Slice(apiResults, func(i, j int) bool {
if apiResults[i].Height == apiResults[j].Height {
return apiResults[i].Index < apiResults[j].Index
}
return apiResults[i].Height < apiResults[j].Height
})
default:
return nil, errors.New("expected order_by to be either `asc` or `desc` or empty")
}
}
return &ctypes.ResultTxSearch{Txs: apiResults, TotalCount: totalCount}, nil
}
+8
View File
@@ -859,6 +859,14 @@ paths:
type: number
default: 30
example: 30
- in: query
name: order_by
description: Order in which transactions are sorted ("asc" or "desc"), by height & index. If empty, default sorting will be still applied.
required: false
schema:
type: string
default: "asc"
example: "asc"
tags:
- Info
description: |