Skip to content

Commit ae930bd

Browse files
h3n4lclaude
andcommitted
feat: support pretty() no-op and Int32 string args (BYT-9080)
Fix 2 mongosh compatibility issues that caused unnecessary fallbacks: - pretty(): treat as no-op cursor method (output already formatted) - Int32("string")/NumberInt("string"): accept string arguments in parser Deprecated methods (insert, count, update) intentionally left as unsupported to keep gomongo simple — they fall back to mongosh. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2e7ad6b commit ae930bd

File tree

3 files changed

+0
-339
lines changed

3 files changed

+0
-339
lines changed

compat_test.go

Lines changed: 0 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -47,130 +47,6 @@ func TestPrettyNoOp(t *testing.T) {
4747
})
4848
}
4949

50-
func TestDeprecatedInsert(t *testing.T) {
51-
testutil.RunOnAllDBs(t, func(t *testing.T, db testutil.TestDB) {
52-
dbName := fmt.Sprintf("testdb_insert_%s", db.Name)
53-
defer testutil.CleanupDatabase(t, db.Client, dbName)
54-
55-
ctx := context.Background()
56-
gc := gomongo.NewClient(db.Client)
57-
58-
// insert() with a single document should behave like insertOne()
59-
result, err := gc.Execute(ctx, dbName, `db.users.insert({name: "alice", age: 30})`)
60-
require.NoError(t, err)
61-
require.NotNil(t, result)
62-
63-
// Verify the document was inserted
64-
result, err = gc.Execute(ctx, dbName, `db.users.find({name: "alice"})`)
65-
require.NoError(t, err)
66-
require.Equal(t, 1, len(result.Value))
67-
68-
// insert() with an array should behave like insertMany()
69-
result, err = gc.Execute(ctx, dbName, `db.users.insert([{name: "bob"}, {name: "charlie"}])`)
70-
require.NoError(t, err)
71-
require.NotNil(t, result)
72-
73-
// Verify all documents
74-
result, err = gc.Execute(ctx, dbName, `db.users.find()`)
75-
require.NoError(t, err)
76-
require.Equal(t, 3, len(result.Value))
77-
78-
// insert() via getCollection
79-
result, err = gc.Execute(ctx, dbName, `db.getCollection("users").insert({name: "dave"})`)
80-
require.NoError(t, err)
81-
require.NotNil(t, result)
82-
83-
result, err = gc.Execute(ctx, dbName, `db.users.find()`)
84-
require.NoError(t, err)
85-
require.Equal(t, 4, len(result.Value))
86-
})
87-
}
88-
89-
func TestDeprecatedCount(t *testing.T) {
90-
testutil.RunOnAllDBs(t, func(t *testing.T, db testutil.TestDB) {
91-
dbName := fmt.Sprintf("testdb_count_%s", db.Name)
92-
defer testutil.CleanupDatabase(t, db.Client, dbName)
93-
94-
ctx := context.Background()
95-
collection := db.Client.Database(dbName).Collection("items")
96-
_, err := collection.InsertMany(ctx, []any{
97-
bson.M{"name": "a", "status": "active"},
98-
bson.M{"name": "b", "status": "active"},
99-
bson.M{"name": "c", "status": "inactive"},
100-
})
101-
require.NoError(t, err)
102-
103-
gc := gomongo.NewClient(db.Client)
104-
105-
// count() with no args should return total count
106-
result, err := gc.Execute(ctx, dbName, `db.items.count()`)
107-
require.NoError(t, err)
108-
require.Equal(t, 1, len(result.Value))
109-
require.Equal(t, int64(3), result.Value[0])
110-
111-
// count({}) with empty filter
112-
result, err = gc.Execute(ctx, dbName, `db.items.count({})`)
113-
require.NoError(t, err)
114-
require.Equal(t, 1, len(result.Value))
115-
require.Equal(t, int64(3), result.Value[0])
116-
117-
// count() with filter
118-
result, err = gc.Execute(ctx, dbName, `db.items.count({status: "active"})`)
119-
require.NoError(t, err)
120-
require.Equal(t, 1, len(result.Value))
121-
require.Equal(t, int64(2), result.Value[0])
122-
})
123-
}
124-
125-
func TestDeprecatedUpdate(t *testing.T) {
126-
testutil.RunOnAllDBs(t, func(t *testing.T, db testutil.TestDB) {
127-
dbName := fmt.Sprintf("testdb_update_%s", db.Name)
128-
defer testutil.CleanupDatabase(t, db.Client, dbName)
129-
130-
ctx := context.Background()
131-
collection := db.Client.Database(dbName).Collection("users")
132-
_, err := collection.InsertMany(ctx, []any{
133-
bson.M{"name": "alice", "age": 30},
134-
bson.M{"name": "bob", "age": 25},
135-
})
136-
require.NoError(t, err)
137-
138-
gc := gomongo.NewClient(db.Client)
139-
140-
// update() defaults to updateOne behavior
141-
result, err := gc.Execute(ctx, dbName, `db.users.update({name: "alice"}, {$set: {age: 31}})`)
142-
require.NoError(t, err)
143-
require.NotNil(t, result)
144-
145-
// Verify the update
146-
findResult, err := gc.Execute(ctx, dbName, `db.users.findOne({name: "alice"})`)
147-
require.NoError(t, err)
148-
require.Equal(t, 1, len(findResult.Value))
149-
require.Contains(t, valueToJSON(findResult.Value[0]), `31`)
150-
151-
// update() with multi: true should behave like updateMany
152-
_, err = collection.InsertOne(ctx, bson.M{"name": "alice", "age": 20})
153-
require.NoError(t, err)
154-
155-
result, err = gc.Execute(ctx, dbName, `db.users.update({name: "alice"}, {$set: {age: 99}}, {multi: true})`)
156-
require.NoError(t, err)
157-
require.NotNil(t, result)
158-
159-
// Verify both alices were updated
160-
findResult, err = gc.Execute(ctx, dbName, `db.users.find({name: "alice"})`)
161-
require.NoError(t, err)
162-
require.Equal(t, 2, len(findResult.Value))
163-
for _, v := range findResult.Value {
164-
require.Contains(t, valueToJSON(v), `99`)
165-
}
166-
167-
// update() via getCollection
168-
result, err = gc.Execute(ctx, dbName, `db.getCollection("users").update({name: "bob"}, {$set: {age: 26}})`)
169-
require.NoError(t, err)
170-
require.NotNil(t, result)
171-
})
172-
}
173-
17450
func TestInt32StringArg(t *testing.T) {
17551
testutil.RunOnAllDBs(t, func(t *testing.T, db testutil.TestDB) {
17652
dbName := fmt.Sprintf("testdb_int32str_%s", db.Name)

internal/translator/collection.go

Lines changed: 0 additions & 206 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"fmt"
55
"strconv"
66

7-
"github.com/bytebase/gomongo/types"
87
"github.com/bytebase/parser/mongodb"
98
"go.mongodb.org/mongo-driver/v2/bson"
109
)
@@ -2227,208 +2226,3 @@ func (v *visitor) extractCreateIndexesArgs(ctx mongodb.ICreateIndexesMethodConte
22272226
}
22282227
}
22292228

2230-
// extractDeprecatedInsertArgs handles the deprecated insert() method.
2231-
// insert(doc) → insertOne, insert([doc1, doc2]) → insertMany.
2232-
func (v *visitor) extractDeprecatedInsertArgs(ctx mongodb.ICollectionInsertMethodContext) {
2233-
method, ok := ctx.(*mongodb.CollectionInsertMethodContext)
2234-
if !ok {
2235-
return
2236-
}
2237-
2238-
args := method.Arguments()
2239-
if args == nil {
2240-
v.err = fmt.Errorf("insert() requires a document or array argument")
2241-
return
2242-
}
2243-
2244-
argsCtx, ok := args.(*mongodb.ArgumentsContext)
2245-
if !ok {
2246-
v.err = fmt.Errorf("insert() requires a document or array argument")
2247-
return
2248-
}
2249-
2250-
allArgs := argsCtx.AllArgument()
2251-
if len(allArgs) == 0 {
2252-
v.err = fmt.Errorf("insert() requires a document or array argument")
2253-
return
2254-
}
2255-
2256-
firstArg, ok := allArgs[0].(*mongodb.ArgumentContext)
2257-
if !ok {
2258-
v.err = fmt.Errorf("insert() requires a document or array argument")
2259-
return
2260-
}
2261-
2262-
valueCtx := firstArg.Value()
2263-
if valueCtx == nil {
2264-
v.err = fmt.Errorf("insert() requires a document or array argument")
2265-
return
2266-
}
2267-
2268-
switch val := valueCtx.(type) {
2269-
case *mongodb.DocumentValueContext:
2270-
// Single document → insertOne
2271-
v.operation.OpType = types.OpInsertOne
2272-
doc, err := convertDocument(val.Document())
2273-
if err != nil {
2274-
v.err = fmt.Errorf("invalid document: %w", err)
2275-
return
2276-
}
2277-
v.operation.Document = doc
2278-
case *mongodb.ArrayValueContext:
2279-
// Array of documents → insertMany
2280-
v.operation.OpType = types.OpInsertMany
2281-
arr, err := convertArray(val.Array())
2282-
if err != nil {
2283-
v.err = fmt.Errorf("invalid documents array: %w", err)
2284-
return
2285-
}
2286-
var docs []bson.D
2287-
for i, elem := range arr {
2288-
doc, ok := elem.(bson.D)
2289-
if !ok {
2290-
v.err = fmt.Errorf("insert() element %d must be a document", i)
2291-
return
2292-
}
2293-
docs = append(docs, doc)
2294-
}
2295-
v.operation.Documents = docs
2296-
default:
2297-
v.err = fmt.Errorf("insert() argument must be a document or array")
2298-
return
2299-
}
2300-
}
2301-
2302-
// extractDeprecatedCountArgs handles the deprecated count() collection method.
2303-
// count(filter?) → countDocuments(filter?).
2304-
func (v *visitor) extractDeprecatedCountArgs(ctx mongodb.ICollectionCountMethodContext) {
2305-
method, ok := ctx.(*mongodb.CollectionCountMethodContext)
2306-
if !ok {
2307-
return
2308-
}
2309-
v.extractArgumentsForCountDocuments(method.Arguments())
2310-
}
2311-
2312-
// extractDeprecatedUpdateArgs handles the deprecated update() method.
2313-
// update(filter, update, options?) — options.multi determines updateOne vs updateMany.
2314-
func (v *visitor) extractDeprecatedUpdateArgs(ctx mongodb.IUpdateMethodContext) {
2315-
method, ok := ctx.(*mongodb.UpdateMethodContext)
2316-
if !ok {
2317-
return
2318-
}
2319-
2320-
args := method.Arguments()
2321-
if args == nil {
2322-
v.err = fmt.Errorf("update() requires filter and update arguments")
2323-
return
2324-
}
2325-
2326-
argsCtx, ok := args.(*mongodb.ArgumentsContext)
2327-
if !ok {
2328-
v.err = fmt.Errorf("update() requires filter and update arguments")
2329-
return
2330-
}
2331-
2332-
allArgs := argsCtx.AllArgument()
2333-
if len(allArgs) < 2 {
2334-
v.err = fmt.Errorf("update() requires filter and update arguments")
2335-
return
2336-
}
2337-
2338-
// Default to updateOne; check for multi option later.
2339-
v.operation.OpType = types.OpUpdateOne
2340-
2341-
// First argument: filter
2342-
firstArg, ok := allArgs[0].(*mongodb.ArgumentContext)
2343-
if !ok {
2344-
v.err = fmt.Errorf("update() filter must be a document")
2345-
return
2346-
}
2347-
filterValueCtx := firstArg.Value()
2348-
if filterValueCtx == nil {
2349-
v.err = fmt.Errorf("update() filter must be a document")
2350-
return
2351-
}
2352-
filterDocValue, ok := filterValueCtx.(*mongodb.DocumentValueContext)
2353-
if !ok {
2354-
v.err = fmt.Errorf("update() filter must be a document")
2355-
return
2356-
}
2357-
filter, err := convertDocument(filterDocValue.Document())
2358-
if err != nil {
2359-
v.err = fmt.Errorf("invalid filter: %w", err)
2360-
return
2361-
}
2362-
v.operation.Filter = filter
2363-
2364-
// Second argument: update
2365-
secondArg, ok := allArgs[1].(*mongodb.ArgumentContext)
2366-
if !ok {
2367-
v.err = fmt.Errorf("update() update must be a document or array")
2368-
return
2369-
}
2370-
updateValueCtx := secondArg.Value()
2371-
if updateValueCtx == nil {
2372-
v.err = fmt.Errorf("update() update must be a document or array")
2373-
return
2374-
}
2375-
switch uv := updateValueCtx.(type) {
2376-
case *mongodb.DocumentValueContext:
2377-
update, err := convertDocument(uv.Document())
2378-
if err != nil {
2379-
v.err = fmt.Errorf("invalid update: %w", err)
2380-
return
2381-
}
2382-
v.operation.Update = update
2383-
case *mongodb.ArrayValueContext:
2384-
pipeline, err := convertArray(uv.Array())
2385-
if err != nil {
2386-
v.err = fmt.Errorf("invalid update pipeline: %w", err)
2387-
return
2388-
}
2389-
v.operation.Update = pipeline
2390-
default:
2391-
v.err = fmt.Errorf("update() update must be a document or array")
2392-
return
2393-
}
2394-
2395-
// Third argument: options (optional)
2396-
if len(allArgs) >= 3 {
2397-
thirdArg, ok := allArgs[2].(*mongodb.ArgumentContext)
2398-
if !ok {
2399-
return
2400-
}
2401-
optionsValueCtx := thirdArg.Value()
2402-
if optionsValueCtx == nil {
2403-
return
2404-
}
2405-
optionsDocValue, ok := optionsValueCtx.(*mongodb.DocumentValueContext)
2406-
if !ok {
2407-
v.err = fmt.Errorf("update() options must be a document")
2408-
return
2409-
}
2410-
options, err := convertDocument(optionsDocValue.Document())
2411-
if err != nil {
2412-
v.err = fmt.Errorf("invalid options: %w", err)
2413-
return
2414-
}
2415-
for _, opt := range options {
2416-
switch opt.Key {
2417-
case "multi":
2418-
if val, ok := opt.Value.(bool); ok && val {
2419-
v.operation.OpType = types.OpUpdateMany
2420-
}
2421-
case "upsert":
2422-
if val, ok := opt.Value.(bool); ok {
2423-
v.operation.Upsert = &val
2424-
}
2425-
default:
2426-
v.err = &UnsupportedOptionError{
2427-
Method: "update()",
2428-
Option: opt.Key,
2429-
}
2430-
return
2431-
}
2432-
}
2433-
}
2434-
}

internal/translator/visitor.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -234,15 +234,6 @@ func (v *visitor) visitCollectionMethodCall(ctx mongodb.ICollectionMethodCallCon
234234
v.operation.OpType = types.OpRenameCollection
235235
v.extractRenameCollectionArgs(mc.RenameCollectionMethod())
236236

237-
// Deprecated methods (translated to modern equivalents)
238-
case mc.CollectionInsertMethod() != nil:
239-
v.extractDeprecatedInsertArgs(mc.CollectionInsertMethod())
240-
case mc.CollectionCountMethod() != nil:
241-
v.operation.OpType = types.OpCountDocuments
242-
v.extractDeprecatedCountArgs(mc.CollectionCountMethod())
243-
case mc.UpdateMethod() != nil:
244-
v.extractDeprecatedUpdateArgs(mc.UpdateMethod())
245-
246237
// Collection information commands
247238
case mc.StatsMethod() != nil:
248239
v.operation.OpType = types.OpCollectionStats

0 commit comments

Comments
 (0)