Query MongoDB - Web SDK
On this page
- Prerequisites
- Set Up Your Project
- Set Up Your Project
- Link a MongoDB Atlas Service Cluster
- Import Realm Dependencies
- Instantiate a MongoDB Collection Handle
- Example Data
- Create Documents
- Insert a Single Document
- Insert Multiple Documents
- Read Documents
- Find a Single Document
- Find Multiple Documents
- Count Documents in the Collection
- Update Documents
- Update a Single Document
- Update Multiple Documents
- Upsert Documents
- Delete Documents
- Delete a Single Document
- Delete Multiple Documents
- Watch for Changes
- Watch for Changes in a Collection
- Watch for Changes in a Collection with a Filter
- Aggregate Documents
- Aggregate Documents in a Collection
- Aggregation Stages
- Filter Documents
- Group Documents
- Paginate Documents
- Project Document Fields
- Add Fields to Documents
- Unwind Array Values
You can query data stored in MongoDB Atlas directly from your client application code by using the Realm Web SDK's MongoDB client with the Query API. Atlas App Services provides data access rules on collections to securely retrieve results based on the logged-in user or the content of each document.
The following actions let you query a linked MongoDB Atlas cluster from a Web app using the Realm Web SDK.
Note
Each operation described on this page uses a query to
match certain documents in the collection upon which the operation
executes. When a filter matches multiple documents in a collection,
they are returned in an indeterminate order unless you
specify a sorting parameter. This means that if you do not specify
a sort for the findOne()
, updateOne()
, or deleteOne()
functions, your operation could match any document that matches the
query. For more information on sorting, see
cursor.sort().
Prerequisites
Before you can query MongoDB from your web application, you must set up MongoDB Data Access in your App Services App. To learn how to set up your backend App to let the Realm SDK query Atlas, refer to Set Up MongoDB Data Access in the App Services documentation.
Set Up Your Project
Set Up Your Project
Follow the steps in the Install Realm for Web guide.
Link a MongoDB Atlas Service Cluster
Follow the steps in the Link a Data Source guide. Assign your service a meaningful name -- you'll need it to connect to the cluster using the Realm SDK.
Example Data
The examples on this page use the following MongoDB collection that describes various plants for sale in a chain of plant stores:
{ _id: ObjectId("5f87976b7b800b285345a8b4"), name: "venus flytrap", sunlight: "full", color: "white", type: "perennial", _partition: "Store 42", }, { _id: ObjectId("5f87976b7b800b285345a8b5"), name: "sweet basil", sunlight: "partial", color: "green", type: "annual", _partition: "Store 42", }, { _id: ObjectId("5f87976b7b800b285345a8b6"), name: "thai basil", sunlight: "partial", color: "green", type: "perennial", _partition: "Store 42", }, { _id: ObjectId("5f87976b7b800b285345a8b7"), name: "helianthus", sunlight: "full", color: "yellow", type: "annual", _partition: "Store 42", }, { _id: ObjectId("5f87976b7b800b285345a8b8"), name: "petunia", sunlight: "full", color: "purple", type: "annual", _partition: "Store 47", },
Create Documents
These code snippets demonstrate how to insert one or more documents into a MongoDB collection from a web app. Insert operations take a document or documents to add to MongoDB as an argument and return a Promise that resolves to an object that contains the results of the execution of the operation.
Insert a Single Document
You can insert a single document by calling collection.insertOne().
The following snippet inserts a single document describing a "lily of the valley" plant into a collection of documents that describe plants for sale in a group of stores:
const result = await plants.insertOne({ name: "lily of the valley", sunlight: "full", color: "white", type: "perennial", _partition: "Store 47", }); console.log(result);
Running this snippet produces output resembling the following:
{ insertedId: ObjectId("5f879f83fc9013565c23360e") }
Insert Multiple Documents
You can insert multiple documents at the same time using collection.insertMany().
The following snippet inserts three documents describing plants into a collection of documents that describe plants for sale in a group of stores:
const result = await plants.insertMany([ { name: "rhubarb", sunlight: "full", color: "red", type: "perennial", _partition: "Store 47", }, { name: "wisteria lilac", sunlight: "partial", color: "purple", type: "perennial", _partition: "Store 42", }, { name: "daffodil", sunlight: "full", color: "yellow", type: "perennial", _partition: "Store 42", }, ]); console.log(result);
Running this snippet produces output resembling the following:
{ insertedIds: [ ObjectId("5f87a0defc9013565c233611"), ObjectId("5f87a0defc9013565c233612"), ObjectId("5f87a0defc9013565c233613"), ], }
Read Documents
These code snippets demonstrate how to read data stored in a MongoDB
collection from a mobile application. Read operations use query
filters to specify which
documents to return from the database. Read operations return a
Promise that
resolves to one of the following: a single matched document (in the case of
findOne()
), a numeric value (in the case of count()
) or an array
of matched documents (in the case of find()
).
Find a Single Document
You can find a single document using collection.findOne().
The following snippet finds the document that describes venus flytraps in the collection of documents that describe plants for sale in a group of stores:
const venusFlytrap = await plants.findOne({ name: "venus flytrap" }); console.log("venusFlytrap", venusFlytrap);
Running this snippet produces output resembling the following:
{ _id: ObjectId("5f87976b7b800b285345a8b4"), name: "venus flytrap", sunlight: "full", color: "white", type: "perennial", _partition: "Store 42", }
Find Multiple Documents
You can find multiple documents using collection.find().
The following snippet finds all documents in a
collection of documents that describe plants for sale in a group of stores that contain a field named
type
with a value of "perennial":
const perennials = await plants.find({ type: "perennial" }); console.log("perennials", perennials);
Running this snippet produces output resembling the following:
[ { _id: ObjectId("5f87976b7b800b285345a8b4"), name: "venus flytrap", sunlight: "full", color: "white", type: "perennial", _partition: "Store 42", }, { _id: ObjectId("5f87976b7b800b285345a8b6"), name: "thai basil", sunlight: "partial", color: "green", type: "perennial", _partition: "Store 42", }, ]
Count Documents in the Collection
You can count documents in a collection using collection.count(). You can specify an optional query to determine which documents to count. If you don't specify a query, the action counts all documents in the collection.
The following snippet counts the number of documents in a collection of documents that describe plants for sale in a group of stores:
const numPlants = await plants.count(); console.log(`There are ${numPlants} plants in the collection`);
Running this snippet produces output resembling the following:
"There are 5 plants in the collection"
Update Documents
These code snippets demonstrate how to update data stored in a MongoDB collection from a mobile application. Update operations use queries to specify which documents to update and update operators to describe how to mutate documents that match the query. Update operations return a Promise that resolves to an object that contains the results of the execution of the operation.
Update a Single Document
You can update a single document using collection.updateOne().
The following snippet updates a single document in a
collection of documents that describe plants for sale in a group of stores. This operation queries for a document
where the name
field contains the value "petunia" and changes the value
of the first matched document's sunlight
field to "partial":
const result = await plants.updateOne( { name: "petunia" }, { $set: { sunlight: "partial" } } ); console.log(result);
Running this snippet produces output resembling the following:
{ matchedCount: 1, modifiedCount: 1 }
Update Multiple Documents
You can update a single document using collection.updateMany().
The following snippet updates multiple documents in a
collection of documents that describe plants for sale in a group of stores. This operation queries for documents
where the _partition
field contains the value "Store 42" and changes
the value of the _partition
field of each matching document to "Store 51":
const result = await plants.updateMany( { _partition: "Store 42" }, { $set: { _partition: "Store 51" } } ); console.log(result);
Running this snippet produces output resembling the following:
{ matchedCount: 4, modifiedCount: 4 }
Upsert Documents
If an update operation does not match any document in the collection,
you can automatically insert a single new document into the collection
that matches the update query by setting the upsert
option to
true
.
The following snippet updates a document in a collection of documents that describe plants for sale in a group of stores or inserts a new document if no document matches the query. This operation queries for documents where:
the
sunlight
field has a value of "full"the
type
field has a value of "perennial"the
color
field has a value of "green"the
_partition
field has a value of "Store 47"
Because this snippet sets the upsert
option to true
, if no
document matches the query, MongoDB creates a new document that includes
both the query and specified updates:
const result = await plants.updateOne( { sunlight: "full", type: "perennial", color: "green", _partition: "Store 47", }, { $set: { name: "super sweet basil" } }, { upsert: true } ); console.log(result);
Running this snippet produces output resembling the following:
{ matchedCount: 0, modifiedCount: 0, upsertedId: ObjectId("5f1f63055512f2cb67f460a3"), }
Delete Documents
These code snippets demonstrate how to delete documents that are stored in a MongoDB collection from a mobile application. Delete operations use a query to specify which documents to delete and return a Promise that resolves to an object that contains the results of the execution of the operation.
Delete a Single Document
You can delete a single document from a collection using collection.deleteOne().
The following snippet deletes one document in a
collection of documents that describe plants for sale in a group of stores. This operation queries for a
document where the color
field has a value of "green" and deletes
the first document that matches the query:
const result = await plants.deleteOne({ color: "green" }); console.log(result);
Running this snippet produces output resembling the following:
{ deletedCount: 1 }
Delete Multiple Documents
You can delete multiple items from a collection using collection.deleteMany().
The following snippet deletes all documents for plants that are in "Store 42" in a collection of documents that describe plants for sale in a group of stores:
const result = await plants.deleteMany({ _partition: "Store 42", }); console.log(result);
Running this snippet produces output resembling the following:
{ deletedCount: 4 }
Watch for Changes
You can call collection.watch() to subscribe to real-time notification events whenever a document in the collection is added, modified, or deleted. Each notification specifies a document that changed, how it changed, and the full document after the operation that caused the event.
Important
Serverless Limitations
You cannot watch for changes if the data source is an Atlas serverless instance. MongoDB serverless currently does not support change streams, which are used on watched collections to listen for changes.
Atlas App Services uses MongoDB change streams on watched collections to listen for changes and broadcasts notifications to subscribed client applications. This is useful for cases where you want to know when something happened while a user is online. For example:
Track the location of a delivery
Get the most up-to-date score and stats for a game
Update a chat thread when a user sends a new message
Note
collection.watch()
returns an async generator
that allows you to asynchronously pull change events for operations as they occur.
Watch for Changes in a Collection
To watch for all changes in a collection, call collection.watch() without any arguments:
for await (const change of plants.watch()) { let breakAsyncIterator = false; // Later used to exit async iterator switch (change.operationType) { case "insert": { const { documentKey, fullDocument } = change; console.log(`new document: ${documentKey}`, fullDocument); breakAsyncIterator = true; break; } case "update": { const { documentKey, fullDocument } = change; console.log(`updated document: ${documentKey}`, fullDocument); breakAsyncIterator = true; break; } case "replace": { const { documentKey, fullDocument } = change; console.log(`replaced document: ${documentKey}`, fullDocument); breakAsyncIterator = true; break; } case "delete": { const { documentKey } = change; console.log(`deleted document: ${documentKey}`); breakAsyncIterator = true; break; } } if (breakAsyncIterator) break; // Exit async iterator }
Watch for Changes in a Collection with a Filter
To watch for specific changes in a collection, call collection.watch() with a query that specifies change event values to watch:
for await (const change of plants.watch({ filter: { operationType: "update", "fullDocument.type": "perennial", }, })) { // The change event will always represent a newly inserted perennial const { documentKey, fullDocument } = change; console.log(`new document: ${documentKey}`, fullDocument); break; // Exit async iterator }
Aggregate Documents
Aggregation operations run all documents in a collection through a series of stages called an aggregation pipeline. Aggregation allows you to filter and transform documents, collect summary data about groups of related documents, and other complex data operations.
Aggregation operations accept a list of aggregation stages as input, and return a Promise that resolves to a collection of documents processed by the pipeline.
Aggregate Documents in a Collection
You can execute an aggregation pipeline using collection.aggregate().
The following snippet groups all documents in the plants
collection by their type
value and aggregates a count of the
number of each type:
const result = await plants.aggregate([ { $group: { _id: "$type", total: { $sum: 1 }, }, }, { $sort: { _id: 1 } }, ]); console.log(result);
Running this snippet produces output resembling the following:
[ { _id: "annual", total: 3 }, { _id: "perennial", total: 2 }, ]
Aggregation Stages
Filter Documents
You can use the $match stage to filter documents according to standard MongoDB query syntax.
{ "$match": { "<Field Name>": <Query Expression>, ... } }
Example
The following $match
stage filters documents to include
only those where the type
field has a value equal to "perennial":
const perennials = await plants.aggregate([ { $match: { type: { $eq: "perennial" } } }, ]); console.log(perennials);
Group Documents
You can use the $group stage to aggregate summary
data for one or more documents. MongoDB groups documents based
on the expression defined in the _id
field of the $group
stage.
You can reference a specific document field by prefixing the field name
with a $
.
{ "$group": { "_id": <Group By Expression>, "<Field Name>": <Aggregation Expression>, ... } }
Example
The following $group
stage arranges documents by the value of their
type
field and calculates the number of plant documents
that each unique type
value appears in.
const result = await plants.aggregate([ { $group: { _id: "$type", numItems: { $sum: 1 }, }, }, { $sort: { _id: 1 } }, ]); console.log(result);
Paginate Documents
To paginate results, you can use range aggregation queries with the $match
,
$sort
, and $limit
operators. To learn more about paginating documents,
refer to Using Range Queries
in the MongoDB Server documentation.
Example
The following example paginates through a collection of documents in ascending order.
// Paginates through list of plants // in ascending order by plant name (A -> Z) async function paginateCollectionAscending( collection, nPerPage, startValue ) { const pipeline = [{ $sort: { name: 1 } }, { $limit: nPerPage }]; // If not starting from the beginning of the collection, // only match documents greater than the previous greatest value. if (startValue !== undefined) { pipeline.unshift({ $match: { name: { $gt: startValue }, }, }); } const results = await collection.aggregate(pipeline); return results; } // Number of results to show on each page const resultsPerPage = 3; const pageOneResults = await paginateCollectionAscending( plants, resultsPerPage ); const pageTwoStartValue = pageOneResults[pageOneResults.length - 1].name; const pageTwoResults = await paginateCollectionAscending( plants, resultsPerPage, pageTwoStartValue ); // ... can keep paginating for as many plants as there are in the collection
Project Document Fields
You can use the $project stage to include or omit specific fields from documents or to calculate new fields using aggregation operators. Projections work in two ways:
Explicitly include fields with a value of 1. This has the side-effect of implicitly excluding all unspecified fields.
Implicitly exclude fields with a value of 0. This has the side-effect of implicitly including all unspecified fields.
These two methods of projection are mutually exclusive: if you explicitly include fields, you cannot explicitly exclude fields, and vice versa.
Note
The _id
field is a special case: it is always included in every
query unless explicitly specified otherwise. For this reason, you
can exclude the _id
field with a 0
value while simultaneously
including other fields, like _partition
, with a 1
. Only the
special case of exclusion of the _id
field allows both exclusion
and inclusion in one $project
stage.
{ "$project": { "<Field Name>": <0 | 1 | Expression>, ... } }
Example
The following $project
stage omits the _id
field, includes
the name
field, and creates a new field named storeNumber
.
The storeNumber
is generated using two aggregation operators:
$split
separates the_partition
value into two string segments surrounding the space character. For example, the value "Store 42" split in this way returns an array with two elements: "Store" and "42".$arrayElemAt
selects a specific element from an array based on the second argument. In this case, the value1
selects the second element from the array generated by the$split
operator since arrays index from0
. For example, the value ["Store", "42"] passed to this operation would return a value of "42".
const result = await plants.aggregate([ { $project: { _id: 0, name: 1, storeNumber: { $arrayElemAt: [{ $split: ["$_partition", " "] }, 1], }, }, }, ]); console.log(result);
Add Fields to Documents
You can use the $addFields stage to add new fields with calculated values using aggregation operators.
{ $addFields: { <newField>: <expression>, ... } }
Note
$addFields
is similar to $project but does not allow you to
include or omit fields.
Example
The following $addFields
stage creates a new field named
storeNumber
where the value is the output of two aggregate operators
that transform the value of the _partition
field.
const result = await plants.aggregate([ { $addFields: { storeNumber: { $arrayElemAt: [{ $split: ["$_partition", " "] }, 1], }, }, }, ]); console.log(result);
Unwind Array Values
You can use the $unwind stage to transform a single document containing an array into multiple documents containing individual values from that array. When you unwind an array field, MongoDB copies each document once for each element of the array field but replaces the array value with the array element in each copy.
{ $unwind: { path: <Array Field Path>, includeArrayIndex: <string>, preserveNullAndEmptyArrays: <boolean> } }
Example
The following example uses the $unwind
stage for each object's type
and color
combination. The aggregation pipeline has the following steps:
Use
$group
stage with$addToSet
to create new documents for eachtype
with a new fieldcolors
that contains an array of all the the colors for that flower type that occur in the collection.Use
$unwind
stage to create separate documents for each combination of type and color.Use
$sort
stage to sort the results in alphabetical order.
const result = await plants.aggregate([ { $group: { _id: "$type", colors: { $addToSet: "$color" } } }, { $unwind: { path: "$colors" } }, { $sort: { _id: 1, colors: 1 } }, ]); console.log(result);