From 1699f3265a89ae3f47208f3988c11fdb2ad9a9f5 Mon Sep 17 00:00:00 2001 From: scott Date: Sat, 19 Sep 2020 15:14:53 -0700 Subject: [PATCH] search added reverse ordering in search --- x/crud/client/cli/query.go | 46 +++++++++++++++++++++++++++++++ x/crud/client/rest/query.go | 11 ++++++++ x/crud/client/rest/rest.go | 1 + x/crud/internal/keeper/keeper.go | 45 ++++++++++++++++++++++++++++++ x/crud/internal/keeper/querier.go | 15 ++++++++++ x/crud/internal/types/querier.go | 5 ++++ 6 files changed, 123 insertions(+) diff --git a/x/crud/client/cli/query.go b/x/crud/client/cli/query.go index 58a63aba..6cb74e3c 100644 --- a/x/crud/client/cli/query.go +++ b/x/crud/client/cli/query.go @@ -38,6 +38,7 @@ func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { GetCmdQRead(storeKey, cdc), GetCmdQHas(storeKey, cdc), GetCmdQKeys(storeKey, cdc), + GetCmdQSearch(storeKey, cdc), GetCmdQKeyValues(storeKey, cdc), GetCmdQCount(storeKey, cdc), GetCmdQGetLease(storeKey, cdc), @@ -134,6 +135,51 @@ func GetCmdQKeys(queryRoute string, cdc *codec.Codec) *cobra.Command { } } +func GetCmdQSearch(queryRoute string, cdc *codec.Codec) *cobra.Command { + reverse := false + cmd := &cobra.Command{ + Use: "search [UUID] [prefix] [page] [limit]", + Short: "search UUID prefix [page] [limit]", + Args: cobra.MinimumNArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + UUID := args[0] + prefix := args[1] + + direction := "asc" + if reverse { + direction = "desc" + } + + if len(args) < 3 { + args = append(args, "1") + } + + if len(args) < 4 { + args = append(args, "100") + } + + page := args[2] + limit := args[3] + res, _, _ := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/search/%s/%s/%s/%s/%s", queryRoute, UUID, prefix, page, limit, direction), nil) + + var out types.QueryResultSearch + cdc.MustUnmarshalJSON(res, &out) + + // ensure we don't lose the fact that the keyvalues list is empty... + if out.KeyValues == nil { + out.KeyValues = make([]types.KeyValue, 0) + } + + return cliCtx.PrintOutput(out) + }, + } + cmd.Flags().BoolVar(&reverse, "reverse", false, "Search in descending order") + return cmd + +} + + func GetCmdQKeyValues(queryRoute string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "keyvalues [UUID]", diff --git a/x/crud/client/rest/query.go b/x/crud/client/rest/query.go index adf32eec..950b5b23 100644 --- a/x/crud/client/rest/query.go +++ b/x/crud/client/rest/query.go @@ -94,6 +94,17 @@ func BlzQKeysHandler(cliCtx context.CLIContext, storeName string) http.HandlerFu } } +func BlzQSearchHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + res, _, _ := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/search/%s/%s/%s/%s/%s", storeName, vars["UUID"], vars["prefix"], vars["page"], vars["limit"], vars["direction"]), nil) + + rest.PostProcessResponse(w, cliCtx, res) + } +} + + func BlzQKeyValuesHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) diff --git a/x/crud/client/rest/rest.go b/x/crud/client/rest/rest.go index 766b8eb0..2c57fdbc 100644 --- a/x/crud/client/rest/rest.go +++ b/x/crud/client/rest/rest.go @@ -35,6 +35,7 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) r.HandleFunc(fmt.Sprintf("/%s/has/{UUID}/{key}", storeName), BlzQHasHandler(cliCtx, storeName)).Methods("GET") r.HandleFunc(fmt.Sprintf("/%s/keys", storeName), BlzKeysHandler(cliCtx)).Methods("POST") r.HandleFunc(fmt.Sprintf("/%s/keys/{UUID}", storeName), BlzQKeysHandler(cliCtx, storeName)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/%s/search/{UUID}/{prefix}/{page}/{limit}/{direction}", storeName), BlzQSearchHandler(cliCtx, storeName)).Methods("GET") r.HandleFunc(fmt.Sprintf("/%s/keyvalues", storeName), BlzKeyValuesHandler(cliCtx)).Methods("POST") r.HandleFunc(fmt.Sprintf("/%s/keyvalues/{UUID}", storeName), BlzQKeyValuesHandler(cliCtx, storeName)).Methods("GET") r.HandleFunc(fmt.Sprintf("/%s/multiupdate", storeName), BlzMultiUpdateHandler(cliCtx)).Methods("POST") diff --git a/x/crud/internal/keeper/keeper.go b/x/crud/internal/keeper/keeper.go index c0d39e70..513676cd 100644 --- a/x/crud/internal/keeper/keeper.go +++ b/x/crud/internal/keeper/keeper.go @@ -24,6 +24,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" "sort" "strconv" + cosmosTypes "github.com/cosmos/cosmos-sdk/store/types" ) type MaxKeeperSizes struct { @@ -40,6 +41,7 @@ type IKeeper interface { GetCount(ctx sdk.Context, store sdk.KVStore, UUID string, owner sdk.AccAddress) types.QueryResultCount GetDefaultLeaseBlocks() int64 GetKVStore(ctx sdk.Context) sdk.KVStore + Search(ctx sdk.Context, store sdk.KVStore, UUID string, prefix string, page, limit uint, direction string, owner sdk.AccAddress) types.QueryResultKeyValues GetKeyValues(ctx sdk.Context, store sdk.KVStore, UUID string, owner sdk.AccAddress) types.QueryResultKeyValues GetKeys(ctx sdk.Context, store sdk.KVStore, UUID string, owner sdk.AccAddress) types.QueryResultKeys GetLeaseStore(ctx sdk.Context) sdk.KVStore @@ -190,6 +192,49 @@ func (k Keeper) GetCdc() *codec.Codec { return k.cdc } +func (k Keeper) Search(ctx sdk.Context, store sdk.KVStore, UUID string, searchPrefix string, page, limit uint, direction string, owner sdk.AccAddress) types.QueryResultKeyValues { + prefix := UUID + "\x00" + searchPrefix + var iterator sdk.Iterator + + if direction == "desc" { + // TODO: change this to be sdk instead of cosmosTypes after cosmos-sdk is fixed + iterator = cosmosTypes.KVStoreReversePrefixIteratorPaginated(store, []byte(prefix), page, limit) + } else { + iterator = sdk.KVStorePrefixIteratorPaginated(store, []byte(prefix), page, limit) + } + defer iterator.Close() + + keyValues := types.QueryResultKeyValues{UUID: UUID, KeyValues: make([]types.KeyValue, 0)} + + keyValuesSize := uint64(0) + + for ; iterator.Valid(); iterator.Next() { + var bz = store.Get(iterator.Key()) + var value types.BLZValue + k.cdc.MustUnmarshalBinaryBare(bz, &value) + + if owner == nil || value.Owner.Equals(owner) { + key := string(iterator.Key())[len(UUID) + 1:] + keyValuesSize = keyValuesSize + uint64(len(key)) + uint64(len(value.Value)) + + if ctx.GasMeter().IsPastLimit() { + return types.QueryResultKeyValues{UUID: UUID, KeyValues: make([]types.KeyValue, 0)} + } + + if keyValuesSize < k.mks.MaxKeyValuesSize { + keyValues.KeyValues = append(keyValues.KeyValues, types.KeyValue{ + Key: key, + Value: value.Value, + }) + } else { + return keyValues + } + } + } + return keyValues +} + + func (k Keeper) GetKeyValues(ctx sdk.Context, store sdk.KVStore, UUID string, owner sdk.AccAddress) types.QueryResultKeyValues { prefix := UUID + "\x00" iterator := sdk.KVStorePrefixIterator(store, []byte(prefix)) diff --git a/x/crud/internal/keeper/querier.go b/x/crud/internal/keeper/querier.go index 0cf5179b..c4fb5cc5 100644 --- a/x/crud/internal/keeper/querier.go +++ b/x/crud/internal/keeper/querier.go @@ -27,6 +27,7 @@ const ( QueryRead = "read" QueryHas = "has" QueryKeys = "keys" + QuerySearch = "search" QueryKeyValues = "keyvalues" QueryCount = "count" QueryGetLease = "getlease" @@ -45,6 +46,8 @@ func NewQuerier(keeper IKeeper) sdk.Querier { return queryHas(ctx, path[1:], req, keeper, keeper.GetCdc()) case QueryKeys: return queryKeys(ctx, path[1:], req, keeper, keeper.GetCdc()) + case QuerySearch: + return querySearch(ctx, path[1:], req, keeper, keeper.GetCdc()) case QueryKeyValues: return queryKeyValues(ctx, path[1:], req, keeper, keeper.GetCdc()) case QueryCount: @@ -110,6 +113,18 @@ func queryKeys(ctx sdk.Context, path []string, _ abci.RequestQuery, keeper IKeep return res, nil } +func querySearch(ctx sdk.Context, path []string, _ abci.RequestQuery, keeper IKeeper, cdc *codec.Codec) ([]byte, error) { + page, _ := strconv.ParseUint(path[2], 10, 64) + limit, _ := strconv.ParseUint(path[3], 10, 64) + + res, err := codec.MarshalJSONIndent(cdc, keeper.Search(ctx, keeper.GetKVStore(ctx), path[0], path[1], uint(page), uint(limit), path[4], nil)) + if err != nil { + panic("could not marshal result to JSON") + } + + return res, nil +} + func queryKeyValues(ctx sdk.Context, path []string, _ abci.RequestQuery, keeper IKeeper, cdc *codec.Codec) ([]byte, error) { res, err := codec.MarshalJSONIndent(cdc, keeper.GetKeyValues(ctx, keeper.GetKVStore(ctx), path[0], nil)) if err != nil { diff --git a/x/crud/internal/types/querier.go b/x/crud/internal/types/querier.go index 789c1916..6f503ac0 100644 --- a/x/crud/internal/types/querier.go +++ b/x/crud/internal/types/querier.go @@ -56,6 +56,11 @@ type QueryResultKeyValues struct { KeyValues []KeyValue `json:"keyvalues"` } +type QueryResultSearch struct { + UUID string `json:"uuid"` + KeyValues []KeyValue `json:"keyvalues"` +} + type QueryResultCount struct { UUID string `json:"uuid"` Count uint64 `json:"count,string"`