Mongo Query Formatter
Format MongoDB shell queries with ISODate, ObjectId and NumberLong kept intact — where plain JSON formatters choke, entirely on this device.
Paste a mongosh query or aggregation pipeline — unquoted keys, single quotes and ISODate(…) all parse here, where plain JSON formatters choke. Nothing you paste leaves this tab.
Runs synchronously in this tab — no upload, no worker, no request, and the query is never executed. Share links embed the query text and options. · Press ? for shortcuts
Do it offline:$ mongosh --quiet --eval 'db.orders.find({ created: { $gte: ISODate("2026-01-01") } })'
How to use it
- Pick a mode: Format pretty-prints the query; Minify collapses it to one line and strips comments.
- Paste anything mongosh accepts as a single expression — a filter document, a pipeline array, or a db.collection.method(…) chain, up to 1 MB.
- For Format, choose the indent unit: 2 spaces, 4 spaces or tabs.
- Set the Keys policy — keep them as typed, quote every key (the $-operators included), or unquote the ones that are valid identifiers — and pick a quote style for strings.
- Read the result as you type: the strip under the output counts every constructor that rode through untouched, and a syntax error shows the parser's own message with a caret frame plus a Jump-to-line button.
Paste a mongosh query or aggregation pipeline and get it back cleanly indented — with ISODate, ObjectId, NumberLong and the other shell constructors kept intact, byte for byte. Plain JSON formatters choke on that syntax; this page's relaxed lexer reads unquoted keys, single quotes, trailing commas and comments the way the shell does. Nothing is executed and nothing leaves this tab.
Examples
db.orders.aggregate([{ $match: { created: { $gte: ISODate("2026-01-01") }, userId: ObjectId("65a1f30e9d2c4b7a8c3d1e2f") } }, { $group: { _id: "$status", total: { $sum: "$amount" }, big: { $max: NumberLong("9007199254740993") } } }])db.orders.aggregate([
{
$match: {
created: {
$gte: ISODate("2026-01-01")
},
userId: ObjectId("65a1f30e9d2c4b7a8c3d1e2f")
}
},
{
$group: {
_id: "$status",
total: {
$sum: "$amount"
},
big: {
$max: NumberLong("9007199254740993")
}
}
}
])Format — a one-line pipeline unfolds at two spaces per level. ISODate, ObjectId and NumberLong pass through byte-exact, and the strip under the output reads 1 ISODate · 1 ObjectId · 1 NumberLong. This exact input throws in JSON.parse.
db.orders.find({
created: {
$gte: ISODate('2026-01-01')
},
status: 'paid'
})db.orders.find({created:{$gte:ISODate('2026-01-01')},status:'paid'})Minify — the formatted query collapses back to a one-liner. Formatting the result reproduces the input exactly, so the round trip loses nothing.
{ user_id: 42, 'active': true, $or: [ { role: 'admin' }, { role: "editor" } ] }{
"user_id": 42,
"active": true,
"$or": [
{
"role": "admin"
},
{
"role": "editor"
}
]
}Normalize toward JSON — Keys: Quote all plus double quotes turns a shell-style filter into a document strict parsers accept, $or included. With no constructors inside, the result is valid JSON.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Why JSON.parse fails on shell syntax
A mongosh query is JavaScript, not JSON: keys may be bare, strings may take single quotes, and ISODate("…") is a function call — three things the JSON grammar has no production for. A strict parser stops at the first bare key. This page's lexer accepts the shell's relaxed forms and treats every constructor call as an opaque atom, so the query formats without being translated. The 12 recognized constructors: ISODate, ObjectId, NumberLong, NumberInt, NumberDecimal, UUID, BinData, Timestamp, DBRef, MinKey, MaxKey, Date.
NumberLong precision — why the wrapper must stay
JavaScript numbers are 64-bit floats: past 2^53 they lose integer precision, which is why 9007199254740993 cannot survive as a bare literal — it silently becomes …992. NumberLong("9007199254740993") carries the value as a string into a true 64-bit integer, so the formatter never unwraps a constructor into a plain number. The argument bytes you typed are the argument bytes you get back.
Regex literals versus $regex
{ name: /^ada/i } and { name: { $regex: '^ada', $options: 'i' } } query the same thing in different clothes. The literal form is shell syntax this page passes through verbatim, flags and all; the $regex form is a plain document that also formats here — and is the one to reach for in drivers or Extended JSON, where slash literals do not exist.
Comments survive Format, not Minify
In Format mode, // and /* */ comments are kept: a comment on its own line stays on its own line, and one trailing an entry rides that entry's line. Minify strips them all and says so in a counted note rather than silently. Trailing commas are removed in both modes, also with a note.
One expression only — the out-of-scope list
This page formats a single query or db.collection method chain. Variables, loops, multi-statement scripts, use statements and template literals are out of scope by design and refuse with a typed message pointing at the offending token — a formatter that half-understands a script would reformat it wrongly with confidence. The 12 everyday methods this page documents are find, findOne, aggregate, countDocuments, distinct, insertOne, insertMany, updateOne, updateMany, replaceOne, deleteOne, deleteMany.
Common errors
Unbalanced parentheses — '(' opened at line 1, column 15 was never closed.- The classic truncated paste: db.orders.find({ status: 'paid' } closes the document but not the call. The position names the opener that never found its partner, and the caret frame marks the same spot in your input.
Unterminated string — the closing ' for the string opened at line 1, column 9 was never found.- A quote opened and the line ended first, as in { name: 'ada }. Shell strings cannot span a raw line break, so the fix is on the line the position points at: close the quote or escape what sits inside it.
'use' is out of scope here — variables, loops and multi-statement scripts do not format; paste a single query or db.collection call.- Session commands like use shop are shell statements, not query expressions — they carry no document to format. Drop the use line and paste the query beneath it on its own.
FAQ
Is my query uploaded or executed when I format it?
Neither. The lexer and printer run synchronously inside this tab — no server call, no worker, no request, and no MongoDB connection. The tool only rearranges text; it never runs the query. Share links do embed the query text and options, and the page says so before you copy one.
Are ISODate and ObjectId arguments checked for validity?
No — constructor calls are opaque on purpose. ObjectId('zzz') formats fine here and still fails in mongosh, because validating arguments would mean reimplementing the shell's types and drifting from them. What the page guarantees is narrower and stronger: the call comes back byte-identical, and an unrecognized constructor name gets a note instead of a rewrite.
Can it format my whole mongosh script file?
Not in this version — one query or one db.collection method chain per run, by design. Scripts with variables, loops or several statements refuse with a typed message naming the first token outside that scope. Format the queries a script contains one at a time; each keeps its constructors intact.