Skip to content
Monghoul

Import a CSV or JSON file into MongoDB

mongoimport needs --headerline for CSV and guesses every type unless you tell it otherwise. The import dialog does the same job with the traps labelled.

Checked against v1.14.0 Updated
The import dialog with a file chosen, its format detected, and the write strategy options
Import · Format detected from the file, and a write strategy to choose

mongoimport ships with the database tools and loads JSON, NDJSON, CSV and TSV into a collection. The import dialog here does the same job. The traps are the same either way, and they are all about types.

With mongoimport, on any MongoDB

mongoimport --uri="mongodb://localhost:27017" \
  --db=shop --collection=orders \
  --type=csv --headerline --file=orders.csv

--headerline is what makes row one the field names. Without it, mongoimport imports that row as a document, so you get one row of column titles stored as data and every field named by position. It applies to CSV and TSV only, and it is an error on JSON.

Every CSV value is text, and the guessing is narrower than you would hope. Left to itself, mongoimport tries a 32-bit integer, then a 64-bit integer, then a float, then gives up and stores a string. That is the whole list. A ZIP code of 01234 becomes the number 1234 and an order reference of 1e5 becomes 100000, while a date lands as a string, because nothing in that sequence parses dates or booleans at all.

Name the types yourself with --columnsHaveTypes, where each column is written as <name>.<type>(<arg>). Where the annotations go depends on where the names come from, and the two options are alternatives rather than a pair. --fields is for a file with no header line:

mongoimport --uri="mongodb://localhost:27017" \
  --db=shop --collection=orders \
  --type=csv --columnsHaveTypes \
  --fields='reference.string(),total.decimal(),placedAt.date(2006-01-02 15:04:05)' \
  --file=orders.csv

With --headerline, the annotations belong in the file’s own first row instead, and --fields is not passed at all:

reference.string(),total.decimal(),placedAt.date(2006-01-02 15:04:05)
INV-1,19.99,2026-01-04 09:15:00

The date argument is a Go time layout rather than a YYYY-MM-DD pattern, which surprises everyone the first time.

Pick the write mode on purpose. --mode defaults to insert. A duplicate _id raises a duplicate key error for that document, and the run carries on: mongoimport continues through duplicate key and validation errors unless you pass --stopOnError, so a partial import reports failures at the end rather than halting at the first one. The other modes are upsert (replace matching documents, insert the rest), merge (merge fields into matching documents) and delete. Matching is on _id unless you name other fields with --upsertFields. --drop empties the collection and its indexes first.

For restoring a dump rather than loading a file, the tool is mongorestore, and copying a collection covers when each applies.

In the app

There are three ways in, all in the sidebar:

  • Right-click a database and choose Import Collection to load a file into a new collection.
  • Right-click a collection and choose Import into Collection to load a file into that one.
  • Right-click a connection and choose Import Database to load a folder of files (Pro).

The steps:

  1. Open the dialog. Both file dialogs are titled Import Collection.
  2. Under Source File, pick the File Format (JSON/EJSON, CSV/TSV Files or Excel Files) and the File Location. Choosing the file sets the format from its extension. It suggests a collection name, and it picks the tab delimiter for a .tsv. A name you typed is never replaced.
  3. For a new collection, check the Collection Name. The app creates the collection if it does not exist.
  4. For CSV, set the Delimiter and First row contains column names. Without a header row, the columns are named field1, field2 and so on.
  5. Set the Write Mode and the Processing Options, described below.
  6. Click Start Import. The progress bar counts the documents imported and the documents that failed. Cancel Import stops the run, and the documents already imported stay.

When the import finishes, the app reports how many documents landed and opens the collection if the import created it.

JSON, NDJSON, CSV and TSV are streamed rather than read whole, so memory does not grow with file size and there is no practical ceiling. Excel import is Pro, reads the first sheet, takes row one as the column names, and reads the whole workbook. A leading UTF-8 byte-order mark is removed, so a file saved by Notepad or exported from Excel as CSV UTF-8 keeps its first column name.

Pick a write mode deliberately

Write Mode What it does
Insert Only (Skip duplicates) Adds documents. A document whose _id the collection already holds is refused and counted as failed. The rest still land
Upsert (Update or insert by _id) Replaces a matching document, inserts the rest. A document with no _id is inserted
Drop & Insert (Clear collection first) Empties the collection first. A red warning appears when you pick it

The app reads the file before it drops the collection, so a file that cannot be read leaves your data in place. Drop & Insert into a write-protected target asks for confirmation before the drop. If you are importing into production, turn write protection on before you start rather than after.

Errors: stop or continue

Stop on First Error is off by default. The import then skips a document or row it cannot read, and it counts them. Tick the box while you are still working out whether the file is right. Clear it once you know. A row with more or fewer cells than the header row is imported, not skipped.

The type inference traps

This is where CSV imports go wrong quietly.

  • Numbers, booleans and null are inferred when Auto-convert Types is on. A column of 1 and 0 becomes numbers, not booleans, unless the cells say true and false.
  • A cell holding JSON is parsed into an object or an array.
  • An empty cell imports as null, not as a missing field. Those are different in MongoDB and queries with $exists will tell them apart.
  • Dates only from an ISO-style leading YYYY-MM-DD. This is deliberate, and it is why order-1, sku-2 and Q1-2024 stay text instead of turning into dates in 2001. If your dates are 03/04/2026, they will import as strings, and that is the correct answer because nobody can tell whether that is March or April.

The Auto-convert Types switch is on by default. The dialog does not offer it for JSON, which already carries its types. If the inference is not what you want for a CSV, turn it off and every column imports as text, then fix the types with an update. Either way, check the result in tree view, where every value shows its BSON type, before you import the other nine files.

A whole database at once

Right-click the connection, choose Import Database (Pro), name the database, and pick the source directory and the file format. The dialog reports how many files match before it starts. Each collection takes the file’s name without its extension. Files with another extension are ignored, and the import will not start when nothing matches. With Stop on First Error on, one failed file stops the files after it.

Next