MongoDB & Mongoose
What's a database?
- A database is an organized collection of data
- This data can be organized by any criteria
- Using a database we can read, create, update and delete data
Database types
- Now a day we can find different types of databases that fulfill different projects needs
- We can split the big database family into two big categories: Relational or No relational or NoSQL
- The Relational are known as SQL databases (as they use SQL as query language)
- NoSQL databases are No relational
- Each database type will have pros and cons when reading, creating, updating or deleting values
- We need to choose the right database for our job and know why we're going to use it
- In this course section we'll learn MongoDB that's a NoSQL database
Relational Databases assets
- What is a Relational Database?
- Khan Academy - Intro to SQL: Querying and managing data
- SQL for Beginners. Learn basics of SQL in 1 Hour
- W3 Schools
NoSQL
- NoSQL (Not Only SQL) is the given name for databases that are not relationals
- In this database family we can find different types of databases like
key/value, document oriented, grapsh or big tables
- All this databases are prepare to horizontaly scale (this means that we can add more databases servers if we need so)
- In some cases we can even use a simple computer (not much hardware) to solve a business problem
- Martin Fowler gave a great Introduction to NoSQL talk where he explains all this concepts
- This talk is a must to understand this subject
- Also, you can read on his blog about it
MongoDB
- MongoDB is a NoSQL document oriented database
- This database a;low us to store data in JSON format
- It has a flexible schema, this means that we can change/update our documents structure whenever we need it
- This becomes helpful as we develop and we don't have the final data structure
- MongoDB also it's prepared to horizontal scale in a easy way
- As we learned JavaScript we'll use a database engine that allows us to keep on using this language to store our data
Install MongoDB
- To use MongoDB locally we need to download & install it
- Open the documentation to Install MongoDB Community Edition and choose your OS MongoDB version
- Windows
- For Windows, execute the downloaded installer, follow all the default wizard steps and MongoDB will be installed on the following path
C:\Program Files\MongoDB\Server\3.4\.
- To be able to access MongoDB from anywhere in the terminal we need to configure our environments variables
- For Windows, execute the downloaded installer, follow all the default wizard steps and MongoDB will be installed on the following path
- Mac
- For Mac, use Homebrew to install MongoDB
- Linux
- Windows
MongoDB videos
Start MongoDB instance
After adding MongoDB to our path we can start a server instance
- Windows:
mongod.exe
- Linux/Mac:
mongod
- Windows:
Once MongoDB starts will show us server configuration information
It will show the configured database
/data/db
Engine type
wiredTiger
Default MongoDB port
27017
[initandlisten] Detected data files in /data/db created by the 'wiredTiger' storage engine, so setting the active storage engine to 'wiredTiger'
I NETWORK [thread1] waiting for connections on port 27017
MongoDB shell
To interact with MongoDB we need the server runnig (mongod) and a MongoDB shell
Start MongoDB shell running the following command:
- Windows:
mongo.exe
- Linux/Mac:
mongo
- Windows:
Once MongoDB shell connects we'll see a description message:
MongoDB shell version v3.4.2
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.2
>MongoDB shell let us know that it's connected to
mongodb://127.0.0.1:27017
Also shows the shell and server version (
v3.4.2
)To use MongoDB we need the server and shell running
MongoDB shell it's a JavaScript REPL like Node.js one
This means that we can write/execute JavaScript code on it
> 2 + 2
4To start learning mongo shell we can execute the
help
command that will show all the commands that we can use> help
db.help() help on db methods
db.mycoll.help() help on collection methods
sh.help() sharding helpers
rs.help() replica set helpers
help admin administrative help
help connect connecting to a db help
help keys key shortcuts
help misc misc things to know
help mr mapreduce
show dbs show database names
show collections show collections in current database
show users show users in current database
show profile show most recent system.profile entries with time >= 1ms
show logs show the accessible logger names
show log [name] prints out the last segment of log in memory, 'global' is default
use <db_name> set current database
db.foo.find() list objects in collection foo
db.foo.find( { a : 1 } ) list objects in foo where a == 1
it result of the last line evaluated; use to further iterate
DBQuery.shellBatchSize = x set default number of items to display on shell
exit quit the mongo shellOne of the commands it's
show dbs
This command will let show us all the local databases
> show dbs
local 0.000GB
test 0.000GBTo know which database we're using we can call
db
db
it's just a JavaScript variable that has the selected databaseBy default MongoDB will set the initial selected database to
test
> db
test
>MongoDB has databases
MongoDB Databases has collections
A collection is where we can store our documents grouped by some criteria
This means that by using the database and the collection we'll have a namespace
database.collection
For example we can have a
comics
database with asuperheroes
collectionTo access the
superheroes
collection we need to callcomics.superheroes
To create a new datbase we use the
use dabasename
commandIn this case we run
use comics
switched to db comicsThe
use
command will create a new database if it doesn't exists or swith to the selected oneMongoDB will create our databases and collections in a
lazzy
wayThis means that it won't really, really create it until it has some data on it
We can still use the databases and collection while we don't have data on it but it's not persisted
When we change databases MongoDB will change the
db
referenceswitched to db comics
If we type db we can see the selected database
> db
comics
Document create
To create a new document we use the
insertOne
commandThis method accepts a JSON object as parameter
This JSON object represents the document that we want to store in our database collection
Use
db
to let MongoDB in which database we would like to store this documentThis means that we need to be sure that we
use
the right database first> use comics
switched to db comics
> db.superheroes.insertOne({ "name": "SPIDER-MAN", "image": "spiderman.jpg" })
{
"acknowledged" : true,
"insertedId" : ObjectId("5b055c94f9ca342c9eaaf73e")
}Once we inserted a document MongoDB will show us a result message
"acknowledged" : true
means that it was able to store the document"insertedId" : ObjectId("59c6eae6eb2fe8bf77e9c391")
MongoDB creates a new ID for each new documentOur document has a
_id
property with thisinsertedId
valueObjectId
is a data type supported by MongoDB to manage documents idsWe can insert some other documents
> db.superheroes.insertOne({ "name": "CAPTAIN MARVEL", "image": "captainmarvel.jpg" });
{
"acknowledged" : true,
"insertedId" : ObjectId("5b055f19f9ca342c9eaaf73f")
}
> db.superheroes.insertOne({ "name": "HULK", "image": "hulk.jpg" });
{
"acknowledged" : true,
"insertedId" : ObjectId("5b055f19f9ca342c9eaaf740")
}
> db.superheroes.insertOne({ "name": "THOR", "image": "thor.jpg" });
{
"acknowledged" : true,
"insertedId" : ObjectId("5b055f19f9ca342c9eaaf741")
}
> db.superheroes.insertOne({ "name": "IRON MAN", "image": "ironman.jpg" });
{
"acknowledged" : true,
"insertedId" : ObjectId("5b055f19f9ca342c9eaaf742")
}
> db.superheroes.insertOne({ "name": "DAREDEVIL", "image": "daredevil.jpg" });
{
"acknowledged" : true,
"insertedId" : ObjectId("5b055f19f9ca342c9eaaf743")
}
> db.superheroes.insertOne({ "name": "BLACK WIDOW", "image": "blackwidow.jpg" });
{
"acknowledged" : true,
"insertedId" : ObjectId("5b055f19f9ca342c9eaaf744")
}
> db.superheroes.insertOne({ "name": "CAPTAIN AMERICA", "image": "captanamerica.jpg" });
{
"acknowledged" : true,
"insertedId" : ObjectId("5b055f19f9ca342c9eaaf745")
}
> db.superheroes.insertOne({ "name": "WOLVERINE", "image": "wolverine.jpg" });
{
"acknowledged" : true,
"insertedId" : ObjectId("5b055f1bf9ca342c9eaaf746")
}Now MongoDB stored our database and collection
So we can use the
comics.superheroes
collectionUse
show collections
to know the database collections> show collections
superheroesAlso, we can check that the database has been saved too using
show dbs
> show dbs
comicsOnce we have a database created we can start MongoDB shell and specify the initial selected database
The
mongo
command accepts the initial selected database as parametermongo comics
> db
comicsFull example:
mongo comics
MongoDB shell version v3.4.2
connecting to: mongodb://127.0.0.1:27017/comics
MongoDB server version: 3.4.2
Server has startup warnings:
2018-05-23T07:07:16.271-0500 I CONTROL [initandlisten]
2018-05-23T07:07:16.271-0500 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.
2018-05-23T07:07:16.271-0500 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.
2018-05-23T07:07:16.271-0500 I CONTROL [initandlisten]
> db
comics
Practice
Read a collection
Use
find
to show the collection documentsIf we don't pass any parameter to the find method it will return all the collection documents
> db.superheroes.find()
{ "_id" : ObjectId("5b055c94f9ca342c9eaaf73e"), "name" : "SPIDER-MAN", "image" : "spiderman.jpg" }
{ "_id" : ObjectId("5b055f19f9ca342c9eaaf73f"), "name" : "CAPTAIN MARVEL", "image" : "captainmarvel.jpg" }
{ "_id" : ObjectId("5b055f19f9ca342c9eaaf740"), "name" : "HULK", "image" : "hulk.jpg" }
{ "_id" : ObjectId("5b055f19f9ca342c9eaaf741"), "name" : "THOR", "image" : "thor.jpg" }
{ "_id" : ObjectId("5b055f19f9ca342c9eaaf742"), "name" : "IRON MAN", "image" : "ironman.jpg" }
{ "_id" : ObjectId("5b055f19f9ca342c9eaaf743"), "name" : "DAREDEVIL", "image" : "daredevil.jpg" }
{ "_id" : ObjectId("5b055f19f9ca342c9eaaf744"), "name" : "BLACK WIDOW", "image" : "blackwidow.jpg" }
{ "_id" : ObjectId("5b055f19f9ca342c9eaaf745"), "name" : "CAPTAIN AMERICA", "image" : "captanamerica.jpg" }
{ "_id" : ObjectId("5b055f1bf9ca342c9eaaf746"), "name" : "WOLVERINE", "image" : "wolverine.jpg" }In this case we have 9 inserted documents
Each of them have an unique
_id
property to identify itAll documents have a name property too
Document Query
The
find
method accepts an object with properties criteria to query documentsIt will bring all documents if we pass an empty object as we're not using any criteria
> db.superheroes.find({})
{ "_id" : ObjectId("5b055c94f9ca342c9eaaf73e"), "name" : "SPIDER-MAN", "image" : "spiderman.jpg" }
{ "_id" : ObjectId("5b055f19f9ca342c9eaaf73f"), "name" : "CAPTAIN MARVEL", "image" : "captainmarvel.jpg" }
{ "_id" : ObjectId("5b055f19f9ca342c9eaaf740"), "name" : "HULK", "image" : "hulk.jpg" }
{ "_id" : ObjectId("5b055f19f9ca342c9eaaf741"), "name" : "THOR", "image" : "thor.jpg" }
{ "_id" : ObjectId("5b055f19f9ca342c9eaaf742"), "name" : "IRON MAN", "image" : "ironman.jpg" }
{ "_id" : ObjectId("5b055f19f9ca342c9eaaf743"), "name" : "DAREDEVIL", "image" : "daredevil.jpg" }
{ "_id" : ObjectId("5b055f19f9ca342c9eaaf744"), "name" : "BLACK WIDOW", "image" : "blackwidow.jpg" }
{ "_id" : ObjectId("5b055f19f9ca342c9eaaf745"), "name" : "CAPTAIN AMERICA", "image" : "captanamerica.jpg" }
{ "_id" : ObjectId("5b055f1bf9ca342c9eaaf746"), "name" : "WOLVERINE", "image" : "wolverine.jpg" }We can also add a criteria to search by
For example we can query documents by name
> db.superheroes.find({ "name": "WOLVERINE" })
{ "_id" : ObjectId("5b055f1bf9ca342c9eaaf746"), "name" : "WOLVERINE", "image" : "wolverine.jpg" }In this case we only have one document with WOLVERINE as name
We can use any document property to query by
> db.superheroes.find({ "image": "captanamerica.jpg" })
{ "_id" : ObjectId("5b055f19f9ca342c9eaaf745"), "name" : "CAPTAIN AMERICA", "image" : "captanamerica.jpg" }
Practice
Cursor
It looks like
find
returns documentsBut in reality it returns a
cursor
As we have JavaScript we can assign the find returned value to a variable
Dado que no le pasamos parámetro esta búsqueda retorna todos los documentos que tenemos en la colección
Using limit we can
limit
the amount of documents that we queryIn this example we'll get the first three collection documents
> const cursor = db.superheroes.find().limit(3)
We can know if we can iterate over the cursor using
hasNext()
This method will return a boolean value
In case it's true then we can call the next value
> cursor.hasNext()
trueAl ser un cursor el restulado podemos llamar al método
next
para obtener el próximo resultado> cursor.next()
{
"_id" : ObjectId("5b055c94f9ca342c9eaaf73e"),
"name" : "SPIDER-MAN",
"image" : "spiderman.jpg"
}
> cursor.next()
{
"_id" : ObjectId("5b055f19f9ca342c9eaaf73f"),
"name" : "CAPTAIN MARVEL",
"image" : "captainmarvel.jpg"
}
> cursor.next()
{
"_id" : ObjectId("5b055f19f9ca342c9eaaf740"),
"name" : "HULK",
"image" : "hulk.jpg"
}In case we can call
next
three times as it's the result we got from the find callsOnce we reach the last document hasNext() will return false and we know we can't iterate
MongoDB will show an error if we try to iterate more documents that the cursor has
> cursor.hasNext()
false
> cursor.next()
E QUERY [thread1] Error: error hasNext: false :
DBQuery.prototype.next@src/mongo/shell/query.js:305:1
@(shell):1:1
Practice
Collection drop
To delete a collection we use the collection
drop
method> db.superheroes.drop()
true
> show collections
>Calling collection
drop
method will delete all the collection documents
Database drop
We can also drop a database using the database
dropDatabase
methodshow dbs
local 0.000GB
test 0.000GB
comics 0.000GB
> use comics
> db.dropDatabase()
{ "dropped" : "comics", "ok" : 1 }
> show dbs
local 0.000GB
test 0.000GBCalling
db.dropDatabase()
we tell MongoDB to delete the selecteddb
databaseOnce we dropped the database we can recreate using
use
> use comics
switched to db comics
Insert multiple documents
To insert a document we use the collection method
insertOne
To insert many documents we use the collection method
insertMany
insertMany
accpets a document collection as parameter in JSON format> db.superheroes.insertMany([
{ "name": "SPIDER-MAN", "image": "spiderman.jpg" },
{ "name": "CAPTAIN MARVEL", "image": "captainmarvel.jpg" },
{ "name": "HULK", "image": "hulk.jpg" },
{ "name": "THOR", "image": "thor.jpg" },
{ "name": "IRON MAN", "image": "ironman.jpg" },
{ "name": "DAREDEVIL", "image": "daredevil.jpg" },
{ "name": "BLACK WIDOW", "image": "blackwidow.jpg" },
{ "name": "CAPTAIN AMERICA", "image": "captanamerica.jpg" },
{ "name": "WOLVERINE", "image": "wolverine.jpg" }
])
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("5b0569b32c95847cf4135e75"),
ObjectId("5b0569b32c95847cf4135e76"),
ObjectId("5b0569b32c95847cf4135e77"),
ObjectId("5b0569b32c95847cf4135e78"),
ObjectId("5b0569b32c95847cf4135e79"),
ObjectId("5b0569b32c95847cf4135e7a"),
ObjectId("5b0569b32c95847cf4135e7b"),
ObjectId("5b0569b32c95847cf4135e7c"),
ObjectId("5b0569b32c95847cf4135e7d")
]
}Now we can call
find
again to retrieve all documents> db.superheroes.find()
{ "_id" : ObjectId("5b0569b32c95847cf4135e75"), "name" : "SPIDER-MAN", "image" : "spiderman.jpg" }
{ "_id" : ObjectId("5b0569b32c95847cf4135e76"), "name" : "CAPTAIN MARVEL", "image" : "captainmarvel.jpg" }
{ "_id" : ObjectId("5b0569b32c95847cf4135e77"), "name" : "HULK", "image" : "hulk.jpg" }
{ "_id" : ObjectId("5b0569b32c95847cf4135e78"), "name" : "THOR", "image" : "thor.jpg" }
{ "_id" : ObjectId("5b0569b32c95847cf4135e79"), "name" : "IRON MAN", "image" : "ironman.jpg" }
{ "_id" : ObjectId("5b0569b32c95847cf4135e7a"), "name" : "DAREDEVIL", "image" : "daredevil.jpg" }
{ "_id" : ObjectId("5b0569b32c95847cf4135e7b"), "name" : "BLACK WIDOW", "image" : "blackwidow.jpg" }
{ "_id" : ObjectId("5b0569b32c95847cf4135e7c"), "name" : "CAPTAIN AMERICA", "image" : "captanamerica.jpg" }
{ "_id" : ObjectId("5b0569b32c95847cf4135e7d"), "name" : "WOLVERINE", "image" : "wolverine.jpg" }In this example we see how to drop and insert many new documents
insertMany
works in a similar way asinsertOne
but with a collection of documentsWe get all the inserted documents ids
Also we can count the collections document using the collection method
count
> db.superheroes.find().count()
9
Practice
Pretty results
Sometimes our documents are too big and it's difficult to read
That's why MongoDB has a
pretty
pmethod to show documents result better> db.superheroes.find().pretty()
{
"_id" : ObjectId("5b0569b32c95847cf4135e75"),
"name" : "SPIDER-MAN",
"image" : "spiderman.jpg"
}
{
"_id" : ObjectId("5b0569b32c95847cf4135e76"),
"name" : "CAPTAIN MARVEL",
"image" : "captainmarvel.jpg"
}
{
"_id" : ObjectId("5b0569b32c95847cf4135e77"),
"name" : "HULK",
"image" : "hulk.jpg"
}
{
"_id" : ObjectId("5b0569b32c95847cf4135e78"),
"name" : "THOR",
"image" : "thor.jpg"
}
{
"_id" : ObjectId("5b0569b32c95847cf4135e79"),
"name" : "IRON MAN",
"image" : "ironman.jpg"
}
{
"_id" : ObjectId("5b0569b32c95847cf4135e7a"),
"name" : "DAREDEVIL",
"image" : "daredevil.jpg"
}
{
"_id" : ObjectId("5b0569b32c95847cf4135e7b"),
"name" : "BLACK WIDOW",
"image" : "blackwidow.jpg"
}
{
"_id" : ObjectId("5b0569b32c95847cf4135e7c"),
"name" : "CAPTAIN AMERICA",
"image" : "captanamerica.jpg"
}
{
"_id" : ObjectId("5b0569b32c95847cf4135e7d"),
"name" : "WOLVERINE",
"image" : "wolverine.jpg"
}
Practice
We can also search documents for more than one criteria
> db.superheroes.find({"name": "WOLVERINE", "image" : "wolverine.jpg"})
{ "_id" : ObjectId("5b0569b32c95847cf4135e7d"), "name" : "WOLVERINE", "image" : "wolverine.jpg" }In this case we're searching for a document with the WOLVERINE name and wolverine.jpg as image
If we search using other parameters we might not get documents back
> db.superheroes.find({"name": "WOLVERINE", "image" : "captanamerica.jpg"})
MongoDB is not able to retrieve any documents as we don't have any document with name WOLVERINE and image captanamerica.jpg
Practice
Update a document
To update a document we use the collection method
updateOne
This method accepts an object as first parameter with the
find
criteriaThe second parameter is an object with a special operator called
$set
$set
uses an object with the document property that we want to update and the corresponding value> db.superheroes.updateOne({ "name": "WOLVERINE"}, { $set: { "name": "LOGAN"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
{ "_id" : ObjectId("5b0569b32c95847cf4135e7d"), "name" : "LOGAN", "image" : "wolverine.jpg" }MongoDB updates the document and inform the operation status showing the amount of updated documents that match the critearia
As MongoDB doesn't have a rigid schema we can add any property to any document without changing the rest of the documents
For example we could add a power property to the hulk document
> db.superheroes.updateOne({ "name": "HULK"}, { $set: { "power": 100}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }Now we can query the Hulk document
> db.superheroes.find({"name": "HULK"}).pretty()
{
"_id" : ObjectId("5b0569b32c95847cf4135e77"),
"name" : "HULK",
"image" : "hulk.jpg",
"power" : 100
}Now we can see that power was added to the document with Hulk name
> b.superheroes.find().limit(1).pretty()
{
"_id" : ObjectId("5b0569b32c95847cf4135e75"),
"name" : "SPIDER-MAN",
"image" : "spiderman.jpg"
}We still see that the rest of the document haven't been updated with the new property
In this examples we have been using the name property to find our documents
What could it happen if we have more than one document with the same name?
The only way to make sure that we're going to update the right document is using the document id
Remember that the documents hava a
_id
property by default and this value is created by MongoDB to be uniqueTo update a document using the id we do it using
ObjectId(id)
> db.superheroes.updateOne({"_id": ObjectId("5b0569b32c95847cf4135e77")}, { $set: { "name": "THE HULK"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }We can also search by id
> db.superheroes.find({ "_id": ObjectId("5b0569b32c95847cf4135e77")}).pretty()
{
"_id" : ObjectId("5b0569b32c95847cf4135e77"),
"name" : "THE HULK",
"image" : "hulk.jpg",
"power" : 100
}Excelent now we can use
_id
in our queries
Practice
Delete a document
To delete a document we use
deleteOne
This method will delete the first document that match the query criteria
To be sure that we don't delete the wrong document we can use
_id
too> db.superheroes.deleteOne({ "_id": ObjectId("5b0569b32c95847cf4135e77")})
{ "acknowledged" : true, "deletedCount" : 1 }
> db.superheroes.find({ "_id": ObjectId("5b0569b32c95847cf4135e77")})
>Using
deleteOne
andObjectId()
we can delete a documentWe could use any criteria (example: name property) to delete a document by
In case we need to delete many documents with the same criteria we can use the collection method
deleteMany
> db.superheroes.deleteMany({})
{ "acknowledged" : true, "deletedCount" : 8 }As we passed an empty array we delete all the collections documents
Practice
Node.js integration
To use MongoDB from Node.js we need to use the MongoDB official driver
npm i mongodb
Once we installed Mongo we need to require MongoClient from this module
const MongoClient = require("mongodb").MongoClient;
Also we need to set up MongoDB url to connect with
const url = "mongodb://localhost:27017";
We know that by default MongoDB uses port 27017 and we have it installed in our local environment
MongoClient
has aconnect
method that allows us to connect to MongoDB using Node.jThis method accepts the MongoDB server
address (url)
and acallback function
The callback function gets two parameters: The first parameter is an
error
object The second parameter ir aclient
objectMongoClient.connect(url, function(err, client) {
console.log("Connected successfully to server");
});We're going to use the client object to get the database
The client object has a
db
method that accepts a string with the database nameIt returns a database object
const db = client.db("comics");
The same as using MongoDB shell we need to use a collection
The
db
object that we got from theclient
one has acollection
method that allow us to select the collectionThis method accepts a string with the collection name
This method returns a collection object
const collection = db.collection("superheroes");
Now that we have the collection we can use the
find
method to queryThe
find
method accepts an object with the criteria to search bycollection.find({});
The
find
method returns acursor
objectWe need to find a way to transform our cursor into objects to use the documents we get back
That's why the cursor object has a
toArray
method that will transform the result into an array of objects (documents)The toArray object accepts a callback
This callback gets two parameters, first an error and second the documents that it get back
collection.find({}).toArray((error, documents) => {
console.log(documents);
});As we opened a connection to the MongoDB we need to close it if we're not using it
The
client
object has aclose
method that will close the database connectionclient.close();
Great now we know how the mongodb driver works
Lets put everything together
const MongoClient = require("mongodb").MongoClient;
const url = "mongodb://localhost:27017";
MongoClient.connect(url, function(err, client) {
const db = client.db("comics");
const collection = db.collection("superheroes");
collection.find({}).toArray((error, documents) => {
console.log(documents);
client.close();
});
});Using express we'll need to add this code to our route handler
app.get("/", (req, res) => {
MongoClient.connect(url, function(err, client) {
const db = client.db("comics");
const collection = db.collection("superheroes");
collection.find({}).toArray((error, documents) => {
client.close();
res.render("index", { documents: documents });
});
});
});Close the connection before sending the response to the client
Now we know how to query MongoDB from Node.js and also using Express route
But what about inserting, updating or deleting documents?
The MongoDB driver has methods for each case
Check the MongoDB driver collection methods doc
As you can see there're a lot of things that we can do once we have a collection
To insert documents we have two methods:
insertOne & insertMany
const doc = { name: "CAPTAIN MARVEL", image: "captainmarvel.jpg" };
collection.insertOne(doc, (err, result) => {
callback(result);
});Now insert many documents using an array
const documents = [
{ name: "CAPTAIN MARVEL", image: "captainmarvel.jpg" },
{ name: "HULK", image: "hulk.jpg" },
{ name: "THOR", image: "thor.jpg" }
];
collection.insertMany(documents, (err, result) => {
callback(result);
});Update also has two methods:
updateOne & updateMany
This updates methods accepts a filter object as first parameter
Also accepts a second parameter that's a callback
const filter = { name: "HULK" };
const update = { $set: { power: 100 } };
collection.updateOne(filter, update, (err, result) => {
callback(result);
});We can do the same with updateMany
const filter = { name: "HULK" };
const update = { $set: { power: 100 } };
collection.updateMany(doc, update, (err, result) => {
callback(result);
});If we have more than one document with the name HULK it will update the power to 100
Finally we can delete documents using MongoDB driver two methods:
deleteOne & deleteMany
As we're going to delete documents now it's a good time about MongoDB ObjectID
MongoDB driver has a
const ObjectID = require("mongodb").ObjectID;
const filter = { _id: ObjectID("5b07560bda15952ac0b33e6c") };
collection.deleteOne(query, function(err, result) {
callback(result);
});
Practice
Other Databases
- Express apps can use any database supported by Node (Express itself doesn't define any specific additional behavior/requirements for database management). There are many popular options, including PostgreSQL, MySQL, Redis, SQLite, and MongoDB.
Using an ODM/ORM
- Rather than using the databases' native query language (e.g. SQL), you can use an Object Data Model ("ODM") / Object Relational Model ("ORM"). An ODM/ORM represents the website's data as JavaScript objects, which are then mapped to the underlying database. Some ORMs are tied to a specific database, while others provide a database-agnostic backend.
- Pros
- Easier for programmers can continue to think in terms of JavaScript objects rather than database semantics
- Provide an obvious place to perform validation and checking of data
- Cons
- Less performant than native query languages
- Pros
Mongoose
- Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment. Mongoose supports both promises and callbacks.
- Installing Mongoose and MongoDB
npm install mongoose
Connecting to MongoDB
Mongoose requires a connection to a MongoDB database. You can require() and connect to a locally hosted database with mongoose.connect(), as shown below.
//Import the mongoose module
var mongoose = require("mongoose");
//Set up default mongoose connection
var mongoDB = "mongodb://127.0.0.1/my_database";
mongoose.connect(mongoDB, { useNewUrlParser: true });
//Get the default connection
var db = mongoose.connection;
//Bind connection to error event (to get notification of connection errors)
db.on("error", console.error.bind(console, "MongoDB connection error:"));
Defining and creating models
Models are defined using the Schema interface.
The Schema allows you to define the fields stored in each document along with their validation requirements and default values.
const mongoose = require("mongoose");
const superHeroSchema = new mongoose.Schema({
name: String
});A SchemaType is then a configuration object for an individual property.
Schemas are then "compiled" into models using the
mongoose.model()
method.const SuperHeroModel = mongoose.model("SuperHeroModel", superHeroSchema);
The following are all the valid SchemaTypes in Mongoose. Mongoose plugins can also add custom SchemaTypes:
Mongoose provides built-in and custom validators, and synchronous and asynchronous validators.
The built-in validators include:
- All SchemaTypes have the built-in required validator. This is used to specify whether the field must be supplied in order to save a document.
- Numbers have min and max validators.
- Strings have:
var superHeroSchema = new Schema({
movieCount: {
type: Number,
min: [0, "Too few movies"],
max: 12,
required: [true, "Why no movies?"]
},
brand: {
type: String,
enum: ["Marvel", "DC"]
}
});While you can create schemas and models using any file structure you like, we highly recommend defining each model schema in its own module (file), exporting the method to create the model.
./models/superhero.model.js
const mongoose = require("mongoose");
const superHeroSchema = new mongoose.Schema({
name: String
});
module.exports = mongoose.model("SuperHeroModel", superHeroSchema);
Using models
Creating Documents
An instance of a model is called a document. Creating them and saving to the database is easy.
var SuperHeroModel = require("../models/superhero.model");
var hero = new SuperHeroModel({ name: "WOLVERINE" });
hero.save(function(err) {
if (err) return handleError(err);
// saved!
});
// or
SuperHeroModel.create({ name: "WOLVERINE" }, function(err, small) {
if (err) return handleError(err);
// saved!
});
// or, for inserting large batches of documents
SuperHeroModel.insertMany([{ name: "WOLVERINE" }], function(err) {});
Querying Documents
- Finding documents is easy with Mongoose, which supports the rich query syntax of MongoDB. Documents can be retreived using each models find, findById, findOne, or where static methods.
SuperHeroModel.find({ name: "WOLVERINE" })
.where("createdDate")
.gt(oneYearAgo)
.exec(callback);
Updating Documents
- Each model has its own update method for modifying documents in the database without returning them to your application. See the API docs for more detail.
SuperHeroModel.deleteOne({ brand: "DC" }, function(err) {
if (err) return handleError(err);
// deleted at most one tank document
});
SuperHeroModel.updateOne({ name: "WOLVERINE" }, { name: "LOGAN" }, function(
err,
res
) {
// Updated at most one doc, `res.modifiedCount` contains the number
// of docs that MongoDB updated
});
Deleting Documents
- Models have static deleteOne() and deleteMany() functions for removing all documents matching the given filter.
SuperHeroModel.deleteOne({ brand: "DC" }, function(err) {
if (err) return handleError(err);
// deleted at most one tank document
});