MongoDB store data as documents.
On this page
Introduction
A document is a data structure composed of field and value pairs. The value of a field can be a simple data type, such as a string or a number, or it can be a more complex data type, such as an array or another document.
Below you have an example of a JSON document that describes the employee Laura:
{ "_id": "laura", "firstName": "Laura", "lastName": "Codina", "email": "laura@acme.com", "department": "Finance"}Documents in document databases are self-describing, which means they contain both data values and information about what type of data is stored.
Below you have a document from another employee who works in several departments:
{ "_id": "roser", "firstName": "Roser", "lastName": "Vilalta", "email": "roser@acme.com", "salary": 70000, "department": ["Finance", "Accounting"]}This second document has some differences compared to the first example.
In document databases:
- Documents not only describe themselves, but also their schema is dynamic, which means you don’t need to define it before starting to save data.
- Fields can differ between different documents in the same database, and you can modify the document structure at will, adding or removing fields as you go.
- Documents can also be nested, that is, a field of a document can have a value consisting of another document, allowing you to store complex data within a single document entry.
{ "_id": "david", "firstName": "David", "lastName": "de Mingo", "email": "david@acme.com", "department": ["Finance", "Accounting"], "socialMediaAccounts": [ { "type": "facebook", "username": "david_de_mingo" }, { "type": "twitter", "username": "@ddemingo" } ]}WSL
Start a new Windows Subsystem for Linux (WSL) terminal.
wslInstall
Create a /etc/yum.repos.d/mongodb-org-8.2.repo file so that you can install MongoDB directly using dnf:
sudo tee /etc/yum.repos.d/mongodb-org-8.2.repo > /dev/null <<EOF[mongodb-org-8.2]name=MongoDB Repositorybaseurl=https://repo.mongodb.org/yum/redhat/9/mongodb-org/8.2/x86_64/gpgcheck=1enabled=1gpgkey=https://pgp.mongodb.com/server-8.0.ascEOFInstall the latest stable version of MongoDB:
sudo dnf install -y mongodb-orgStart
You can start the mongod process by issuing the following command:
sudo systemctl start mongodYou can verify that the mongod process has started successfully by issuing the following command:
sudo systemctl status mongodYou can optionally ensure that MongoDB will start following a system reboot by issuing the following command:
sudo systemctl enable mongodShell
Start a mongosh session on the same host machine as the mongod.
You can run mongosh without any command-line options to connect to a mongod that is running on your localhost with default port 27017.
mongoshCurrent Mongosh Log ID: 69add5cb8a7690371a8563b0Connecting to: mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.7.0Using MongoDB: 8.2.5Using Mongosh: 2.7.0
...test>This is equivalent to the following command:
mongosh "mongodb://localhost:27017"You can verify your current database connection using the db.getMongo() method.
test> db.getMongo()mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.7.0The method returns the connection string URI for your current connection:
Databases and collections
MongoDB stores data as documents in JSON (JavaScript Object Notation) format. When working with different types of data - like users, orders, or products – you’ll want to organize them separately rather than mixing them.
Collections are MongoDB’s way of grouping related documents, similar to how tables work in relational databases.
For example:
- All user documents go in a
userscollection - All order documents go in an
orderscollection - All product documents go in a
productscollection
Databases in MongoDB act as containers that hold multiple collections. Think of them as simple namespaces that help organize your collections – they don’t have the heavy overhead that traditional SQL databases often have.
To work with data in MongoDB, you need to specify:
- Database name (the namespace)
- Collection name (the group of documents)
When you start MongoDB without specifying a database, it automatically uses a default database called test.
To show the database you are using, type db:
> dbtestThe operation should return test, which is the default database.
Next, create a new database to keep all the activity exercises in the same namespace:
test> use demoswitched to db demodemo>You will see a message verifying that you have changed databases.
Insert
It’s time to create your first document. Since you are using a JavaScript shell, your documents will be specified in JSON. For example, a simple document describing a user might look like this:
{ name: "laura" }The document contains a single key and value to store Laura’s username.
To save this document, you need to choose a collection to save it to. Appropriately enough, you’ll save it to the users collection.
demo> db.users.insertOne({name: "laura"}){ acknowledged: true, insertedId: ObjectId('678171de72c54e457acb0ce2')}If a collection doesn’t exist, MongoDB creates it the first time you store data in that collection.
If the insertion is successful, you have just saved your first document.
You can make a query to see the new document:
demo> db.users.find()[ { _id: ObjectId('678171de72c54e457acb0ce2'), name: 'laura' } ]Close the connection with exit or quit.
demo> exitStart the mongod again, and start a new mongosh session.
sudo systemctl restart mongodmongoshSince the data is now part of the users collection, reopening the shell and executing the query will show the same result:
test> use demoswitched to db demodemo> db.users.find()[ { _id: ObjectId('678171de72c54e457acb0ce2'), name: 'laura' } ]Note that an _id field has been added to the document. You can think of the _id value as the document’s primary key. Every MongoDB document requires an _id, and if there isn’t one when the document is created, an ObjectID will be generated and added to the document at that time.
The ObjectID that appears in your shell won’t be the same as the one in the code listing, but it will be unique among all _id values in the collection, which is the only requirement for the field. You can set your own _id by setting it in the document you insert, the ObjectID is just MongoDB’s default
Add a new user to the collection:
demo> db.users.insertOne({name: "david"}){ acknowledged: true, insertedId: ObjectId('6781722272c54e457acb0ce3')}Now there should be two documents in the collection. Go ahead and check it by running the count command:
demo> db.users.countDocuments()2Query
Now that you have more than one document in the collection, let’s look at some slightly more sophisticated queries. As before, you can still query all documents in the collection:
demo> db.users.find()[ { _id: ObjectId('678171de72c54e457acb0ce2'), name: 'laura' }, { _id: ObjectId('6781722272c54e457acb0ce3'), name: 'david' }]You can also pass a simple query selector to the find method. A query selector is a document used to match all documents in the collection. To query all documents where the username is laura, pass a simple document that acts as a query selector like this:
demo> db.users.find({name:"laura"})[ { _id: ObjectId('678171de72c54e457acb0ce2'), name: 'laura' } ]The query predicate {username: "laura"} returns all documents where username is laura.
Note that calling the find method without any arguments is equivalent to passing an empty predicate: db.users.find() is the same as db.users.find({}).
Next, add some more users with insertMany:
demo> db.users.insertMany([ {name:"laura", age: 34}, {name:"roser", email: "roser@gmail.com"}]){ acknowledged: true, insertedIds: { '0': ObjectId('678172be72c54e457acb0ce4'), '1': ObjectId('678172be72c54e457acb0ce5') }}The MongoDB shell adds the three dots after the first line of the query to indicate that the command takes more than one line.
You can also specify multiple fields in the query predicate, which creates an implicit AND between the fields.
For example, a query with the following selector:
demo> db.users.find({name:"laura", age:34})[ { _id: ObjectId('678172be72c54e457acb0ce4'), name: 'laura', age: 34 }]The query returns all documents where name equals "laura" and age equals 34.
You can also use the $and operator explicitly.
The previous query is identical to:
demo> db.users.find({ $and: [{name:"laura",age:34}] })[ { _id: ObjectId('678172be72c54e457acb0ce4'), name: 'laura', age: 34 }]Selecting documents with an OR is similar: use the $or operator.
Consider the following query:
demo> db.users.find({ $or: [ {name: "david"}, {age: 34} ] })[ { _id: ObjectId('6781722272c54e457acb0ce3'), name: 'david' }, { _id: ObjectId('678172be72c54e457acb0ce4'), name: 'laura', age: 34 }]The query returns the david and laura documents, because we asked for a name of david or an age of 34.
This example is different from the previous ones because it doesn’t just insert or search for a specific document. Rather, the query itself is a document.
The idea of representing commands as documents is often used in MongoDB and may surprise you if you are used to relational databases.
One advantage of this interface is that it’s easier to create queries programmatically in your application because they are documents instead of a long SQL string.
Update
All updates require at least two arguments:
- The first specifies which documents to update,
- and the second defines how the selected documents should be modified. T
The first examples show modifying a single document, but the same operations can be applied to many documents, even an entire collection, as we show at the end of this section. But note that by default the update() method updates a single document.
There are two general types of updates with different properties and use cases:
- One type of update involves applying modification operations to a document or documents
- and the other type involves replacing the old document with a new one.
Modify
The first type of update involves passing a document with some type of operator description as the second argument to the update function.
In this section, you’ll see an example of how to use the $set operator, which sets a single field with the specified value.
Suppose user David decides to add his country of residence.
With updateOne you can modify a document:
demo> db.users.updateOne({name:"david"}, {$set: {country: "Spain"}}){ acknowledged: true, insertedId: null, matchedCount: 1, modifiedCount: 1, upsertedCount: 0}This update tells MongoDB to find a document where the username is “david” and then set the value of the country property to Spain. You can see that the change is reflected in the message the server sends back (modifiedCount: 1).
If you now make a query, you can see that the document has been updated:
demo> db.users.find({name:"david"})[ { _id: ObjectId('6781722272c54e457acb0ce3'), name: 'david', country: 'Spain' }]Next let’s update laura’s country of residence:
demo> db.users.updateOne({name:"laura"}, {$set: {country: "Spain"}}){ acknowledged: true, insertedId: null, matchedCount: 1, modifiedCount: 1, upsertedCount: 0}demo> db.users.find({name:"laura"})[ { _id: ObjectId('678171de72c54e457acb0ce2'), name: 'laura', country: 'Spain' }, { _id: ObjectId('678172be72c54e457acb0ce4'), name: 'laura', age: 34 }]The updateOne method only updates the first document it finds.
If you repeat the operation again, you can see that nothing happens:
demo> db.users.updateOne({name:"laura"}, {$set: {country: "Spain"}}){ acknowledged: true, insertedId: null, matchedCount: 1, modifiedCount: 0, upsertedCount: 0}You can see that a document was found matchedCount: 1, but it was not modified modifiedCount: 0.
If you want to update all lauras at once, you can do it with the updateMany method, but instead of that we’ll update only the laura who is 34 years old:
demo> db.users.updateOne({name:"laura", age: 34}, {$set: {country: "Spain"}}){ acknowledged: true, insertedId: null, matchedCount: 1, modifiedCount: 1, upsertedCount: 0}demo> db.users.find({name:"laura"})[ { _id: ObjectId('678171de72c54e457acb0ce2'), name: 'laura', country: 'Spain' }, { _id: ObjectId('678172be72c54e457acb0ce4'), name: 'laura', age: 34, country: 'Spain' }]Complex data
Documents can contain complex data structures.
Suppose that, in addition to storing profile information, users can store lists of their favorite things.
An example document could be this:
{ name: "roser", favorites: { cities: ["Barcelona", "Girona"], movies: ["Forrest Gump", "The Godfather", "Titanic"] }}The favorites key points to an object that contains two more keys, which point to lists of favorite cities and movies. Given what you already know, can you think of a way to modify roser’s original document?
demo> db.users.updateOne({name: "roser"}, {$set: {... favorites: {... cities: ["Barcelona","Girona"],... movies: ["Forrest Gump", "The Godfather", "Titanic"]... }}}){ acknowledged: true, insertedId: null, matchedCount: 1, modifiedCount: 1, upsertedCount: 0}Note that using spacing for indentation is not mandatory, but it helps avoid errors, as the document is more readable this way.
Let’s modify david in the same way, but in this case you’ll only add a couple of favorite movies:
demo> db.users.updateOne({ name: "david" }, { $set: { favorites: { movies: ["Forrest Gump", "Life of Brian"] }}}){ acknowledged: true, insertedId: null, matchedCount: 1, modifiedCount: 1, upsertedCount: 0}If you make a mistake, you can use the up arrow key to recall the shell’s last statement.
Now query the users collection to make sure both updates were successful:
demo> db.users.find()[... { _id: ObjectId('678172be72c54e457acb0ce5'), name: 'roser', email: 'roser@gmail.com', favorites: { cities: [ 'Barcelona', 'Girona' ], movies: [ 'Forrest Gump', 'The Godfather', 'Titanic' ] } }]Strictly speaking, the find() method returns a cursor to the documents it returns. Therefore, to access the documents you need to iterate the cursor. The find() method automatically returns 20 documents (if available) after iterating the cursor 20 times.
Next query all users who like the movie Forrest Gump:
demo> db.users.find({"favorites.movies": "Forrest Gump"})[ { _id: ObjectId('6781722272c54e457acb0ce3'), name: 'david', country: 'Spain', favorites: { movies: [ 'Forrest Gump', 'Life of Brian' ] } }, { _id: ObjectId('678172be72c54e457acb0ce5'), name: 'roser', email: 'roser@gmail.com', favorites: { cities: [ 'Barcelona', 'Girona' ], movies: [ 'Forrest Gump', 'The Godfather', 'Titanic' ] } }]The dot between favorites and movies tells the query engine to look for a key called favorites that points to an object with an internal key called movies and then match against the value of the internal key. Therefore, this query will return both user documents because queries on lists match if any element in the list matches the original query
Add to set
Suppose you know that any user who likes Forrest Gump also likes Pulp Fiction and you want to update your database to reflect this fact.
Since all you want to do is add an element to the list, you better use $push or $addToSet. Both operators add an element to a list, but the second does it uniquely, avoiding duplicate addition.
demo> db.users.updateMany( {"favorites.movies": "Forrest Gump"},... { $addToSet: {"favorites.movies": "Pulp Fiction"}}){ acknowledged: true, insertedId: null, matchedCount: 2, modifiedCount: 2, upsertedCount: 0}The first argument is a query predicate that matches users who have Forrest Gump in their movie list. The second argument adds Pulp Fiction to that list using the $addToSet operator.
Delete
To delete only a certain subset of documents from a collection you can pass a query selector to the remove() method.
If you want to delete all users whose favorite city is Barcelona, the expression is simple:
demo> db.users.deleteOne({"favorites.cities": "Barcelona"}){ acknowledged: true, deletedCount: 1 }If you want to delete all documents from a collection:
demo> db.users.deleteMany({}){ acknowledged: true, deletedCount: 3 }Note that the deleteMany({}) operation does not actually delete the collection; it only deletes documents from a collection.
If your intention is to delete the collection along with all its indexes, use the drop() method:
demo> db.users.drop()trueOther shell features
You may have already noticed, but the shell does many things to make it easier to work with MongoDB. You can revisit previous commands using the up and down arrows and use auto-completion for certain inputs, such as collection names. The auto-completion feature uses the tab key to auto-complete or to list completion possibilities.
You can also discover more information in the shell by typing this:
> helpMany functions print help messages that also explain them. Try it:
demo> db.help()
Database Class:
getMongo Returns the current database connection getName Returns the name of the DB...
demo> db.getName()demoThere are also several options you can use when starting the MongoDB shell. To show a list of these, add the help flag when starting the shell:
> mongo --helpAdministration
Database information
You’ll often want to know which collections and databases exist in a given installation. Fortunately, the MongoDB shell provides a series of commands, along with some syntactic sugar, to get information about the system.
show dbs prints a list of all databases in the system:
demo> show dbsadmin 40.00 KiBconfig 108.00 KiBdemo 672.00 KiBlocal 72.00 KiBshow collections shows a list of all collections defined in the current database.
demo> show collectionsnumbersTo get a lower-level view of databases and collections, the stats() method is useful. When you run it on a database object, you get the following output:
demo> db.stats(){ db: 'demo', collections: Long('1'), views: Long('0'), objects: Long('20000'), avgObjSize: 31, dataSize: 620000, storageSize: 237568, indexes: Long('2'), indexSize: 409600, totalSize: 647168, scaleFactor: Long('1'), fsUsedSize: 317962870784, fsTotalSize: 510735151104, ok: 1}You can also run the stats() command on an individual collection:
demo> db.numbers.stats(){ ok: 1, capped: false,Some of the values provided in these result documents are only useful in complicated debugging or tuning situations. But, at a minimum, you’ll be able to figure out how much space a given collection and its indexes occupy.
Compass
Compass is an interactive tool to query, optimize, and analyze your MongoDB data.
Install:
> scoop install extras/mongodb-compassSample data
Download the complete sample dataset:
> Set-Variable ProgressPreference SilentlyContinue> curl https://atlas-education.s3.amazonaws.com/sampledata.archive -o sampledata.archiveInstall the administration tools:
> scoop install main/mongodb-database-toolsRun mongorestore to unpack and host a local copy of the sample dataset (the database must be running):
> mongorestore --archive=sampledata.archive> rm .\sampledata.archiveAtlas
MongoDB Atlas provides an easy way to host and manage your data in the cloud.
Check the Atlas UI documentation
In Atlas, you can only connect to a cluster from a trusted IP address.
Change the access IP to 0.0.0.0/0 (any IP)
This link explains how to load the Atlas database with sample data: Load Data into Atlas