CRUD Basics: Inserting and Finding Documents

MongoDB Fundamentals — CRUD Basics: Inserting and Finding Documents
MongoDB Fundamentals
Chapter 3 · CRUD Basics: Inserting and Finding Documents

📝 CRUD Basics: Inserting and Finding Documents

With a working instance and shell from Chapter 2, this chapter starts writing and reading real data — the Create and Read of CRUD. Chapter 4 covers Update and Delete.

Inserting Documents

insertOne adds a single document; insertMany adds several at once. If you don't supply your own _id, MongoDB generates one automatically.

insertOne

db.products.insertOne({ name: "Notebook", price: 3.50, category: "stationery" }) // { acknowledged: true, insertedId: ObjectId("64f3...") }

insertMany

db.products.insertMany([ { name: "Laptop", price: 899, category: "electronics" }, { name: "Novel", price: 12, category: "books" } ]) // { acknowledged: true, insertedIds: { '0': ObjectId("..."), '1': ObjectId("...") } }

Finding Documents: find and findOne

find() matches potentially many documents; findOne() returns just the first match, or null if nothing matches.

Basic Queries

db.products.find({}) // every document in the products collection db.products.findOne({ name: "Notebook" }) // the single matching document, or null

Query Operators: Filtering Beyond Exact Match

A plain field/value pair means "equals" implicitly. MongoDB's query operators (always prefixed with $) express everything else:

OperatorMeaning
$eqEquals (usually left implicit)
$neNot equal
$gt / $gteGreater than / greater than or equal
$lt / $lteLess than / less than or equal
$inMatches any value in a given list
$ninMatches none of the values in a given list

Combining Comparison Operators

db.products.find({ price: { $gt: 10, $lt: 50 } }) // products priced strictly between 10 and 50

Using $in

db.products.find({ category: { $in: ["electronics", "books"] } }) // products in EITHER category

Projection: Choosing Which Fields Come Back

A second argument to find()/findOne() controls which fields are returned — 1 to include, 0 to exclude (a document's _id is included by default unless explicitly excluded). This is conceptually similar to the client-specifies-what-it-wants idea the GraphQL course covers at the whole-API level — here it's just scoped to a single query:

A Projected Query

db.products.find({}, { name: 1, price: 1, _id: 0 }) // returns only name and price for every product, no _id

Combining Conditions: Implicit AND, and $or

Multiple fields inside one query object are implicitly ANDed together. For OR logic, use the explicit $or operator with an array of alternative conditions:

Implicit AND vs. Explicit $or

// implicit AND: category is "books" AND price is under 15 db.products.find({ category: "books", price: { $lt: 15 } }) // explicit $or: category is "books" OR price is under 15 db.products.find({ $or: [ { category: "books" }, { price: { $lt: 15 } } ] })

💻 Coding Challenges

Challenge 1: Insert Three Documents at Once

Write an insertMany call adding three "task" documents to a tasks collection, each with a title and a done boolean field.

Goal: Practice the insertMany syntax with a realistic multi-document array.

→ Solution

Challenge 2: Query with Comparison Operators

Write a find query on a products collection returning products priced 20 or more, in either the "electronics" or "home" category.

Goal: Practice combining $gte and $in in one query.

→ Solution

Challenge 3: Write a Projected Query

Write a query on a users collection that returns only each user's email field (no _id, no other fields).

Goal: Practice projection syntax for both inclusion and explicit _id exclusion.

→ Solution

⚠️ Gotcha: find() Returns a Cursor, Not an Array

In the interactive mongosh shell, running db.products.find({}) appears to just print a list of documents — which makes it easy to assume find() hands back a plain array. It doesn't: it returns a cursor, an object that lazily fetches matching documents in batches as you iterate it. The shell happens to auto-iterate and print the first batch for convenience, quietly hiding this detail. It matters once you start writing real driver code (Chapter 8), where you'll typically need to explicitly iterate the cursor or call something like .toArray() to get an actual array of documents — code that treats a raw find() result as if it were already an array will not behave the way the shell's output made it look.

🎯 What's Next

The next chapter covers the other half of CRUD: Updating and Deleting DocumentsupdateOne/updateMany, update operators like $set/$inc/$push, deleteOne/deleteMany, and upserts.