Skip to content

feat: support toArray no-op, runCommand, explain on read ops, and empty-statement no-op#29

Merged
h3n4l merged 1 commit intomainfrom
support-toarray-noop
May 9, 2026
Merged

feat: support toArray no-op, runCommand, explain on read ops, and empty-statement no-op#29
h3n4l merged 1 commit intomainfrom
support-toarray-noop

Conversation

@h3n4l
Copy link
Copy Markdown
Member

@h3n4l h3n4l commented May 8, 2026

Summary

Four mongosh compatibility fixes drawn from production gomongoFallback telemetry. Combined with omni#127 (trailing-comma tolerance), these clear ~13 of the 25 deprecation blockers from the most recent batch of fallback events, with no regressions on the others. None of the four require omni#127 to be merged first — this PR stands on the released omni.

Changes

toArray() — no-op cursor terminator

Joins pretty() in translateCursorMethod as a no-op. Cursors are already materialized into Result.Value; the chained call is purely cosmetic in mongo shell. Covers wild shape db.coll.find({…}).limit(N).toArray().

db.runCommand({…}) — generic escape hatch

Adds OpRunCommand, an extractRunCommandArgs validator (1 doc, non-empty), and an executeRunCommand thin wrapper that hands the body to mongo.Database.RunCommand and returns the bson.D response unchanged. Result-shape doc in client.go updated.

explain() on read ops + aggregate {explain: true} option

A trailing .explain() / .explain(\"verbosity\") on find / aggregate / count / distinct rewrites the operation into {explain: <innerCommand>, verbosity: <v>} and runs it as OpRunCommand. The aggregate-option form db.coll.aggregate([…], {explain: true}) routes to the same code path. Verbosity defaults to \"queryPlanner\" and is validated against the three accepted values. Write-op explain (update / delete) is intentionally out of scope.

Empty / comment-only input — no-op

Pure-comment statements (e.g., // db.coll.update(...)) previously errored with empty statement: …. mongosh treats them as no-op successes; gomongo now does too via a new OpNoOp returned by the translator and handled by the executor as an empty Result.

Test plan

  • TestToArrayNoOp covers the four wild shapes (find, find+limit, find+filter+projection, aggregate)
  • TestRunCommand covers ping, the legacy count command (the wild case), empty body rejection, wrong arity rejection
  • TestExplain covers post-method on find / aggregate / count(+filter) / count() / distinct, both verbosities, the aggregate-option form, plus invalid verbosity / wrong arity / explain on unsupported op type
  • TestCommentOnlyStatementIsNoOp covers line comments, multi-line comments, block comments, whitespace-only, empty string, and comment-followed-by-real-statement
  • All-92 historical wild-statement harness: 32 → 41 succeed, 0 regressions (45 → with omni#127 trailing-comma also)

🤖 Generated with Claude Code

…ty-statement no-op

Four mongosh compatibility fixes drawn from production gomongoFallback
telemetry. Each one was fronting a real fallback to mongosh; with these
in place ~9 distinct user-query shapes that previously fell back now run
natively in gomongo.

- toArray(): treat as a no-op cursor terminator alongside pretty(). The
  result rows are already materialized into Result.Value, so the chained
  call is purely cosmetic in mongo shell. Wild shape:
  db.coll.find({…}).limit(N).toArray().

- db.runCommand({…}): generic escape hatch. Adds OpRunCommand and a
  thin executor that hands the command body to mongo.Database.RunCommand
  and returns the bson.D server response unchanged. Used in the wild
  for the legacy `count` command form, and useful for any server
  command without a dedicated typed wrapper.

- explain() on read operations: a trailing .explain() / .explain(verbosity)
  on find / aggregate / count / distinct rewrites the operation into
  {explain: <innerCommand>, verbosity: <v>} run via runCommand. Also
  accepts the aggregate-option form db.coll.aggregate([…], {explain: true}).
  Verbosity defaults to "queryPlanner" and is validated against the three
  accepted values. Write-op explain (update/delete) is left out for now.

- Empty / comment-only input: gomongo previously rejected pure-comment
  statements with "empty statement: …". mongosh treats them as no-op
  successes; now gomongo does too via a new OpNoOp that the executor
  handles by returning an empty Result.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 8, 2026 22:32
@h3n4l h3n4l requested a review from a team as a code owner May 8, 2026 22:32
@h3n4l h3n4l enabled auto-merge (squash) May 8, 2026 22:34
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves gomongo’s mongosh compatibility by adding support for no-op cursor terminators, a generic db.runCommand() escape hatch, explain() for read operations (including aggregate {explain: true}), and treating comment-only/empty input as a no-op operation.

Changes:

  • Add OpNoOp handling so comment-only / whitespace-only input returns an empty successful result.
  • Add OpRunCommand + argument validation + executor implementation to pass through db.runCommand({...}).
  • Add explain() rewriting for supported read ops into an explain wrapper executed via runCommand, and treat toArray() as a no-op cursor terminator.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
types/operation_type.go Adds new operation types (OpNoOp, OpRunCommand).
internal/translator/types.go Extends Operation with Command and Explain fields.
internal/translator/translator.go Treats empty/comment-only input as OpNoOp instead of erroring.
internal/translator/translate.go Adds runCommand, toArray no-op, and explain tagging + rewrite hook.
internal/translator/explain.go Implements explain tagging, validation, and command-rewrite builders.
internal/translator/database.go Adds runCommand() argument extraction/validation.
internal/translator/collection.go Adds aggregate {explain: true} option support.
internal/executor/executor.go Executes OpNoOp locally and routes OpRunCommand to executor.
internal/executor/admin.go Implements executeRunCommand() using existing runCommand() helper.
client.go Documents new OpRunCommand and OpNoOp result shapes.
collection_test.go Adds tests for toArray() no-op and explain() on read ops.
admin_test.go Adds tests for runCommand() and comment-only input no-op behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread types/operation_type.go
Comment on lines 7 to 14
const (
OpUnknown OperationType = iota
// OpNoOp represents an input that contains no executable statements
// (e.g., the entire input is a JS comment or whitespace). Executors
// return an empty Result for OpNoOp without contacting the server.
OpNoOp
OpFind
OpFindOne
}
if len(op.Min) > 0 {
cmd = append(cmd, bson.E{Key: "min", Value: op.Min})
}
Comment on lines +132 to +136
func buildCountCommand(op *Operation) bson.D {
cmd := bson.D{{Key: "count", Value: op.Collection}}
if len(op.Filter) > 0 {
cmd = append(cmd, bson.E{Key: "query", Value: op.Filter})
}
}
if len(op.Filter) > 0 {
cmd = append(cmd, bson.E{Key: "query", Value: op.Filter})
}
@h3n4l h3n4l merged commit c7fe133 into main May 9, 2026
6 checks passed
@h3n4l h3n4l deleted the support-toarray-noop branch May 9, 2026 02:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants