Skip to main content
The query tab is a JavaScript shell. Write db.orders.find({status: "new"}) the way mongosh takes it, unquoted keys and all, and keep going into variables, loops and forEach. Collections appear as tables in the sidebar, with top-level fields as columns and nested objects as formatted JSON. The driver is not in the app. Picking in the Choose a Database sheet offers the download before the form opens, and opening a saved connection installs it without asking. Settings > Plugins > Browse > installs it up front. See Plugins.

Quick setup

1

Create Connection

Click Create Connection…, select MongoDB, and enter hosts and credentials
2

Test Connection

Click Test Connection to verify, then Save & Connect

Connection settings

Naming a Database skips listing every database on the server, which is worth doing on a cluster with hundreds. Leave it empty and the first non-system database opens instead. Cmd+K switches either way, on the same connection, with no reconnect. Auth Database is a separate question: it says where your account is defined, not what you browse. Left empty, it follows the Database field. An account defined in admin needs Auth Database set to admin whenever Database names something else, or authentication fails. SRV connections authenticate against admin regardless, unless told otherwise. Switching databases in the app never changes it, so browsing a database your user has no account in is fine. Also in Advanced: Read Preference, Write Concern, Use SRV Record, Replica Set name, and Legacy UUID Encoding. There is no minimum server version; the driver adapts what it asks for to what the server answers.
MongoDB connection form with the multi-host Hosts editorMongoDB connection form with the multi-host Hosts editor

MongoDB connection form

On MongoDB 4.0 and later the database list is requested as authorized databases only, so an account without the listDatabases privilege still sees what it can read. On an older server that list comes back empty: name a Database on the connection instead.

Connection URL

mongodb+srv:// resolves hosts through DNS SRV records and takes no port; one pasted into the field is stripped before connecting. Plain mongodb:// keeps whatever port you give it. See Connection URL Reference.

MongoDB Atlas (SRV)

An Atlas connection needs the cluster hostname, a username, and a password. Atlas requires SRV and TLS, so a host ending in .mongodb.net gets both turned on for you, TLS only if the SSL mode was still Disabled. Elsewhere the Use SRV Record toggle in Advanced does the same job. Add your current IP to the cluster’s access list in the Atlas console first: traffic from an address that is not on it times out rather than failing.

Replica sets

The Hosts field takes comma-separated pairs (host1:27017,host2:27017,host3:27017), the primary is discovered, and writes are routed to it. Set the replica set name in Advanced. A multi-host URI pastes in directly:
Over an SSH tunnel only the first host is used and the rest of the list is dropped. Replica set discovery and failover are off for that session, with nothing on screen to say so.

Browsing collections

Click a collection to page through its documents. Column layout is inferred by sampling documents, not read from a validator, so a field missing from the sample gets no column. ObjectIds render as strings, arrays and nested objects as formatted JSON. The filter bar’s column picker lists paths inside nested objects and arrays of objects, so customer.country and items.sku filter directly; a row on an array field chooses any element or same element, which makes one array entry satisfy every row set to it. See Filtering. A field name containing a literal dot is left out of the picker, since MongoDB reads a dot as a path separator; reach it with $getField inside $expr. The Structure tab lists a collection’s indexes; create and drop them from a query tab with db.users.createIndex({"email": 1}) and db.users.dropIndex("email_1"). New Database asks for a database name and a first collection, both required. New View opens a query tab holding a db.createView("view_name", "source_collection", [pipeline]) template, and editing a view pre-fills db.runCommand({"collMod": …}).

Binary UUIDs

A binary subtype 4 field renders as UUID("8cd003eb-4a25-4324-9332-88fce2da0d1a"). Subtype 3 is the legacy format, and its bytes do not say which driver wrote them, so it stays BinData(3, "…") until Legacy UUID Encoding on the connection is set to Java, C#, or Python. Match it to the driver that wrote the data: the wrong choice shows a valid-looking but wrong UUID. Once set, the value renders as LegacyJavaUUID("…") and reads that way everywhere, filters and MQL export included. Nothing stored is rewritten, uuidRepresentation=javaLegacy in a pasted URL sets the same option, and a change takes effect on the next connect.

Writing queries

Queries run through JavaScriptCore, so a statement is JavaScript and the whole language is available: object literals with unquoted keys, single-quoted strings, regex literals such as /abc/i, new Date(), arithmetic, // and /* */ comments. Date("2020-01-01") without new gives a date rather than the string plain JavaScript would return, so a filter written that way keeps matching. new Date and instanceof Date are the native ones.

Scripts

The shell is per connection, so a variable or function defined in one statement is there for the next, in any tab on that connection, until you disconnect.
print and printjson write to the result grid, one row per line, whenever the statement itself returns no documents. A statement that returns documents shows those instead, with the printed lines on the status line under the grid.

Collection references

db.users, db["users"], or db.getCollection("users"). Use getCollection for names with dots or spaces, names starting with a digit, and names that collide with a database method: db.stats and db["stats"] both reach the method, because the shell cannot tell which you meant.

Cursors

find() and aggregate() return a cursor and touch nothing until something reads it. Chain sort, skip, limit, projection, hint, collation, maxTimeMS, batchSize and allowDiskUse onto it, then read it with forEach, map, toArray, hasNext/next, itcount, count or explain. On an aggregation, sort, skip and limit become $sort, $skip and $limit stages appended to the pipeline. A modifier after the cursor has started throws, the same as mongosh. Split it into two statements, or set the modifier before the first read.

Write options

updateOne, updateMany, replaceOne, findOneAndUpdate and the delete calls take an options document, and upsert, arrayFilters, hint, collation and returnDocument reach the server. A write returns the object mongosh returns: matchedCount, modifiedCount, upsertedCount and upsertedId for an update, deletedCount for a delete, insertedId for an insert.

Methods

Collection: find, findOne, aggregate, countDocuments/count, estimatedDocumentCount, distinct, insertOne/insertMany/insert, updateOne/updateMany/update, replaceOne, save, deleteOne/deleteMany/remove, findOneAndUpdate/findOneAndReplace/findOneAndDelete, bulkWrite, createIndex/createIndexes, dropIndex/dropIndexes, getIndexes, hideIndex/unhideIndex, drop, renameCollection, stats, dataSize, storageSize, totalIndexSize, totalSize, isCapped, validate, explain. Database: getCollection, getSiblingDB, getCollectionNames, getCollectionInfos, createCollection, dropDatabase, stats, version, serverStatus, hostInfo, currentOp, killOp, runCommand, adminCommand. use <name>, show dbs and show collections work as typed. Anything with no method of its own goes through db.runCommand({…}). Cmd+Shift+F reformats by nesting depth. Autocomplete offers collections, collection methods, cursor methods after find(), nested field paths such as address.city, and the $ operators valid at the cursor; see Autocomplete. For a query plan, chain .explain("executionStats") onto the cursor.

SSL/TLS

New connections default to Disabled, and the driver has no TLS fallback: Preferred behaves exactly as Required, which is what the SSL pane warns about. For an unencrypted local instance use Disabled or SSH tunneling. See SSL/TLS.

Limitations

  • A row with no _id cannot be updated or deleted. The save is skipped rather than matched on the remaining fields. Keep _id in the projection so every row carries one.
  • _id is read-only in the grid, and left out of an insert entirely so the server generates it. To choose your own, insert with db.collection.insertOne({…}).
  • Transactions are not exposed. Statements always run standalone, on any topology.
  • Nested paths filter but do not sort. Sorting works on the grid’s own columns.
  • same element covers a field one array deep. A path through an array inside another array needs nested $elemMatch, so those filter with dot notation only.
  • GridFS buckets are not browsable, and change streams are unsupported.
  • A script that loops without touching the database cannot be stopped: JavaScriptCore has no public way to interrupt one. Cmd+. stops anything that reads, writes or prints, which covers every query. A script silent for 120 seconds is abandoned and the shell restarts.
  • Field names that look like integers ("0", "12") sort ahead of the rest in a document literal, which is what JavaScript does with them.

Troubleshooting

Connection refused: check MongoDB is running (brew services start mongodb-community) and that the port and bindIp in mongod.conf match what you entered. Authentication fails on connect: the error names the database that was authenticated against. If your user does not live there, set Auth Database in Advanced; otherwise check the username, password, and auth mechanism. The MQL editor does not parse db.getUsers() or db.createUser(); read users with db.runCommand({"usersInfo": 1}). Timeout: for Atlas, add your IP to the cluster’s access list first. Otherwise verify host and port and check the network and firewall. A collection is slow to open: a sort or filter on an unindexed field makes MongoDB read every document, even for 20 rows. Check the Structure tab for an index on that field. Cmd+. stops the query on the server. The row total shows ~: that is the instant estimate from collection metadata. The automatic count is capped at 5 seconds and keeps the estimate if the server is slower; Count Exactly runs a real count against your query timeout. Views and time-series collections have no metadata count, so their estimate can be missing altogether.