Chapter 03 · CRUD Fundamentals: Insert, Find, Projection, Sort, Limit, and Delete

InsertOne/InsertMany, Generated _id Values, Ordered Writes, and Error Handling

Insert records without confusing “exception” with “nothing happened”: make generated identifiers, ordered/unordered batch semantics, acknowledgements, and partial success observable.

Beginner105–130 minutesInsert acknowledgement + partial-success labMongoDB Community Server 8.3.8 · mongosh 2.10.0 · PyMongo 4.17.0Last reviewed: September 2026

Learning outcomes

AtlasMart is loading a new product feed. Some product documents are independent and valid, one input accidentally reuses an existing identifier, and the ingestion service must know whether the batch stopped, partially succeeded, or completed. Treating insertMany() as “paste an array into MongoDB” is unsafe because the result and failure object are part of the data contract.

01

Explain insertOne and insertMany as acknowledged write operations with observable result metadata.

02

Trace where generated _id values come from and why a client-generated identifier helps retry and reconciliation workflows.

03

Predict ordered versus unordered insert behavior when one document violates the unique _id index.

04

Interpret partial success and BulkWriteError evidence instead of retrying a whole batch blindly.

05

Build a deterministic insert lab with before/after verification and cleanup.

Chapter 03 reproducible baseline

Mandatory labs use a disposable loopback-only standalone mongodb/mongodb-community-server:8.3.8-ubuntu2204-slim with a dedicated AtlasMart collection and explicit reset commands. Driver examples pin pymongo==4.17.0. The standalone is intentionally unauthenticated only for these short-lived local exercises; do not publish it beyond 127.0.0.1. Docker commands use syntax accepted directly by ordinary shells; if the first cleanup reports that the container does not exist, that is harmless. Retryable-write behavior that requires a replica set or sharded cluster is explained but not claimed for this standalone.

Generation-time execution note

Docker, mongod, mongosh, and PyMongo are not available in this generation environment. Commands were checked against current official MongoDB Server and PyMongo documentation, but product commands were not executed here. Expected-output blocks describe stable fields and relationships to verify; they are not fabricated captured transcripts.

1. An insert is a state transition plus an acknowledgement

insertOne(document, options) asks MongoDB to add one document. insertMany(documents, options) asks it to add a sequence of documents. The collection always has a unique index on _id. If the application or driver does not provide _id, most drivers generate an ObjectId before sending the write; the server can also add one when necessary. The returned acknowledged flag tells you whether the write used acknowledged write concern, while insertedId/insertedIds identify successful logical inserts in the ordinary success path.

Acknowledgement is not “the whole business workflow succeeded.” It says the database write met the configured write-concern acknowledgement. Payment capture, cache invalidation, messaging, or a later replica-set durability guarantee are separate concerns.

mongosh · insertOne result and generated _id
use atlasmartdb.crud_products.drop()const before = db.crud_products.countDocuments({})const r = db.crud_products.insertOne({  sku: "sku-cam-100",  name: "AtlasCam Mini",  category: "camera",  priceCents: 12900,  active: true})printjson({before, acknowledged:r.acknowledged, insertedId:r.insertedId})printjson(db.crud_products.findOne({_id:r.insertedId}))

The key observation is that the inserted document can be queried by exactly the identifier returned by the write. In mongosh, the shell/driver typically materializes the ObjectId before the command reaches the server. Do not infer event time, business identity, or global ordering merely from an ObjectId.

2. Ordered batches stop at the first ordinary write error

insertMany() defaults to ordered:true. The server processes the supplied sequence in order. If an ordinary insert error such as duplicate _id occurs, later queued documents are not processed. Earlier documents remain inserted: this is partial success, not atomic rollback of the entire batch.

mongosh · deliberately broken ordered batch
db.crud_products.drop()try {  db.crud_products.insertMany([    {_id:"p-101", sku:"sku-101", name:"Tripod", priceCents:3900},    {_id:"p-102", sku:"sku-102", name:"Bag", priceCents:5900},    {_id:"p-102", sku:"sku-duplicate", name:"Duplicate id", priceCents:1},    {_id:"p-103", sku:"sku-103", name:"Battery", priceCents:2900}  ], {ordered:true})} catch (e) {  print("ordered error code:", e.code)  printjson(e.result)}printjson(db.crud_products.find({}, {_id:1, sku:1}).sort({_id:1}).toArray())

Verify that the first two documents exist, the duplicate fails, and p-103 was never attempted. The error object may expose different wrapper formatting across mongosh/driver releases, so treat stable concepts—error code, failed operation index, inserted count, and persisted documents—as the evidence, not punctuation in a printed exception.

3. Unordered means continue after independent errors—not atomic and not ordered

With ordered:false, MongoDB continues processing remaining inserts after an ordinary write error. The server may reorder work for efficiency, so your application must not rely on execution order. This mode is useful for independent records where maximizing successful ingestion matters more than stopping on the first bad record.

mongosh · unordered partial success
db.crud_products.drop()try {  db.crud_products.insertMany([    {_id:"p-201", sku:"sku-201", name:"Lens", priceCents:9900},    {_id:"p-202", sku:"sku-202", name:"Light", priceCents:7900},    {_id:"p-202", sku:"sku-dup", name:"Duplicate id", priceCents:1},    {_id:"p-203", sku:"sku-203", name:"Mic", priceCents:6900}  ], {ordered:false})} catch (e) {  print("unordered error code:", e.code)  printjson(e.result)}printjson(db.crud_products.find({}, {_id:1, sku:1}).sort({_id:1}).toArray())

The expected durable state contains p-201, p-202, and p-203. That before/after query is more important than assuming a thrown exception means “nothing happened.” For very large inputs, high-level drivers also split inserts into protocol-sized batches, so one application call can contain several wire-level batches.

4. Driver error handling: reconcile what succeeded before retrying

PyMongo raises BulkWriteError for batch write errors. Its details include counts and indexed errors that let the application determine which inputs failed. A naive catch block that immediately retries the entire original list can turn an ambiguous network incident or partial success into duplicate-key noise and duplicate business side effects around the database write.

Python · inspect BulkWriteError before retry
from pymongo import MongoClientfrom pymongo.errors import BulkWriteErrorclient = MongoClient("mongodb://127.0.0.1:27027/?directConnection=true", serverSelectionTimeoutMS=3000)coll = client.atlasmart.crud_productscoll.drop()docs = [    {"_id":"p-301", "sku":"sku-301"},    {"_id":"p-302", "sku":"sku-302"},    {"_id":"p-302", "sku":"sku-dup"},    {"_id":"p-303", "sku":"sku-303"},]try:    result = coll.insert_many(docs, ordered=False)    print("inserted_ids", result.inserted_ids)except BulkWriteError as exc:    d = exc.details    print("nInserted", d.get("nInserted"))    print("writeErrors", [(e.get("index"), e.get("code")) for e in d.get("writeErrors", [])])    print("persisted", list(coll.find({}, {"_id":1}).sort("_id", 1)))finally:    client.close()

Retryable writes are a distinct server/driver feature. They require a replica set or sharded cluster and acknowledged write concern; a standalone lab cannot demonstrate their server-side guarantee. Modern drivers enable retryable writes by default where the topology supports them. For supported write operations, the driver can retry after certain transient failures, but application-level side effects outside MongoDB still need their own idempotency strategy.

5. AtlasMart insert lab: prove the result, the failure, and the final state

shell · start disposable MongoDB on 127.0.0.1:27027
docker rm -f atlasmart-mongo-ch03-l1docker run --name atlasmart-mongo-ch03-l1 -p 127.0.0.1:27027:27017 -d mongodb/mongodb-community-server:8.3.8-ubuntu2204-slimdocker logs atlasmart-mongo-ch03-l1 --tail 25
shell · complete insert verification
mongosh "mongodb://127.0.0.1:27027/atlasmart?directConnection=true" --quiet --eval 'db.crud_products.drop();const one = db.crud_products.insertOne({sku:"sku-generated", name:"Generated id demo", priceCents:1000});printjson({oneResult:one, oneDoc:db.crud_products.findOne({_id:one.insertedId})});try {  db.crud_products.insertMany([    {_id:"lab-a", sku:"a"}, {_id:"lab-b", sku:"b"}, {_id:"lab-b", sku:"dup"}, {_id:"lab-c", sku:"c"}  ], {ordered:false});} catch(e) { printjson({code:e.code, result:e.result}); }printjson({count:db.crud_products.countDocuments({}), docs:db.crud_products.find({}, {_id:1,sku:1}).sort({_id:1}).toArray()});' 

Verification checklist

  • The first insert returns an acknowledged result and a generated identifier that retrieves exactly one document.
  • The unordered batch reports a duplicate-key failure but still persists the nonconflicting documents.
  • The final query is sorted by _id, so verification output is deterministic.
  • You can distinguish “exception thrown” from “zero writes performed.”
  • No command depends on Atlas, Enterprise Advanced, Search, TLS, or KMS.

Check your understanding

  1. Why can an insertMany exception still mean data was inserted?
  2. What does ordered:false change?
  3. Why should the application inspect write error indexes?
  4. Does acknowledged:true prove a payment workflow is complete?
  5. Why is retryable-write behavior not proven by this standalone lab?
Review the answers

Because batch writes can partially succeed before an error. Ordered mode preserves earlier successes; unordered mode may continue after independent errors.

It lets remaining operations continue after ordinary write errors and removes any guarantee that server execution follows input order.

They connect failures to specific input operations so the application can reconcile or repair only the affected records.

No. It only reports the database write acknowledgement under its write concern. Other systems and business side effects remain separate.

Retryable writes require a replica set or sharded cluster; this lab intentionally uses a standalone to keep the CRUD environment small and reproducible.

bash · cleanup/reset
docker rm -f atlasmart-mongo-ch03-l1

The next lesson moves from writes to precise reads: filters, projections, stable ordering, skip, and limit.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.