Skip to content

Commit 53c998e

Browse files
h3n4lclaude
andauthored
feat: implement MVP of gomongo library (#1)
* chore: claude.md and readme.md * feat: implement MVP of gomongo library Implement the initial version of gomongo - a Go library that parses MongoDB shell syntax and executes commands using the native Go MongoDB driver. MVP scope (matching bytebase/bytebase#18883): - db.collection.find() - returns all documents (filter parsed but ignored) - Collection access via dot notation, bracket notation, and getCollection() - Extended JSON (Relaxed) output format - Parse errors with line/column information - Unsupported operation errors with hints Not yet supported: - Filter support for find() - findOne() - Cursor modifiers (sort, limit, skip, projection) - Helper functions (ObjectId, ISODate, UUID, etc.) - Shell commands (show dbs, show collections) Also includes: - GitHub Actions workflows for tests and linting - Integration tests using testcontainers - ANTLR replace directive for Bytebase's optimized fork 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 236c51e commit 53c998e

11 files changed

Lines changed: 1066 additions & 0 deletions

File tree

.github/workflows/lint.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Lint
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
13+
cancel-in-progress: true
14+
15+
jobs:
16+
golangci-lint:
17+
runs-on: ubuntu-latest
18+
timeout-minutes: 10
19+
steps:
20+
- uses: actions/checkout@v4
21+
- uses: actions/setup-go@v5
22+
with:
23+
go-version: '1.24'
24+
- name: golangci-lint
25+
uses: golangci/golangci-lint-action@v6
26+
with:
27+
version: v1.64.8
28+
args: --verbose --timeout 5m --allow-parallel-runners

.github/workflows/tests.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
13+
cancel-in-progress: true
14+
15+
jobs:
16+
test:
17+
runs-on: ubuntu-latest
18+
timeout-minutes: 15
19+
steps:
20+
- uses: actions/checkout@v4
21+
- uses: actions/setup-go@v5
22+
with:
23+
go-version: '1.24'
24+
- name: Verify go.mod is tidy
25+
run: |
26+
go mod tidy
27+
git diff --exit-code
28+
- name: Run tests
29+
run: go test -v -race -timeout 10m ./...

CLAUDE.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
This file provides guidance to AI coding assistants when working with code in this repository.
2+
3+
## Project Overview
4+
5+
gomongo is a Go library that parses MongoDB shell syntax and executes commands using the native Go MongoDB driver. It uses the ANTLR-based parser from `github.com/bytebase/parser/mongodb`.
6+
7+
## Project Structure
8+
9+
```
10+
gomongo/
11+
├── client.go # Public API: Client, NewClient, Execute
12+
├── executor.go # Parse → Translate → Execute pipeline
13+
├── translator.go # Walk ANTLR parse tree, build driver operations
14+
├── helper_functions.go # Convert ObjectId(), ISODate(), etc. to BSON
15+
├── errors.go # Error types (ParseError, UnsupportedOperationError)
16+
├── executor_test.go # Integration tests with testcontainers
17+
└── go.mod
18+
```
19+
20+
## Development Workflow
21+
22+
**ALWAYS follow these steps after making code changes:**
23+
24+
1. **Format** — Run `gofmt -w` on modified files
25+
2. **Lint** — Run `golangci-lint run --allow-parallel-runners` to catch issues
26+
- Run repeatedly until there are no issues (linter has max-issues limit)
27+
3. **Auto-fix** — Use `golangci-lint run --fix --allow-parallel-runners` to fix issues automatically
28+
4. **Test** — Run `go test -v ./...` before committing
29+
5. **Build** — Run `go build ./...` to verify compilation
30+
31+
## Build/Test Commands
32+
33+
```bash
34+
# Build
35+
go build ./...
36+
37+
# Run all tests
38+
go test -v ./...
39+
40+
# Run single test
41+
go test -v -count=1 -run ^TestFunctionName$
42+
43+
# Run tests with race detection
44+
go test -race -v ./...
45+
46+
# Lint
47+
golangci-lint run --allow-parallel-runners
48+
49+
# Format
50+
gofmt -w .
51+
```
52+
53+
## Code Style
54+
55+
### General
56+
57+
- Follow [Google Go Style Guide](https://google.github.io/styleguide/go/)
58+
- Write clean, minimal code; fewer lines is better
59+
- Prioritize simplicity for effective and maintainable software
60+
- Only include comments that are essential to understanding functionality
61+
62+
### Go
63+
64+
- Use standard Go error handling with detailed error messages
65+
- Always use `defer` for resource cleanup like `cursor.Close()`
66+
- Avoid using `defer` inside loops — use IIFE or scope properly
67+
- Use `any` instead of `interface{}` (Go 1.18+)
68+
69+
### Naming
70+
71+
- Use American English
72+
- Avoid plurals like "xxxList"
73+
74+
### Imports
75+
76+
- Use organized imports (sorted by import path)
77+
- Group: stdlib, external, internal
78+
79+
### Error Handling
80+
81+
- Be explicit but concise about error cases
82+
- Wrap errors with context using `errors.Wrap()` or `fmt.Errorf("...: %w", err)`
83+
84+
## Common Go Lint Rules
85+
86+
- **Unused Parameters** — Prefix unused parameters with underscore (e.g., `func foo(_ *Bar)`)
87+
- **Modern Go Conventions** — Use `any` instead of `interface{}`
88+
- **Confusing Naming** — Avoid similar names that differ only by capitalization
89+
- **Identical Branches** — Don't use if-else branches that contain identical code
90+
- **Function Receivers** — Don't create unnecessary function receivers
91+
- **Export Rules** — Only export functions and types that need to be used outside the package
92+
93+
## Testing
94+
95+
### Unit Tests
96+
97+
- Test translator logic with mock parse trees
98+
- Test helper function conversions (ObjectId, ISODate, etc.)
99+
- Test error message formatting
100+
101+
### Integration Tests
102+
103+
Use testcontainers for MongoDB:
104+
105+
```go
106+
func TestFind(t *testing.T) {
107+
// Start MongoDB container
108+
// Insert test documents
109+
// Execute query via gomongo
110+
// Verify results
111+
}
112+
```
113+
114+
## Dependencies
115+
116+
- `go.mongodb.org/mongo-driver/v2` — Official MongoDB Go driver
117+
- `github.com/bytebase/parser` — ANTLR-based MongoDB shell parser
118+
- `github.com/antlr4-go/antlr/v4` — ANTLR runtime for Go
119+
120+
## Output Format
121+
122+
All query results are returned as Extended JSON (Relaxed) format using `bson.MarshalExtJSONIndent()`. This ensures:
123+
- Valid JSON that can be parsed by `JSON.parse()`
124+
- Type information preserved for BSON types
125+
- Consistent output format
126+
127+
## Pull Request Guidelines
128+
129+
- **Description** — Clearly describe what the PR changes and why
130+
- **Testing** — Include information about how the changes were tested
131+
- **Breaking Changes** — Clearly mark any breaking API changes

README.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# gomongo
2+
3+
Go library for parsing and executing MongoDB shell syntax using the native MongoDB driver.
4+
5+
## Overview
6+
7+
gomongo parses MongoDB shell commands (e.g., `db.users.find()`) and executes them using the Go MongoDB driver, eliminating the need for external mongosh CLI.
8+
9+
## Status
10+
11+
**MVP v0.1.0** - Basic functionality implemented:
12+
13+
| Feature | Status |
14+
|---------|--------|
15+
| `find()` without filter | Supported |
16+
| `find()` with filter | Parsed but filter ignored (returns all documents) |
17+
| `findOne()` | Not yet supported |
18+
| Cursor modifiers (sort, limit, skip, projection) | Parsed but ignored |
19+
| Helper functions (ObjectId, ISODate, etc.) | Not yet supported |
20+
| Shell commands (show dbs, show collections) | Not yet supported |
21+
| Collection access (dot, bracket, getCollection) | Supported |
22+
23+
## Installation
24+
25+
```bash
26+
go get github.com/bytebase/gomongo
27+
```
28+
29+
## Usage
30+
31+
```go
32+
package main
33+
34+
import (
35+
"context"
36+
"fmt"
37+
38+
"github.com/bytebase/gomongo"
39+
"go.mongodb.org/mongo-driver/v2/mongo"
40+
"go.mongodb.org/mongo-driver/v2/mongo/options"
41+
)
42+
43+
func main() {
44+
// Connect to MongoDB
45+
client, err := mongo.Connect(options.Client().ApplyURI("mongodb://localhost:27017"))
46+
if err != nil {
47+
panic(err)
48+
}
49+
defer client.Disconnect(context.Background())
50+
51+
// Create gomongo client
52+
gc := gomongo.NewClient(client)
53+
54+
// Execute MongoDB shell command
55+
ctx := context.Background()
56+
result, err := gc.Execute(ctx, "mydb", `db.users.find()`)
57+
if err != nil {
58+
panic(err)
59+
}
60+
61+
// Print results (Extended JSON format)
62+
for _, row := range result.Rows {
63+
fmt.Println(row)
64+
}
65+
}
66+
```
67+
68+
## Supported Operations (MVP)
69+
70+
| Category | Operation | Status |
71+
|----------|-----------|--------|
72+
| **Read** | `find()` | Supported (no filter) |
73+
| | `findOne()` | Not yet supported |
74+
| **Collection Access** | dot notation | Supported (`db.users`) |
75+
| | bracket notation | Supported (`db["user-logs"]`) |
76+
| | getCollection | Supported (`db.getCollection("users")`) |
77+
78+
## Output Format
79+
80+
Results are returned in Extended JSON (Relaxed) format:
81+
82+
```json
83+
{
84+
"_id": {"$oid": "507f1f77bcf86cd799439011"},
85+
"name": "Alice",
86+
"age": 30,
87+
"createdAt": {"$date": "2024-01-01T00:00:00Z"}
88+
}
89+
```
90+
91+
## Roadmap
92+
93+
Future versions will add:
94+
- Filter support for `find()` and `findOne()`
95+
- Cursor modifiers (sort, limit, skip, projection)
96+
- Helper functions (ObjectId, ISODate, UUID, NumberLong, etc.)
97+
- Shell commands (show dbs, show collections)
98+
99+
## License
100+
101+
Apache License 2.0

client.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package gomongo
2+
3+
import (
4+
"context"
5+
6+
"go.mongodb.org/mongo-driver/v2/mongo"
7+
)
8+
9+
// Client wraps a MongoDB client and provides query execution.
10+
type Client struct {
11+
client *mongo.Client
12+
}
13+
14+
// NewClient creates a new gomongo client from an existing MongoDB client.
15+
func NewClient(client *mongo.Client) *Client {
16+
return &Client{client: client}
17+
}
18+
19+
// Result represents query execution results.
20+
type Result struct {
21+
Rows []string
22+
RowCount int
23+
Statement string
24+
}
25+
26+
// Execute parses and executes a MongoDB shell statement.
27+
// Returns results as Extended JSON (Relaxed) strings.
28+
func (c *Client) Execute(ctx context.Context, database, statement string) (*Result, error) {
29+
return execute(ctx, c.client, database, statement)
30+
}

errors.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package gomongo
2+
3+
import "fmt"
4+
5+
// ParseError represents a syntax error during parsing.
6+
type ParseError struct {
7+
Line int
8+
Column int
9+
Message string
10+
Found string
11+
Expected string
12+
}
13+
14+
func (e *ParseError) Error() string {
15+
if e.Found != "" && e.Expected != "" {
16+
return fmt.Sprintf("parse error at line %d, column %d: found %q, expected %s", e.Line, e.Column, e.Found, e.Expected)
17+
}
18+
return fmt.Sprintf("parse error at line %d, column %d: %s", e.Line, e.Column, e.Message)
19+
}
20+
21+
// UnsupportedOperationError represents an unsupported operation.
22+
type UnsupportedOperationError struct {
23+
Operation string
24+
Hint string
25+
}
26+
27+
func (e *UnsupportedOperationError) Error() string {
28+
if e.Hint != "" {
29+
return fmt.Sprintf("unsupported operation %q: %s", e.Operation, e.Hint)
30+
}
31+
return fmt.Sprintf("unsupported operation %q", e.Operation)
32+
}

0 commit comments

Comments
 (0)