Skip to content

Commit be278e9

Browse files
committed
Add Count operation to Querier interface
1 parent 06bc242 commit be278e9

16 files changed

+305
-3
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
run: docker run -d -p 27017:27017 --name mongodb mongo:latest
4141

4242
- name: Run Tests
43-
run: go test `go list ./...` -timeout 15s -count=1
43+
run: go test `go list ./mongodb/...` -timeout 15s -count=1
4444

4545
- name: Stop MongoDB
4646
run: |

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# v4.1.0
2+
3+
- (feature) Add `Count` operation to `Querier` interface
4+
15
# v4.0.5
26

37
- (bug) Fix postgres `in` and `inArray` filters

mongodb/collection_querier.go

+21
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,27 @@ func newCollectionQuerier(connection *Connection, database *clerk.Database) *col
2020
}
2121
}
2222

23+
func (q *collectionQuerier) Count(
24+
ctx context.Context,
25+
query *clerk.Query[*clerk.Collection],
26+
) (int64, error) {
27+
opts := options.ListCollections()
28+
29+
filters, err := resolveFilters(query.Filters...)
30+
if err != nil {
31+
return 0, err
32+
}
33+
34+
queryCtx, cancel := q.connection.config.GetContext(ctx)
35+
defer cancel()
36+
37+
names, err := q.connection.client.
38+
Database(q.database.Name).
39+
ListCollectionNames(queryCtx, filters, opts)
40+
41+
return int64(len(names)), err
42+
}
43+
2344
func (q *collectionQuerier) ExecuteQuery(
2445
ctx context.Context,
2546
query *clerk.Query[*clerk.Collection],

mongodb/database_querier.go

+19
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,25 @@ func newDatabaseQuerier(connection *Connection) *databaseQuerier {
1717
}
1818
}
1919

20+
func (q *databaseQuerier) Count(
21+
ctx context.Context,
22+
query *clerk.Query[*clerk.Database],
23+
) (int64, error) {
24+
opts := options.ListDatabases()
25+
26+
filters, err := resolveFilters(query.Filters...)
27+
if err != nil {
28+
return 0, err
29+
}
30+
31+
queryCtx, cancel := q.connection.config.GetContext(ctx)
32+
defer cancel()
33+
34+
names, err := q.connection.client.
35+
ListDatabaseNames(queryCtx, filters, opts)
36+
return int64(len(names)), err
37+
}
38+
2039
func (q *databaseQuerier) ExecuteQuery(
2140
ctx context.Context,
2241
query *clerk.Query[*clerk.Database],

mongodb/index_querier.go

+21
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,27 @@ func newIndexQuerier(connection *Connection, collection *clerk.Collection) *inde
2020
}
2121
}
2222

23+
func (q *indexQuerier) Count(
24+
ctx context.Context,
25+
query *clerk.Query[*clerk.Index],
26+
) (int64, error) {
27+
queryCtx, cancel := q.connection.config.GetContext(ctx)
28+
defer cancel()
29+
30+
cursor, err := q.connection.client.
31+
Database(q.collection.Database.Name).
32+
Collection(q.collection.Name).
33+
Indexes().
34+
List(queryCtx)
35+
if err != nil {
36+
return 0, err
37+
}
38+
39+
var indices []*clerk.Index
40+
err = cursor.All(queryCtx, &indices)
41+
return int64(len(indices)), err
42+
}
43+
2344
func (q *indexQuerier) ExecuteQuery(
2445
ctx context.Context,
2546
query *clerk.Query[*clerk.Index],

mongodb/querier.go

+20
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,26 @@ func newQuerier[T any](connection *Connection, collection *clerk.Collection) *qu
2121
}
2222
}
2323

24+
func (q *querier[T]) Count(
25+
ctx context.Context,
26+
query *clerk.Query[T],
27+
) (int64, error) {
28+
opts := options.Count()
29+
30+
filters, err := resolveFilters(query.Filters...)
31+
if err != nil {
32+
return 0, err
33+
}
34+
35+
queryCtx, cancel := q.connection.config.GetContext(ctx)
36+
defer cancel()
37+
38+
return q.connection.client.
39+
Database(q.collection.Database.Name).
40+
Collection(q.collection.Name).
41+
CountDocuments(queryCtx, filters, opts)
42+
}
43+
2444
func (q *querier[T]) ExecuteQuery(
2545
ctx context.Context,
2646
query *clerk.Query[T],

postgres/collection_querier.go

+7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ func newCollectionQuerier(conn *Connection, database *clerk.Database) *collectio
1818
}
1919
}
2020

21+
func (q *collectionQuerier) Count(
22+
ctx context.Context,
23+
query *clerk.Query[*clerk.Collection],
24+
) (int64, error) {
25+
return 0, nil // @todo
26+
}
27+
2128
func (q *collectionQuerier) ExecuteQuery(
2229
ctx context.Context,
2330
query *clerk.Query[*clerk.Collection],

postgres/collection_querier_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@ import (
99
"github.com/stretchr/testify/assert"
1010
)
1111

12+
func Test_CollectionQuerier_Count(t *testing.T) {
13+
conn := postgres.NewIntegrationConnection(t)
14+
15+
database := clerk.NewDatabase("test_database")
16+
collectionOperator := postgres.NewCollectionOperator(conn, database)
17+
18+
total, err := clerk.NewQuery[*clerk.Collection](collectionOperator).
19+
Count(context.Background())
20+
assert.NoError(t, err)
21+
assert.Equal(t, int64(1), total)
22+
}
23+
1224
func Test_CollectionQuerier_FindsAllCollections(t *testing.T) {
1325
conn := postgres.NewIntegrationConnection(t)
1426

postgres/database_querier.go

+39
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,45 @@ func newDatabaseQuerier(conn *Connection) *databaseQuerier {
1717
}
1818
}
1919

20+
func (q *databaseQuerier) Count(
21+
ctx context.Context,
22+
query *clerk.Query[*clerk.Database],
23+
) (int64, error) {
24+
condition, err := filtersToCondition("", query.Filters...)
25+
if err != nil {
26+
return 0, err
27+
}
28+
29+
stat, vals, err := statementBuilder().
30+
Select("Count(*)").
31+
From("pg_database").
32+
Where(condition).
33+
ToSql()
34+
if err != nil {
35+
return 0, err
36+
}
37+
stat = strings.ReplaceAll(stat, "name", "datname")
38+
39+
queryCtx, cancel := q.conn.config.GetContext(ctx)
40+
defer cancel()
41+
42+
rows, err := q.conn.pool.Query(queryCtx, stat, vals...)
43+
if err != nil {
44+
return 0, err
45+
}
46+
defer rows.Close()
47+
48+
var total int64
49+
50+
for rows.Next() {
51+
if err := rows.Scan(&total); err != nil {
52+
return 0, err
53+
}
54+
}
55+
56+
return total, nil
57+
}
58+
2059
func (q *databaseQuerier) ExecuteQuery(
2160
ctx context.Context,
2261
query *clerk.Query[*clerk.Database],

postgres/database_querier_test.go

+11
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ import (
99
"github.com/stretchr/testify/assert"
1010
)
1111

12+
func Test_DatabaseQuerier_Count(t *testing.T) {
13+
conn := postgres.NewIntegrationConnection(t)
14+
15+
databaseOperator := postgres.NewDatabaseOperator(conn)
16+
17+
total, err := clerk.NewQuery[*clerk.Database](databaseOperator).
18+
Count(context.Background())
19+
assert.NoError(t, err)
20+
assert.Equal(t, int64(1), total)
21+
}
22+
1223
func Test_DatabaseQuerier_FindsAllDatabases(t *testing.T) {
1324
conn := postgres.NewIntegrationConnection(t)
1425

postgres/index_querier.go

+56-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,60 @@ func newIndexQuerier(conn *Connection, collection *clerk.Collection) *indexQueri
2626
}
2727
}
2828

29+
func (q *indexQuerier) Count(
30+
ctx context.Context,
31+
query *clerk.Query[*clerk.Index],
32+
) (int64, error) {
33+
var name string
34+
for _, filter := range query.Filters {
35+
switch filter.(type) {
36+
case *clerk.Equals:
37+
if strings.ToLower(filter.Key()) == "name" {
38+
name = fmt.Sprintf("%s_%s", q.collection.Name, filter.Value().(string))
39+
}
40+
}
41+
}
42+
43+
selectFn := func(ctx context.Context, dbConn *pgx.Conn) (pgx.Rows, error) {
44+
if name == "" {
45+
return dbConn.Query(ctx, "SELECT Count(*) FROM pg_indexes WHERE tablename = $1", q.collection.Name)
46+
}
47+
48+
return dbConn.Query(ctx, "SELECT Count(*) FROM pg_indexes WHERE tablename = $1 AND indexname = $2", q.collection.Name, name)
49+
}
50+
51+
queryCtx, cancel := q.conn.config.GetContext(ctx)
52+
defer cancel()
53+
54+
var total int64
55+
56+
if err := q.transactor.executeInTransactionIfAvailable(queryCtx, q.collection.Database, func(ctx context.Context) error {
57+
dbConn, release, err := q.conn.createOrUseDatabase(ctx, q.collection.Database.Name)
58+
defer release()
59+
if err != nil {
60+
return err
61+
}
62+
63+
rows, err := selectFn(ctx, dbConn)
64+
if err != nil {
65+
return err
66+
}
67+
defer rows.Close()
68+
69+
for rows.Next() {
70+
if err := rows.Scan(&total); err != nil {
71+
return err
72+
}
73+
}
74+
75+
return nil
76+
}); err != nil {
77+
return 0, err
78+
}
79+
80+
return total, nil
81+
}
82+
2983
func (q *indexQuerier) ExecuteQuery(
3084
ctx context.Context,
3185
query *clerk.Query[*clerk.Index],
@@ -40,7 +94,7 @@ func (q *indexQuerier) ExecuteQuery(
4094
}
4195
}
4296

43-
createFn := func(ctx context.Context, dbConn *pgx.Conn) (pgx.Rows, error) {
97+
selectFn := func(ctx context.Context, dbConn *pgx.Conn) (pgx.Rows, error) {
4498
if name == "" {
4599
return dbConn.Query(ctx, "SELECT indexname, indexdef FROM pg_indexes WHERE tablename = $1", q.collection.Name)
46100
}
@@ -60,7 +114,7 @@ func (q *indexQuerier) ExecuteQuery(
60114
return err
61115
}
62116

63-
rows, err := createFn(ctx, dbConn)
117+
rows, err := selectFn(ctx, dbConn)
64118
if err != nil {
65119
return err
66120
}

postgres/index_querier_test.go

+14
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@ import (
99
"github.com/stretchr/testify/assert"
1010
)
1111

12+
func Test_IndexQuerier_Count(t *testing.T) {
13+
conn := postgres.NewIntegrationConnection(t)
14+
15+
database := clerk.NewDatabase("test_database")
16+
collection := clerk.NewCollection(database, "test_collection")
17+
18+
indexOperator := postgres.NewIndexOperator(conn, collection)
19+
20+
total, err := clerk.NewQuery[*clerk.Index](indexOperator).
21+
Count(context.Background())
22+
assert.NoError(t, err)
23+
assert.Equal(t, int64(1), total)
24+
}
25+
1226
func Test_IndexQuerier_FindsAllIndices(t *testing.T) {
1327
conn := postgres.NewIntegrationConnection(t)
1428

postgres/querier.go

+55
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,61 @@ func newQuerier[T any](conn *Connection, collection *clerk.Collection) *querier[
2424
}
2525
}
2626

27+
func (q *querier[T]) Count(
28+
ctx context.Context,
29+
query *clerk.Query[T],
30+
) (int64, error) {
31+
statBuilder := statementBuilder().
32+
Select("Count(*)").
33+
From(q.collection.Name)
34+
35+
condition, err := filtersToCondition("data", query.Filters...)
36+
if err != nil {
37+
return 0, err
38+
}
39+
40+
if condition != nil {
41+
statBuilder = statBuilder.Where(condition)
42+
}
43+
44+
stat, vals, err := statBuilder.
45+
ToSql()
46+
if err != nil {
47+
return 0, err
48+
}
49+
50+
queryCtx, cancel := q.conn.config.GetContext(ctx)
51+
defer cancel()
52+
53+
var total int64
54+
55+
if err := q.transactor.executeInTransactionIfAvailable(queryCtx, q.collection.Database, func(ctx context.Context) error {
56+
dbConn, release, err := q.conn.createOrUseDatabase(ctx, q.collection.Database.Name)
57+
defer release()
58+
if err != nil {
59+
return err
60+
}
61+
62+
rows, err := dbConn.Query(ctx, stat, vals...)
63+
if err != nil {
64+
return err
65+
}
66+
defer rows.Close()
67+
68+
for rows.Next() {
69+
if err := rows.Scan(&total); err != nil {
70+
return err
71+
}
72+
}
73+
74+
return nil
75+
}); err != nil {
76+
return 0, err
77+
}
78+
79+
return total, nil // @todo
80+
}
81+
2782
func (q *querier[T]) ExecuteQuery(
2883
ctx context.Context,
2984
query *clerk.Query[T],

0 commit comments

Comments
 (0)