Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ func executeOperation(ctx context.Context, client *mongo.Client, database string
return executeGetCollectionInfos(ctx, client, database, op)
case opGetIndexes:
return executeGetIndexes(ctx, client, database, op)
case opCountDocuments:
return executeCountDocuments(ctx, client, database, op)
case opEstimatedDocumentCount:
return executeEstimatedDocumentCount(ctx, client, database, op)
default:
return nil, &UnsupportedOperationError{
Operation: statement,
Expand Down Expand Up @@ -326,3 +330,49 @@ func executeGetIndexes(ctx context.Context, client *mongo.Client, database strin
RowCount: len(rows),
}, nil
}

// executeCountDocuments executes a db.collection.countDocuments() command.
func executeCountDocuments(ctx context.Context, client *mongo.Client, database string, op *mongoOperation) (*Result, error) {
collection := client.Database(database).Collection(op.collection)

filter := op.filter
if filter == nil {
filter = bson.D{}
}

opts := options.Count()
if op.hint != nil {
opts.SetHint(op.hint)
}
if op.limit != nil {
opts.SetLimit(*op.limit)
}
if op.skip != nil {
opts.SetSkip(*op.skip)
}

count, err := collection.CountDocuments(ctx, filter, opts)
if err != nil {
return nil, fmt.Errorf("count documents failed: %w", err)
}

return &Result{
Rows: []string{fmt.Sprintf("%d", count)},
RowCount: 1,
}, nil
}

// executeEstimatedDocumentCount executes a db.collection.estimatedDocumentCount() command.
func executeEstimatedDocumentCount(ctx context.Context, client *mongo.Client, database string, op *mongoOperation) (*Result, error) {
collection := client.Database(database).Collection(op.collection)

count, err := collection.EstimatedDocumentCount(ctx)
if err != nil {
return nil, fmt.Errorf("estimated document count failed: %w", err)
}

return &Result{
Rows: []string{fmt.Sprintf("%d", count)},
RowCount: 1,
}, nil
}
228 changes: 228 additions & 0 deletions executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1527,3 +1527,231 @@ func TestGetIndexesBracketNotation(t *testing.T) {
// Verify the _id index exists
require.Contains(t, result.Rows[0], `"name": "_id_"`)
}

func TestCountDocuments(t *testing.T) {
client, cleanup := setupTestContainer(t)
defer cleanup()

ctx := context.Background()

// Create a collection with documents
collection := client.Database("testdb").Collection("users")
_, err := collection.InsertMany(ctx, []any{
bson.M{"name": "alice", "age": 30},
bson.M{"name": "bob", "age": 25},
bson.M{"name": "charlie", "age": 35},
})
require.NoError(t, err)

gc := gomongo.NewClient(client)

// Test countDocuments without filter
result, err := gc.Execute(ctx, "testdb", "db.users.countDocuments()")
require.NoError(t, err)
require.NotNil(t, result)
require.Equal(t, 1, result.RowCount)
require.Equal(t, "3", result.Rows[0])
}

func TestCountDocumentsWithFilter(t *testing.T) {
client, cleanup := setupTestContainer(t)
defer cleanup()

ctx := context.Background()

// Create a collection with documents
collection := client.Database("testdb").Collection("users")
_, err := collection.InsertMany(ctx, []any{
bson.M{"name": "alice", "age": 30, "status": "active"},
bson.M{"name": "bob", "age": 25, "status": "inactive"},
bson.M{"name": "charlie", "age": 35, "status": "active"},
bson.M{"name": "diana", "age": 28, "status": "active"},
})
require.NoError(t, err)

gc := gomongo.NewClient(client)

// Test countDocuments with filter
result, err := gc.Execute(ctx, "testdb", `db.users.countDocuments({ status: "active" })`)
require.NoError(t, err)
require.NotNil(t, result)
require.Equal(t, 1, result.RowCount)
require.Equal(t, "3", result.Rows[0])

// Test with comparison operator
result, err = gc.Execute(ctx, "testdb", `db.users.countDocuments({ age: { $gte: 30 } })`)
require.NoError(t, err)
require.Equal(t, "2", result.Rows[0])
}

func TestCountDocumentsEmptyCollection(t *testing.T) {
client, cleanup := setupTestContainer(t)
defer cleanup()

ctx := context.Background()

gc := gomongo.NewClient(client)

// Test countDocuments on empty/non-existent collection
result, err := gc.Execute(ctx, "testdb", "db.users.countDocuments()")
require.NoError(t, err)
require.NotNil(t, result)
require.Equal(t, 1, result.RowCount)
require.Equal(t, "0", result.Rows[0])
}

func TestCountDocumentsWithEmptyFilter(t *testing.T) {
client, cleanup := setupTestContainer(t)
defer cleanup()

ctx := context.Background()

// Create a collection with documents
collection := client.Database("testdb").Collection("items")
_, err := collection.InsertMany(ctx, []any{
bson.M{"item": "a"},
bson.M{"item": "b"},
})
require.NoError(t, err)

gc := gomongo.NewClient(client)

// Test countDocuments with empty filter {}
result, err := gc.Execute(ctx, "testdb", "db.items.countDocuments({})")
require.NoError(t, err)
require.NotNil(t, result)
require.Equal(t, "2", result.Rows[0])
}

func TestCountDocumentsWithOptions(t *testing.T) {
client, cleanup := setupTestContainer(t)
defer cleanup()

ctx := context.Background()

// Create a collection with documents
collection := client.Database("testdb").Collection("users")
_, err := collection.InsertMany(ctx, []any{
bson.M{"name": "alice", "age": 30},
bson.M{"name": "bob", "age": 25},
bson.M{"name": "charlie", "age": 35},
bson.M{"name": "diana", "age": 28},
bson.M{"name": "eve", "age": 32},
})
require.NoError(t, err)

gc := gomongo.NewClient(client)

// Test with limit option
result, err := gc.Execute(ctx, "testdb", `db.users.countDocuments({}, { limit: 3 })`)
require.NoError(t, err)
require.Equal(t, "3", result.Rows[0])

// Test with skip option
result, err = gc.Execute(ctx, "testdb", `db.users.countDocuments({}, { skip: 2 })`)
require.NoError(t, err)
require.Equal(t, "3", result.Rows[0])

// Test with both limit and skip
result, err = gc.Execute(ctx, "testdb", `db.users.countDocuments({}, { skip: 1, limit: 2 })`)
require.NoError(t, err)
require.Equal(t, "2", result.Rows[0])
}

func TestCountDocumentsWithHint(t *testing.T) {
client, cleanup := setupTestContainer(t)
defer cleanup()

ctx := context.Background()

// Create a collection with documents and an index
collection := client.Database("testdb").Collection("users")
_, err := collection.InsertMany(ctx, []any{
bson.M{"name": "alice", "status": "active"},
bson.M{"name": "bob", "status": "inactive"},
bson.M{"name": "charlie", "status": "active"},
})
require.NoError(t, err)

// Create an index on status
_, err = collection.Indexes().CreateOne(ctx, mongo.IndexModel{
Keys: bson.D{{Key: "status", Value: 1}},
})
require.NoError(t, err)

gc := gomongo.NewClient(client)

// Test with hint using index name
result, err := gc.Execute(ctx, "testdb", `db.users.countDocuments({ status: "active" }, { hint: "status_1" })`)
require.NoError(t, err)
require.Equal(t, "2", result.Rows[0])

// Test with hint using index specification document
result, err = gc.Execute(ctx, "testdb", `db.users.countDocuments({ status: "active" }, { hint: { status: 1 } })`)
require.NoError(t, err)
require.Equal(t, "2", result.Rows[0])
}

func TestEstimatedDocumentCount(t *testing.T) {
client, cleanup := setupTestContainer(t)
defer cleanup()

ctx := context.Background()

// Create a collection with documents
collection := client.Database("testdb").Collection("users")
_, err := collection.InsertMany(ctx, []any{
bson.M{"name": "alice"},
bson.M{"name": "bob"},
bson.M{"name": "charlie"},
})
require.NoError(t, err)

gc := gomongo.NewClient(client)

// Test estimatedDocumentCount
result, err := gc.Execute(ctx, "testdb", "db.users.estimatedDocumentCount()")
require.NoError(t, err)
require.NotNil(t, result)
require.Equal(t, 1, result.RowCount)
require.Equal(t, "3", result.Rows[0])
}

func TestEstimatedDocumentCountEmptyCollection(t *testing.T) {
client, cleanup := setupTestContainer(t)
defer cleanup()

ctx := context.Background()

gc := gomongo.NewClient(client)

// Test estimatedDocumentCount on empty/non-existent collection
result, err := gc.Execute(ctx, "testdb", "db.users.estimatedDocumentCount()")
require.NoError(t, err)
require.NotNil(t, result)
require.Equal(t, 1, result.RowCount)
require.Equal(t, "0", result.Rows[0])
}

func TestEstimatedDocumentCountWithEmptyOptions(t *testing.T) {
client, cleanup := setupTestContainer(t)
defer cleanup()

ctx := context.Background()

// Create a collection with documents
collection := client.Database("testdb").Collection("items")
_, err := collection.InsertMany(ctx, []any{
bson.M{"item": "a"},
bson.M{"item": "b"},
})
require.NoError(t, err)

gc := gomongo.NewClient(client)

// Test estimatedDocumentCount with empty options {}
result, err := gc.Execute(ctx, "testdb", "db.items.estimatedDocumentCount({})")
require.NoError(t, err)
require.NotNil(t, result)
require.Equal(t, "2", result.Rows[0])
}
Loading