TableCore

MongoDB shell syntax

The editor accepts three languages and works out which one you typed from the first significant character:

You start withIt is read as
SELECT or WITHSQL
db.A MongoDB shell query
{A raw MongoDB command document

You do not switch a mode. Paste a db.orders.find(…) from a colleague's message into a tab that had SQL in it and it runs as what it is.

The grammar

Shell support is a closed grammar of reads, not a JavaScript interpreter.

db.orders.find({ status: "open" }, { _id: 0, total: 1 }).sort({ total: -1 }).limit(20)
db.getCollection("order items").find({}).skip(40).limit(20)
db.orders.aggregate([{ $match: { status: "open" } }, { $group: { _id: "$country", n: { $sum: 1 } } }])
db.orders.countDocuments({ status: "open" })
db.orders.estimatedDocumentCount()
db.orders.distinct("country", { active: true })
  • find(filter?, projection?) with .sort(…), .skip(…) and .limit(…) chained in any order.
  • aggregate([stages]).
  • countDocuments(filter?), estimatedDocumentCount(), distinct("field", filter?).
  • Each after db.<collection>. or db.getCollection("<name>")..

The argument documents go through the same BSON parser mongosh uses, so ObjectId("…"), ISODate("…") and unquoted field names mean here what they mean there.

Writes are not in the grammar

insertOne, updateMany, deleteMany, replaceOne, forEach, variables and several statements in one go are rejected with a position, as MONGO_SHELL_SYNTAX_ERROR. This is deliberate: a shell expression quietly interpreted as something else would run something other than what is on your screen. Writes change data with no grid to review first, so they belong behind a confirmation the editor does not have.

Also outside the grammar today: count() (use countDocuments), and explain() on a cursor — use the Explain button, which asks about the current query whichever language it is in.

Edges that are decided, not accidental

  • A collection name ends where the call begins, not where a matching word ends. db.find.archive.countDocuments() reads the collection find.archive.
  • An aggregation is its pipeline. A mongosh aggregation cursor has no sort/skip/limit of its own — those are stages — so a chain after aggregate([…]) is refused rather than translated into stages nobody wrote.
  • estimatedDocumentCount takes no filter. It reads collection metadata, so a filter would be a different question rather than a narrower one. The diagnostic sends you to countDocuments.
  • An empty pipeline is a typo, not a query.

What comes back

A shell find streams and decodes on demand exactly like a simple SELECT, and its result is editable — it carries the same provenance a SQL result does: the collection from the query, and columns named after the document fields.

Two cases close themselves, and they are the two you will actually meet:

  • a projection that removes _id ({ _id: 0, … } — the first thing in every example on the internet) leaves no identifier to write back through, so the grid is read-only for a missing id;
  • a projection that transforms ({ x: "$y" }, $slice, $meta, a dotted path) is a computed projection, and read-only for that reason.

Everything else is read-only by nature: an aggregation may reshape a document beyond recognition, and a count or a distinct value is not a document at all. A count comes back as one row in a column named n — MongoDB's own count command calls it that — and distinct as rows under the field name you asked about.

The row count for an aggregation stays unknown when the first batch came back full, because counting an aggregation means running it a second time.

When to prefer shell over SQL

  • The query already exists in shell form — in a runbook, a ticket, a colleague's message. Retyping it as SQL to have it translated back is work with a defect budget.
  • The shape has no SQL spelling: $elemMatch, $expr, $regex with options, $geoNear, array operators.
  • You want a specific pipeline rather than the one the translator would build.

Prefer SQL when you want the result to be editable in the grid across a projection, when you want the query to be portable to a colleague who thinks in SQL, or simply when you think in SQL — which is the case the product is for.

Two things the editor does not do here

There is no autocompletion for shell queries — no method names, no field names. And a db.… query is not validated as you type: it is neither SQL nor a JSON document, and a red squiggle under a working query would just be a lie. Errors come from the engine when you run it, with a position.