> ## Documentation Index
> Fetch the complete documentation index at: https://ngquct-fix-mongodb-insert-empty-field-name.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# MongoDB

> Connect to MongoDB and run mongosh JavaScript, browse collections, and set up Atlas SRV automatically

export const name_0 = "MongoDB"

export const plugin_0 = "MongoDB Driver"

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 {name_0} driver is not in the app. Picking {name_0} in the **Choose a Database** sheet offers the
download before the form opens, and opening a saved {name_0} connection installs it without asking.
**Settings > Plugins > Browse > {plugin_0}** installs it up front. See [Plugins](/features/plugins).

## Quick setup

<Steps>
  <Step title="Create Connection">
    Click **New Connection…**, select **MongoDB**, and enter hosts and credentials
  </Step>

  <Step title="Test Connection">
    Click **Test Connection** to verify, then **Save & Connect**
  </Step>
</Steps>

## Connection settings

| Field              | Default           | Notes                                                                |
| ------------------ | ----------------- | -------------------------------------------------------------------- |
| **Hosts**          | `localhost:27017` | Several `host:port` pairs for a replica set                          |
| **Username**       | -                 | Leave empty for local dev without auth                               |
| **Password**       | -                 |                                                                      |
| **Database**       | -                 | Opens straight into that database. Leave it empty to browse them all |
| **Auth Database**  | -                 | Advanced. Where your user account lives, usually `admin`             |
| **Auth Mechanism** | Default           | SCRAM-SHA-1, SCRAM-SHA-256, X.509, or AWS IAM, under Authentication  |

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.

<Frame caption="MongoDB connection form">
  <img className="block dark:hidden" src="https://mintcdn.com/ngquct-fix-mongodb-insert-empty-field-name/Rit3n8sM2hKD72e2/images/mongodb-connection-form.png?fit=max&auto=format&n=Rit3n8sM2hKD72e2&q=85&s=46b6ade33d47f3221e6598b305b23d18" alt="MongoDB connection form with the multi-host Hosts editor" width="1560" height="960" data-path="images/mongodb-connection-form.png" />

  <img className="hidden dark:block" src="https://mintcdn.com/ngquct-fix-mongodb-insert-empty-field-name/Rit3n8sM2hKD72e2/images/mongodb-connection-form-dark.png?fit=max&auto=format&n=Rit3n8sM2hKD72e2&q=85&s=90957a8bd6f62baaa5b62ceedde1b829" alt="MongoDB connection form with the multi-host Hosts editor" width="1560" height="960" data-path="images/mongodb-connection-form-dark.png" />
</Frame>

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

```text theme={null}
mongodb://user:password@host:27017/database?authSource=admin
mongodb+srv://user:password@cluster.mongodb.net/database
```

`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](/connections/urls).

## 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:

```text theme={null}
mongodb://user:pass@host1:27017,host2:27017,host3:27017/db?replicaSet=rs0
```

<Warning>
  <Warning>
    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.
  </Warning>
</Warning>

## Browsing collections

Click a collection to page through its documents. The columns are the fields in the documents on screen, then any field the collection's `$jsonSchema` validator declares that none of them hold, so an empty collection still shows the fields it was created with. A top-level ObjectId renders as its hex string. A nested object or array shows as Extended JSON in the order it is stored: `{"$oid": "…"}`, `{"$date": "2024-05-01T10:00:00.123Z"}`, `{"$numberLong": "5"}`, and `3.0` for a double that holds a whole number.

An edited cell keeps its field's type. A date stays a date, whether typed as `2024-01-02T03:04:05Z`, as `2024-01-02`, or picked in the date picker, which writes your local time. An ObjectId stays an ObjectId, an integer past 2^53 is sent as a 64-bit integer, and a field the validator declares as `string` is written as text even when it reads `123` or `true`. Where the grid has no type to go on, `123` is a number and `{"a": 1}` a document; text that only looks like JSON is written as a string.

Editing a nested object or array writes only the paths that changed, such as `address.city` or `tags.1`, so every other value in it keeps its type and its place. Adding two keys at once, reordering keys, or changing an array's length writes the whole value instead. A binary cell keeps its BSON subtype when its bytes are edited, and a duplicated or pasted row keeps its binary fields.

A field whose own name contains a dot, starts with `$` or is `__proto__` is written with `$setField`, which needs MongoDB 5.0 or later. On an older server the save is refused and nothing is sent.

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](/features/filtering#nested-fields). 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; drop one from a query tab with `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": …})`.

### Missing fields and null

A field a document does not have reads **No Field**; a field holding null reads NULL. **Set Value > NULL** stores `null` and keeps the field. To delete a field, right-click the cell and choose **Remove Field**, or choose it from the value menu of the field in the inspector; saving sends `$unset`. A validator that lists the field in `required` refuses the save with `Document failed validation`.

A new row starts with every field missing, and only the cells you fill in are written, so a row saved untouched inserts a document holding only a generated `_id`. **Duplicate** and **Paste** keep which fields were missing and which held null. Pasted text carries no such distinction, so a `NULL` in it leaves the field out. Undo, discarding, and [restoring a save](/features/change-tracking#restoring-a-save) put a removed field back and take out a field that was filled in. A restore treats a field removed or added since the save as **Changed since the save**, and leaves that document alone.

**Set Value > NULL** appears only where the validator takes null: the field's type lists `null` or is not declared, and any `enum` on it lists `null` too. A rule over the whole document, such as a top-level `anyOf` or a query operator beside `$jsonSchema`, hides it on every field. A validator set to `validationAction: "warn"` hides it on none. The filter bar's **is NULL** matches a field holding null and a missing field alike, as `{field: null}` does in `mongosh`. To find only documents that lack a field, run `db.users.find({nickname: {$exists: false}})` in a query tab.

### 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.

## Creating a collection

Choose **Database > New Table…**. Each row of the grid is a field, and its type is one of the BSON types in the list: `objectId`, `string`, `int`, `long`, `double`, `decimal`, `date`, `bool`, `array`, `object` and the rest. **Create Table** turns the rows into a `$jsonSchema` validator and runs `db.createCollection("articles", {"validator": …})`, which **SQL Preview** shows first.

<Frame caption="Nullable fields also accept null; NOT NULL ones go in required">
  <img className="block dark:hidden" src="https://mintcdn.com/ngquct-fix-mongodb-insert-empty-field-name/Rit3n8sM2hKD72e2/images/mongodb-create-collection.png?fit=max&auto=format&n=Rit3n8sM2hKD72e2&q=85&s=78565d7efb945edf510ff3ab5a5be60a" alt="SQL Preview tab of Create Table showing db.createCollection for articles with a $jsonSchema listing _id, title, tags, date and schemaVersion" width="1560" height="960" data-path="images/mongodb-create-collection.png" />

  <img className="hidden dark:block" src="https://mintcdn.com/ngquct-fix-mongodb-insert-empty-field-name/Rit3n8sM2hKD72e2/images/mongodb-create-collection-dark.png?fit=max&auto=format&n=Rit3n8sM2hKD72e2&q=85&s=5f04f73eaadaee75ee7cbf720de1aa9e" alt="SQL Preview tab of Create Table showing db.createCollection for articles with a $jsonSchema listing _id, title, tags, date and schemaVersion" width="1560" height="960" data-path="images/mongodb-create-collection-dark.png" />
</Frame>

| Row                  | Becomes                                                                       |
| -------------------- | ----------------------------------------------------------------------------- |
| **Nullable** NO      | The field is in `required`: a document without it, or with `null`, is refused |
| **Nullable** YES     | The field is optional and may hold `null`                                     |
| `_id`                | Always the key, typed `objectId`. Leave it out and MongoDB still adds one     |
| No rows, only a name | A collection with no validator                                                |

MongoDB enforces the validator with its defaults, `strict` and `error`, so an insert or edit of the wrong type fails with `Document failed validation`. **Primary Key** on any field other than `_id` is refused, and so are field names that start with `$` or contain a dot.

Each row on the **Indexes** tab runs as a `createIndex` after the collection is made, with its fields in the order listed. **BTREE** is an ascending key, **HASH** is `hashed`, **FULLTEXT** is `text`, and **SPATIAL** is `2dsphere`. A hashed index takes one field and cannot be unique, and a text index cannot be unique either.

## Inserting documents

A field exists only in the documents that hold it, so a collection with no documents and no validator shows `_id` alone and has no column to type a field into. Choose **Edit > Insert Document…** to write a whole document instead. It is on a row's context menu too, and on the context menu of an empty grid.

<Frame caption="The first document of an empty collection">
  <img className="block dark:hidden" src="https://mintcdn.com/ngquct-fix-mongodb-insert-empty-field-name/Rit3n8sM2hKD72e2/images/mongodb-insert-document.png?fit=max&auto=format&n=Rit3n8sM2hKD72e2&q=85&s=e2abf125a8b9d956c02a037b827d88b1" alt="Insert Document sheet over the empty events collection, holding a document with a name, a channel, a date, a count and a list of tags" width="3024" height="1722" data-path="images/mongodb-insert-document.png" />

  <img className="hidden dark:block" src="https://mintcdn.com/ngquct-fix-mongodb-insert-empty-field-name/Rit3n8sM2hKD72e2/images/mongodb-insert-document-dark.png?fit=max&auto=format&n=Rit3n8sM2hKD72e2&q=85&s=511aafda4553fccd1759fbd03da3c163" alt="Insert Document sheet over the empty events collection, holding a document with a name, a channel, a date, a count and a list of tags" width="3024" height="1722" data-path="images/mongodb-insert-document-dark.png" />
</Frame>

The text is Extended JSON: quote every field name, and write an ObjectId as `{"$oid": "…"}`, a date as `{"$date": "2024-05-01T10:00:00Z"}` and a decimal as `{"$numberDecimal": "1.10"}`. A whole number is stored as a 32-bit integer, or as a 64-bit one when it does not fit; `{"$numberLong": "5"}` stores a small 64-bit integer. A number with a decimal point is a double. Fields are stored in the order written. Leave out `_id` and the server generates one.

Every field name is stored as typed, an empty name, a dotted name such as `a.b` and a top-level name starting with `$` included. MongoDB before 5.0 refuses the last two kinds. mongosh reads a document holding both `$ref` and `$id` at the top level as a DBRef, so keep that pair for references.

## 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.

`$type`, `$regex` and `$options` objects reach the server as the operator documents they are
written as, the same as in mongosh: `{tags: {$type: ["array", "null"]}}` and
`{name: {$regex: "^a", $options: "i"}}` filter in a query tab and in the filter bar's
**Raw Filter** row. Such an object is a document everywhere, so `insertOne` stores
`{$regex: "a", $options: "i"}` as an embedded document and MongoDB refuses it inside `$in`. Write
a regular expression value as a literal, `/^a/i`.

```javascript theme={null}
db.orders.find({status: "completed"}, {customerId: 1, total: 1})
  .sort({date: -1})
  .limit(20)

db.sales.aggregate([
  {$match: {date: {$gte: ISODate("2025-01-01")}}},
  {$group: {_id: "$product", totalSales: {$sum: "$amount"}}}
])
```

### 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.

```javascript theme={null}
var stale = [];
db.orders.find({status: "pending"}).forEach(function (order) {
  if (order.updatedAt < ISODate("2025-01-01")) { stale.push(order._id); }
});
print("stale orders: " + stale.length);
```

`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.

```javascript theme={null}
db.users.updateOne({_id: 1}, {$set: {active: true}}, {upsert: true})
```

### 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](/features/autocomplete#mongodb). 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](/connections/ssh-tunneling). See [SSL/TLS](/connections/ssl).

## Limitations

* A row with no `_id` cannot be updated or deleted. The save is refused rather than matched on the remaining fields, and every change stays pending. Keep `_id` in the projection so every row carries one.
* A binary value keeps the subtype it was read with. Bytes typed into a new row are saved as subtype 0 where the validator declares the field `binData`.
* Other bytes refuse the save when their subtype was never read, or was read as two different ones: bytes typed into an existing document, pasted from another collection, or put back by Data Rewind after TablePro restarts. Write that value with a query.
* A field holding text in some documents and objects or arrays in others takes no value that reads as JSON from the grid, since it could be either type: the save is refused. Write it with a query.
* A new row, duplicate or paste cannot hold a field named `__proto__`, or an empty field name at any depth: the save is refused. Use **Remove Field** on that cell, save, then set it on the saved document.
* `_id` is read-only in the grid and the row inspector, and a row added with **Add Row** is inserted without one so the server generates it. To choose your own, use **Insert Document…**.
* Transactions are not exposed. Statements always run standalone, on any topology.
* A collection takes one text index. A second **FULLTEXT** row fails after the collection and the indexes before it are created: list every text field in one index instead.
* **New Table…** writes the validator with the server's own level and action. To log bad documents instead of refusing them, run `db.runCommand({collMod: "articles", validationAction: "warn"})` after creating the collection.
* Nested paths filter but do not sort. Sorting works on the grid's own columns.
* Filtering, sorting or editing a field named `""` or starting with `$` fails in the grid. A dotted name such as `a.b` reads as the path to `b` inside `a`. Use `$getField` and `$setField` in the query editor.
* **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 `Code` scope holding an object that opens with `$type`, `$regex` or `$options`, such as `Code("f", {a: {$type: "binData"}})`, fails with `This is not a document MongoDB can read`. List another key of that object first.
* 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 are whole numbers up to 4294967294 (`"0"`, `"12"`) sort ahead of the rest in a document literal, which is what JavaScript does with them. A nested object with such a key after another key, or with a key named `__proto__`, refuses the save when it is duplicated or written whole. Use **Insert Document…** or a query for it.

## 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.
