Appearance
MongoDB Queries
This page covers the core CRUD operations and query operators in MongoDB.
The examples on this page use a trips collection containing HSL city bike trip data.
Selecting a Database
Use the use command to switch to a database. The db variable then refers to that database in all subsequent commands.
js
use bikedataIf the database does not exist yet, MongoDB will create it when you first insert data into it.
Inserting Documents
js
// Insert a single document
db.trips.insertOne({
departure: ISODate("2025-07-31T23:59:58Z"),
return: ISODate("2025-08-01T00:08:37Z"),
departureStationName: "Isoisänsilta",
returnStationName: "Vilhonvuorenkatu",
coveredDistance: 1002,
duration: 516
})
// Insert multiple documents
db.trips.insertMany([
{
departure: ISODate("2025-07-31T23:59:54Z"),
return: ISODate("2025-08-01T00:03:42Z"),
departureStationName: "Karhupuisto",
returnStationName: "Sörnäinen (M)",
coveredDistance: 712,
duration: 224
},
{
departure: ISODate("2025-07-31T23:59:53Z"),
return: ISODate("2025-08-01T00:05:21Z"),
departureStationName: "Hietalahdentori",
returnStationName: "Apollonkatu",
coveredDistance: 1805,
duration: 323
}
])Documents can also contain arrays and nested objects:
js
db.users.insertOne({
username: "john.doe",
email: "john@example.com",
roles: ["user", "editor"],
preferences: {
theme: "dark",
language: "en"
}
})ObjectId
MongoDB automatically generates a unique ObjectId for every document when it is inserted.
Querying (Read)
Find All Documents
js
db.collection.find()Example — fetch all trips:
js
db.trips.find()Find with a Filter
js
// Trips departing from Karhupuisto
db.trips.find({ departureStationName: "Karhupuisto" })Querying Array Fields
When a field contains an array, you can query it directly with a value. MongoDB will match any document where the array contains that value.
js
// Find users who have the "editor" role
db.users.find({ roles: "editor" })
// Find users who have both "editor" and "admin" roles
db.users.find({ roles: { $all: ["editor", "admin"] } })Querying Nested Documents
When a document contains embedded objects, use dot notation to query fields inside them. The field path must be in quotes.
js
// Users whose preferences are set to dark theme
db.users.find({ "preferences.theme": "dark" })
// Users whose preferences language is "en" and theme is "dark"
db.users.find({
"preferences.language": "en",
"preferences.theme": "dark"
})Dot notation also works with comparison operators:
js
// Products with a rating score greater than 4
db.products.find({ "rating.score": { $gt: 4 } })Projection — Selecting Specific Fields
By default, find() returns all fields. Pass a second argument to include or exclude fields. Use 1 to include and 0 to exclude.
js
// Return only station names and distance
db.trips.find({}, { departureStationName: 1, returnStationName: 1, coveredDistance: 1, _id: 0 })
// Return everything except the duration field
db.trips.find({}, { duration: 0 })SQL equivalent:
sql
SELECT departureStationName, returnStationName, coveredDistance FROM tripsWARNING
You cannot mix includes and excludes in the same projection, except for _id which can always be excluded.
Counting Documents
js
// Count all trips
db.trips.countDocuments({})
// Count trips departing from Karhupuisto
db.trips.countDocuments({ departureStationName: "Karhupuisto" })SQL equivalent:
sql
SELECT COUNT(*) FROM trips WHERE departureStationName = 'Karhupuisto'Sorting
Use .sort() to order results. Use 1 for ascending and -1 for descending.
js
// Sort by distance, shortest first
db.trips.find().sort({ coveredDistance: 1 })
// Sort by duration, longest first
db.trips.find().sort({ duration: -1 })
// Sort by multiple fields — first by departure station, then by distance
db.trips.find().sort({ departureStationName: 1, coveredDistance: -1 })SQL equivalent:
sql
SELECT * FROM trips ORDER BY departureStationName ASC, coveredDistance DESCComparison Operators
Use comparison operators as the value of a field filter.
| Operator | Meaning |
|---|---|
$eq | Equal to |
$ne | Not equal to |
$gt | Greater than |
$gte | Greater than or equal to |
$lt | Less than |
$lte | Less than or equal to |
$in | Matches any value in an array |
$nin | Does not match any value in an array |
Examples
js
// Trips longer than 1000 meters
db.trips.find({ coveredDistance: { $gt: 1000 } })
// Trips departing from Karhupuisto or Hietalahdentori
db.trips.find({ departureStationName: { $in: ["Karhupuisto", "Hietalahdentori"] } })Logical Operators
| Operator | Meaning |
|---|---|
$and | Returns documents matching all conditions |
$or | Returns documents matching any condition |
$not | Returns documents that do not match the condition |
$nor | Returns documents that match neither condition |
Example
js
// Trips from Karhupuisto that are longer than 500 meters
db.trips.find({ $and: [
{ departureStationName: "Karhupuisto" },
{ coveredDistance: { $gt: 500 } }
]})SQL equivalent:
sql
SELECT * FROM trips WHERE departureStationName = 'Karhupuisto' AND coveredDistance > 500Existence Check
Use $exists to filter by whether a field is present in the document.
js
// Trips where the 'coveredDistance' field exists
db.trips.find({ coveredDistance: { $exists: true } })
// Trips where the 'coveredDistance' field does NOT exist
db.trips.find({ coveredDistance: { $exists: false } })Date and Time Queries
MongoDB stores dates as ISODate objects. You can create them in the shell with ISODate() or new Date().
Filtering by Date
Use comparison operators to filter by date, just like with numbers.
js
// Trips departing after July 31, 2025
db.trips.find({ departure: { $gt: ISODate("2025-07-31") } })
// Trips departing before August 2025
db.trips.find({ departure: { $lt: ISODate("2025-08-01") } })Date Range
Combine $gte and $lte to query a date range.
js
// Trips during July 2025
db.trips.find({
departure: {
$gte: ISODate("2025-07-01"),
$lte: ISODate("2025-07-31T23:59:59Z")
}
})SQL equivalent:
sql
SELECT * FROM trips WHERE departure BETWEEN '2025-07-01' AND '2025-07-31 23:59:59'Sorting by Date
js
// Oldest trips first
db.trips.find().sort({ departure: 1 })
// Newest trips first
db.trips.find().sort({ departure: -1 })Pagination
.skip(n) and .limit(n)
| Method | Behaviour |
|---|---|
.skip(n) | Skips the first n results |
.limit(n) | Returns at most n results |
Example — page 2 with 10 trips per page:
js
db.trips.find().skip(10).limit(10)SQL equivalent:
sql
SELECT * FROM trips LIMIT 10, 10Updating Documents
js
// Rename a station in all matching trips
db.trips.updateMany(
{ departureStationName: "Sörnäinen (M)" },
{ $set: { departureStationName: "Sörnäinen" } }
)
// Update one document by its ID
db.trips.updateOne(
{ _id: ObjectId('63e362b9415552734e896049') },
{ $set: { coveredDistance: 1050 } }
)Update Operators
| Operator | Description |
|---|---|
$set | Sets the value of a field |
$unset | Removes a field from the document |
$inc | Increments a numeric field by a given amount |
$push | Adds an element to an array field |
$pull | Removes matching elements from an array field |
Examples
js
// Add 100 meters to a trip's covered distance
db.trips.updateOne(
{ _id: ObjectId('63e362b9415552734e896049') },
{ $inc: { coveredDistance: 100 } }
)
// Remove the 'duration' field from all trips
db.trips.updateMany(
{},
{ $unset: { duration: "" } }
)
// Add a tag to a document's tags array
db.trips.updateOne(
{ _id: ObjectId('63e362b9415552734e896049') },
{ $push: { tags: "weekend" } }
)
// Remove a tag from a document's tags array
db.trips.updateOne(
{ _id: ObjectId('63e362b9415552734e896049') },
{ $pull: { tags: "weekend" } }
)Updating Array Elements with the Positional $ Operator
To update a specific element inside an array, use the positional $ operator. The filter must match the array element you want to update, and $ refers to that matched element.
js
// Change the theme preference for a user whose preferences include "dark"
db.users.updateOne(
{ username: "john.doe", "preferences.theme": "dark" },
{ $set: { "preferences.$.theme": "light" } }
)In this example:
- The filter matches the user and the array element where
preferences.themeis"dark" preferences.$refers to that matched element$setupdates only thethemefield of that specific element
Another example — update a specific role's access level in a roles array:
js
db.users.updateOne(
{ username: "john.doe", "roles.name": "editor" },
{ $set: { "roles.$.accessLevel": 2 } }
)WARNING
The positional $ operator only updates the first matching element in the array.
Arithmetic Operators
These operators can be used in aggregation pipelines and in pipeline-style updates to calculate values from existing fields.
| Operator | Description | Example |
|---|---|---|
$add | Addition | { $add: ["$price", 10] } |
$subtract | Subtraction | { $subtract: ["$price", "$discount"] } |
$multiply | Multiplication | { $multiply: ["$price", "$quantity"] } |
$divide | Division | { $divide: ["$coveredDistance", 1000] } |
$mod | Modulo (remainder) | { $mod: ["$duration", 60] } |
$round | Round to n decimals | { $round: ["$avgSpeed", 1] } |
Updating with Calculated Values
You can use an aggregation pipeline as the update parameter to calculate new field values from existing fields. Wrap the pipeline stages in an array [] instead of {}.
js
// Add average speed (km/h) to all trips
// Formula: (distance in meters / 1000) / (duration in seconds / 3600)
db.trips.updateMany(
{},
[
{ $set: { avgSpeed: { $round: [{ $divide: [
{ $multiply: ["$coveredDistance", 3.6] },
"$duration"
]}, 1] } } }
]
)After this update, each document has a new avgSpeed field:
json
{
"departureStationName": "Isoisänsilta",
"returnStationName": "Vilhonvuorenkatu",
"coveredDistance": 1002,
"duration": 516,
"avgSpeed": 7.0
}Another example — add a cost estimate assuming a price of 0.10 EUR per minute:
js
// Add trip cost (duration in seconds / 60 * price per minute)
db.trips.updateMany(
{},
[
{ $set: { cost: { $round: [{ $multiply: [
{ $divide: ["$duration", 60] },
0.10
]}, 2] } } }
]
)TIP
The $round operator rounds the result to a given number of decimal places. $round: [value, 1] rounds to one decimal, $round: [value, 2] rounds to two decimals.
Deleting Documents
js
// Delete all trips
db.trips.deleteMany({})
// Delete trips shorter than 500 meters
db.trips.deleteMany({ coveredDistance: { $lt: 500 } })
// Delete a single trip by ID
db.trips.deleteOne({ _id: ObjectId('63e362b9415552734e896049') })Aggregation
The aggregation pipeline processes documents through a sequence of stages. Each stage transforms the data and passes the result to the next stage.
js
db.collection.aggregate([ stage1, stage2, ... ])Common Stages
| Stage | Description |
|---|---|
$match | Filters documents (like find) |
$group | Groups documents and calculates aggregate values |
$sort | Sorts the results |
$project | Reshapes documents — include, exclude, or rename fields |
$limit | Limits the number of results |
$unwind | Deconstructs an array field into separate documents |
Individual Stage Examples
$match — filter documents before further processing:
js
// Only pass trips longer than 1 km to the next stage
db.trips.aggregate([
{ $match: { coveredDistance: { $gt: 1000 } } }
])$group — group documents and calculate values:
js
// Total distance per departure station
db.trips.aggregate([
{ $group: { _id: "$departureStationName", totalDistance: { $sum: "$coveredDistance" } } }
])$sort — order the results:
js
// Sort trips by duration, longest first
db.trips.aggregate([
{ $sort: { duration: -1 } }
])$project — include, exclude, or rename fields:
js
// Show only station names and distance in km
db.trips.aggregate([
{ $project: { departureStationName: 1, returnStationName: 1, distanceKm: { $divide: ["$coveredDistance", 1000] }, _id: 0 } }
])$limit — return only the first n documents:
js
// Return only the first 5 trips
db.trips.aggregate([
{ $limit: 5 }
])$unwind — expand an array field into separate documents:
js
// Each role in the array becomes its own document
db.users.aggregate([
{ $unwind: "$roles" }
])Step-by-Step Example — Trips per Station
Suppose we have a trips collection with the following documents:
json
[
{ "_id": 1, "departureStationName": "Isoisänsilta", "returnStationName": "Vilhonvuorenkatu", "coveredDistance": 1002, "duration": 516 },
{ "_id": 2, "departureStationName": "Karhupuisto", "returnStationName": "Sörnäinen (M)", "coveredDistance": 712, "duration": 224 },
{ "_id": 3, "departureStationName": "Hietalahdentori", "returnStationName": "Apollonkatu", "coveredDistance": 1805, "duration": 323 },
{ "_id": 4, "departureStationName": "Karhupuisto", "returnStationName": "Isoisänsilta", "coveredDistance": 950, "duration": 412 },
{ "_id": 5, "departureStationName": "Isoisänsilta", "returnStationName": "Karhupuisto", "coveredDistance": 1100, "duration": 380 }
]We want to find the average distance per departure station, sorted highest first. The pipeline:
js
db.trips.aggregate([
{ $group: { _id: "$departureStationName", avgDistance: { $avg: "$coveredDistance" }, tripCount: { $sum: 1 } } },
{ $sort: { avgDistance: -1 } },
{ $project: { _id: 0, station: "$_id", avgDistance: 1, tripCount: 1 } }
])Here is what the data looks like after each stage:
Stage 1 — $group: group by departure station, calculate average distance and trip count.
json
[
{ "_id": "Hietalahdentori", "avgDistance": 1805, "tripCount": 1 },
{ "_id": "Isoisänsilta", "avgDistance": 1051, "tripCount": 2 },
{ "_id": "Karhupuisto", "avgDistance": 831, "tripCount": 2 }
]Stage 2 — $sort: sort by average distance, highest first.
json
[
{ "_id": "Hietalahdentori", "avgDistance": 1805, "tripCount": 1 },
{ "_id": "Isoisänsilta", "avgDistance": 1051, "tripCount": 2 },
{ "_id": "Karhupuisto", "avgDistance": 831, "tripCount": 2 }
]Stage 3 — $project: rename _id to station and hide _id.
json
[
{ "avgDistance": 1805, "tripCount": 1, "station": "Hietalahdentori" },
{ "avgDistance": 1051, "tripCount": 2, "station": "Isoisänsilta" },
{ "avgDistance": 831, "tripCount": 2, "station": "Karhupuisto" }
]Example — Count Trips per Departure Station
js
db.trips.aggregate([
{ $group: { _id: "$departureStationName", count: { $sum: 1 } } }
])SQL equivalent:
sql
SELECT departureStationName, COUNT(*) AS count FROM trips GROUP BY departureStationNameExample — Average Duration of Trips from Karhupuisto
js
db.trips.aggregate([
{ $match: { departureStationName: "Karhupuisto" } },
{ $group: { _id: null, avgDuration: { $avg: "$duration" } } }
])SQL equivalent:
sql
SELECT AVG(duration) AS avgDuration FROM trips WHERE departureStationName = 'Karhupuisto'Example — $unwind with Array Fields
$unwind deconstructs an array field so that each array element becomes its own document. This is useful when you need to group or count by values inside arrays.
Given documents with a roles array:
json
[
{ "_id": 1, "username": "alice", "roles": ["admin", "editor"] },
{ "_id": 2, "username": "bob", "roles": ["editor"] },
{ "_id": 3, "username": "carol", "roles": ["admin", "user"] }
]Count how many users have each role:
js
db.users.aggregate([
{ $unwind: "$roles" },
{ $group: { _id: "$roles", count: { $sum: 1 } } },
{ $sort: { count: -1 } }
])After $unwind, the data is expanded — each role gets its own document:
json
[
{ "_id": 1, "username": "alice", "roles": "admin" },
{ "_id": 1, "username": "alice", "roles": "editor" },
{ "_id": 2, "username": "bob", "roles": "editor" },
{ "_id": 3, "username": "carol", "roles": "admin" },
{ "_id": 3, "username": "carol", "roles": "user" }
]After $group and $sort, the final result:
json
[
{ "_id": "admin", "count": 2 },
{ "_id": "editor", "count": 2 },
{ "_id": "user", "count": 1 }
]Example — Top 3 Longest Trips
js
db.trips.aggregate([
{ $sort: { coveredDistance: -1 } },
{ $limit: 3 },
{ $project: { departureStationName: 1, returnStationName: 1, coveredDistance: 1, _id: 0 } }
])