diff --git a/internal/storage/ledger/accounts.go b/internal/storage/ledger/accounts.go index 51a7c65a5..af0b2a222 100644 --- a/internal/storage/ledger/accounts.go +++ b/internal/storage/ledger/accounts.go @@ -45,13 +45,12 @@ func convertOperatorToSQL(operator string) string { func (s *Store) selectBalance(date *time.Time) *bun.SelectQuery { if date != nil && !date.IsZero() { - sortedMoves := s.SortMovesBySeq(date). - ColumnExpr("(post_commit_volumes->>'input')::numeric - (post_commit_volumes->>'output')::numeric as balance"). - Limit(1) + sortedMoves := s.SelectDistinctMovesBySeq(date). + ColumnExpr("(post_commit_volumes->>'input')::numeric - (post_commit_volumes->>'output')::numeric as balance") return s.db.NewSelect(). ModelTableExpr("(?) moves", sortedMoves). - ColumnExpr("accounts_address, asset") + ColumnExpr("accounts_address, asset, balance") } return s.db.NewSelect(). @@ -96,7 +95,7 @@ func (s *Store) selectAccounts(date *time.Time, expandVolumes, expandEffectiveVo if operator != "$match" { return ledgercontroller.NewErrInvalidQuery("'metadata' key filter can only be used with $match") } - case key == "first_usage": + case key == "first_usage" || key == "balance": default: return ledgercontroller.NewErrInvalidQuery("unknown key '%s' when building query", key) } diff --git a/internal/storage/ledger/accounts_test.go b/internal/storage/ledger/accounts_test.go index e8d14614b..7950426f2 100644 --- a/internal/storage/ledger/accounts_test.go +++ b/internal/storage/ledger/accounts_test.go @@ -190,6 +190,30 @@ func TestAccountsList(t *testing.T) { require.Equal(t, "account:1", accounts.Data[0].Address) require.Equal(t, "bank", accounts.Data[1].Address) }) + t.Run("list using filter on balances[USD] and PIT", func(t *testing.T) { + t.Parallel() + accounts, err := store.ListAccounts(ctx, ledgercontroller.NewListAccountsQuery(ledgercontroller.NewPaginatedQueryOptions(ledgercontroller.PITFilterWithVolumes{ + PITFilter: ledgercontroller.PITFilter{ + PIT: &now, + }, + }). + WithQueryBuilder(query.Lt("balance[USD]", 0)), + )) + require.NoError(t, err) + require.Len(t, accounts.Data, 1) // world + }) + t.Run("list using filter on balances and PIT", func(t *testing.T) { + t.Parallel() + accounts, err := store.ListAccounts(ctx, ledgercontroller.NewListAccountsQuery(ledgercontroller.NewPaginatedQueryOptions(ledgercontroller.PITFilterWithVolumes{ + PITFilter: ledgercontroller.PITFilter{ + PIT: &now, + }, + }). + WithQueryBuilder(query.Lt("balance", 0)), + )) + require.NoError(t, err) + require.Len(t, accounts.Data, 1) // world + }) t.Run("list using filter on exists metadata", func(t *testing.T) { t.Parallel() diff --git a/internal/storage/ledger/transactions_test.go b/internal/storage/ledger/transactions_test.go index 1720d43df..73c295d0b 100644 --- a/internal/storage/ledger/transactions_test.go +++ b/internal/storage/ledger/transactions_test.go @@ -711,6 +711,12 @@ func TestTransactionsList(t *testing.T) { WithQueryBuilder(query.Not(query.Exists("metadata", "category"))), expected: []ledger.Transaction{tx5, tx4}, }, + { + name: "filter using timestamp", + query: ledgercontroller.NewPaginatedQueryOptions(ledgercontroller.PITFilterWithVolumes{}). + WithQueryBuilder(query.Match("timestamp", tx5.Timestamp.Format(time.RFC3339Nano))), + expected: []ledger.Transaction{tx5, tx4}, + }, } for _, tc := range testCases {