The translation dictionary. The first reference a developer reaches for mid-migration, organised so that you may keep it open in another tab and answer one question at a time. Every concept is named in MongoDB's idiom on the left and given its PostgreSQL equivalent on the right, with a short note where the equivalence is approximate rather than exact.
MongoDB is excellent software. The shape of this appendix is a translation, not a critique. Where the two systems differ in a way worth understanding, the difference is named. Where they meet at the same place by different roads, the meeting is recorded.
Storage and Identity
MongoDB
PostgreSQL
Notes
Database
Database
One-to-one. A PostgreSQL database holds collections (tables), schemas, and extensions.
Collection
Table
One collection becomes one table with a data JSONB column. Auto-created by doc_insert.
Document
JSONB row
The full document lives in data JSONB. _id and created_at sit alongside as columns.
Field
JSONB key
Accessed by data->'field' (jsonb) or data->>'field' (text).
Embedded document
Nested JSONB object
data->'address'->>'city'. Dot notation in filters expands automatically.
Embedded array
JSONB array
data->'tags'. Unnested via jsonb_array_elements or jsonb_array_elements_text.
_id (ObjectId)
_id UUID DEFAULT gen_random_uuid()
Outside the document as a first-class column; B-tree indexed and joinable.
ObjectId (12-byte)
UUID (16-byte)
Mint fresh UUIDs at migration time; keep the 24-char hex as legacy_id text with a unique index.
BSON
JSONB
Binary JSON with sorted keys, offset table, and direct field access. Available since PostgreSQL 9.4 (2014).
Extended JSON Types
MongoDB Extended JSON v2 (relaxed mode) wraps types JSON cannot represent. The five wrappers your loader will encounter, and their PostgreSQL homes:
Extended JSON
Example
PostgreSQL equivalent
Loader action
$oid
{"$oid": "6512..."}
text (legacy_id) and a fresh uuid for _id
Unwrap the hex string into legacy_id; mint a UUID for the new _id.
$date
{"$date": "2025-09-27T10:30:00Z"}
timestamptz
PostgreSQL parses ISO-8601 directly. MongoDB stores ms; PostgreSQL stores µs — the migration is lossless, the round-trip is not.
$numberLong
{"$numberLong": "9007199254740993"}
bigint
Unwrap the string and cast. JavaScript's Number loses precision above 2^53; PostgreSQL does not.
$numberDecimal
{"$numberDecimal": "42.50"}
numeric
Unwrap as a string, store as numeric for exact arithmetic. Never round-trip through JS Number.
$binary
{"$binary": {"base64": "...", "subType": "00"}}
bytea
Decode the base64 string into bytea; carry the subType in a sibling column if the application uses it.
In relaxed mode, ordinary 32-bit integers and finite floats arrive as plain JSON numbers and need no special handling. The five wrappers above are the entire migration vocabulary.
Indexes
MongoDB
PostgreSQL
Notes
Single-field {email: 1}
B-tree expression index on ((data->>'email'))
Or on a generated STORED column.
Single-field unique {email: 1} unique
CREATE UNIQUE INDEX ON users ((data->>'email'))
Same semantics.
Compound {tenant_id: 1, created_at: -1}
Composite expression B-tree, column order matches the query
The trade between throughput and durability lives at this knob.
Causal consistency
Strict consistency on the primary; replica lag bounded by streaming replication
PostgreSQL primaries are linearizable by default.
Optimistic concurrency
MVCC with snapshot isolation
PostgreSQL never reads a partially-committed row.
Backup and Recovery
MongoDB
PostgreSQL
Notes
mongodump / mongorestore
pg_dump / pg_restore
Logical backup; portable across versions.
Filesystem snapshot
pg_basebackup
Physical backup; faster restore, version-locked.
Point-in-time recovery (Atlas)
PITR via WAL archive (every managed provider supports it)
Same capability, different tooling.
Oplog tail for incremental
WAL archiving + log shipping / streaming
Same mechanism, named differently.
Drivers and Languages
The Gold Lapel SDKs ship doc_* methods in every supported language. Method casing follows each language's convention; behaviour is identical across all seven.