Introduction to MONGO DB
Why use database ? instead of just saving to a file ?
-> database can handle large amount of data efficiently and
store it compactly
-> they provide tools for easy insertion, querying and
updating of data
-> they generally offer security features and control over
access to data
-> they generally scale well
Difference between SQL and NoSQL
SQL Databases:- Structured query language databases are
relational databases. We pre-define a schema of tables
before inserting anything. Popular SQL databases include
MySQL, Postgres , SQlite , Oracle, Microsoft SQL server
NoSQL Databases:- NoSQL databases do not use SQL. There
are many type of no-sql databases, including
document ,key-value and graph stores. Popular NoSQL
databases include MongoDB, CouchDB , Neo4j , Cassandra ,
Redis.
Why to learn MongoDB:- Mongo is a very commonly used
with Node and Express (Mean and Mern Stack). It’s easily to
get started with(Though it can be really tricky to master). It
plays particularly with JavaScript . Its popularity also means
there is a strong community of developers using Mongo.
We insert the data in JSON(javascript object notation)
Format but it automatically gets converted to BSON(Binary
JSON).
Performing Basic CRUD operation in MongoDB:- Before
performing any operation we need to know how do we start
the mongo server.
If we are using the latest version of the mongoDb then in
the command prompt we need to write {mongosh}. This will
automatically start the mongo server at a localhost:127:0:00
or any other port.
TO VIEW THE DATABASES:- to view all the databases we use
the command -> show dbs.
TO CREATE A DATABASE OF USE A DATABASE:- we use the
command -> use databaseName. If the databases exist then
cursor moves inside the DB . if not present then mongo
creates a database with the same name and moves inside
it.
Now Before inserting we need to have a collections. Inside a
Database in Mongo we can have multiple collections.
TO VIEW ALL COLLECTIONS:- after entering inside the
database we can use to command -> show collections.
It will list out all the collections available.
We can create a collections while inserting any data.
C of CRUD Creation(Inserting)
Inserting Data to our database:- for inserting any data we
have three methods
1) insertOne()
2) insertMany()
3) insert().
insertOne()-> in this method we can insert only one JSON
object.
Suppose I have a collections called dogs and inside it I want
to add a few JSON data or DOG data
Syntax: db.dogs.insertOne({Name:”husk”,Breed:”Siberian
husky”, Age:3, isAgressive:false}).
Now our data has been successfully inserted.
Suppose I want to insert 2 JSON objects I can do that using
the insertMany() method.
insertMany()-> in this method we can insert an array of
JSON object.
Syntax:
db.dogs.insertMany({Name:”Brownie”,Breed:”Corgi”, Age:5,
isAgressive:true},
{Name:”Bruno”,Breed:”Labrador”,Age:12,isAgressive:false}).
Now both these objects will be inserted in the dogs
collection.
Note:- As mongo db has no pre-defined schema so we can
insert any type of key value pair in the collections. Now
both of these operations can be done using the insert()
method.
Insert():- this method is used to enter either one or
multiple JSON obejcts into the collections.
Syntax: db.dogs.insert({Name:”Bluee”,Breed:”chihuahua”,
Age:2, isAgressive:true}).
Or we can do like this
: db.dogs.insert({Name:”Brownie”,Breed:”Corgi”, Age:5,
isAgressive:true},
{Name:”Bruno”,Breed:”Labrador”,Age:12,isAgressive:false}).
R of CRUD Reading(finding).
Just like inserting data into the collections we can also read
data from the collections.
It has 3 methods
1)findOne()
2)findMany()
3)find()
findOne() we can find a particular json object using the
findOne() method. It takes a json object that is evaluated
with each objects in the collections when the first objects is
satisfied it returns the object.
Syntax:
db.findOne({isAgressive:true}) it will return
{
_id: Objectid(987014609713831),
Name:”Brownie”,
Breed:”Corgi”,
Age:5,
isAgressive:true
}
Note:- We notice that we are also getting a id which we
have not specified. Now this id is actually generated by the
mongo incase a id is not provided to the object. It is always
unique.
findMany() we can find JSON objects using the
findMany() method. It takes a json object that is evaluated
with each objects in the collections when the objects is
satisfied it returns a array of those objects.
Syntax:
db.findMany({isAgressive:false}) it will return
[{
_id:Objectid(87930470940932),
Name:”Bruno”,
Breed:”Labrador”,
Age:12,
isAgressive:false
},
{
_id:Objectid(0987324689028),
Name:”husk”,
Breed:”Siberian husky”,
Age:3,
isAgressive:false
}]
Find() this method works the same as both findMany()
and findOne(). If more than one objects satisfy the
condition then it returns an array of object and if a single
object is satisfied it returns the single object.
Syntax:
db.dogs.find({isAgressive:false})
note: keep in mind that mongo is case sensitive. Same and
same are not same for Mongo.
U of CRUD-> updating
To perform an update query in the database we have 2
methods
1) updateOne()
2) updateMany()
updateOne() it takes 2 object. The first object is used to
find or match objects in the collection. Whenever the first
object satisfies the given object it update any constraint of
that object with the new given object constraints.
Ex: suppose I want to update the age of the dog whose
breed is Corgi.
Note: now for updating something we have to use a special
operator called as $set. It is used to update the constraints
of that particular object.
Syntax: db.dogs.updateOne({breed:’Corgi’},{$set:{age:12}})
Note: the set operator must always be used before the
object that is to be updated.
Nice to Have: there is another operator which can be used
to store the last date and time when it was either modified
or updated. The operator is $currentDate:{}
Suppose I want to also add a constraint lastUpdated to our
objects.
Syntax: db.dogs.updateOne({breed:’Corgi’},{$set:{age:12}} ,
{$currentDate:{lastUpdated:true}})
updateMany()This method is used to update many
objects that satisfy the given parameter.
Ex: suppose I want to update the isAvailable to true where
each objects have a isAggresive set to true.
Syntax: db.dogs.updateOne({isAggresive:true},{$set:
{isAvailable:true}}).
D of CRUD Deleting(Destroy):
For deleting any object from the collections we have 2
methods
1)deleteOne()
2)deleteMany()
deleteOne() it takes a object as parameter. When then
given object satisfies with the constraints of objects in the
collection, the first object that gets matched , is deleted.
Ex: suppose I want to delete one dog whose breed is husky
Syntax: db.dogs.deleteOne({breed:’husky’})
deleteMany() it takes a object as parameter. When then
given object satisfies with the constraints of objects in the
collection, those object get deleted.
Ex: suppose I want to delete all dogs who are aggressive
Syntax: db.dogs.deleteMany({isAggresive:true})
Note: if we want to delete all the objects in the collection
then we can use deleteMany with null object
Ex: db.dogs.deleteMany({}). It will delete everything in the
collection.
Nesting objects inside another object:
We can nest various objects inside one object just like we
did it in javascript
Ex: suppose I want to insert a new dog which has a new
property that is personality
Syntax: db.dogs.insertOne({name:”Bruno Samartino”,
age:12, Breed:”Dogo argentino”, isAvailable:true,
isAggresive:true, personality:{isCatFriendly:false ,
isChildFriendly:true}}).
Now if I want to find a dog based on personality I have to
use the dot operator(.) to access the inner property. Now
this key should be inside inverted quotes.
Syntax: db.dogs.find({‘personality.isChildFriendly’:true})
We also have various other operator that we can use in our
query. These are
Comparison operators like:
Name Description
$eq Matches values that are equal to a specified value.
$gt Matches values that are greater than a specified value.
$gte Matches values that are greater than or equal to a specified
value.
$in Matches any of the values specified in an array.
$lt Matches values that are less than a specified value.
$lte Matches values that are less than or equal to a specified value.
$ne Matches all values that are not equal to a specified value.
$nin Matches none of the values specified in an array.
Logical operators like:
Name Description
$and Joins query clauses with a logical AND returns all documents that match
the conditions of both clauses.
$not Inverts the effect of a query expression and returns documents that
do not match the query expression.
$nor Joins query clauses with a logical NOR returns all documents that fail to
match both clauses.
$or Joins query clauses with a logical OR returns all documents that match
the conditions of either clause.
Element query operator like:
Name Description
$exists Matches documents that have the specified field.
$type Selects documents if a field is of the specified type.
Array operators like:
Name Description
$all Matches arrays that contain all elements specified in the query.
$elemMatch Selects documents if element in the array field matches all the
specified $elemMatch conditions.
$size Selects documents if the array field is a specified size.
Introduction to ORM and ODM Models
What is ORM?
Object Relational Mapper
ORM is Object Relational Mapping, basically a
technique to query or perform CRUD (Create, Read,
Update, Delete) operations to the database, mainly RDBMS
(Relational Databases), using an object-oriented paradigm.
With the help of ORM, you don’t actually need to use SQL
at all. You can directly interact with the database and
perform queries in the same language you are using for
your back-end code!
The structure of SQL databases consists of tables and
views.
Let’s take an example to understand:
Here we are using MySQL (Open-source RDBMS)
in NodeJs:
Inserting data in a MySQL table:
In the above code example, we used the SQL query to
insert a single data into a table (inserted as a row in the
table).
If you check the database mydb for the Users table, as you
can see in the below output, the data is inserted.
Users:
But what if someone mistakenly enters a field that doesn't
exist, in the SQL query?
So, let's see what happens when we change the query
on line 25 of mysql.js to something like the below code:
When we run the mysql.js code with the above query we
would get an error like this in the console:
A small mistake resulting in such a big error
message!
The query we used in mysql.js code, is a basic one, so it's
easy to debug, but the SQL queries can be complicated
sometimes, hard to read, and will really be difficult to
debug if we get an error. So, using an ORM (Object
Relational Mapper) saves us from such disasters!
Inserting data using an ORM such as Sequelize:
Running above code, and then check the database:
Data Mapper, executing an application object into a database.
Here, using Sequelize (ORM), we created an object model
for table Users, and using this model, we inserted user
data using the member function create(). You may have
noticed in line 18 of sequelize.js code we tried to insert a
column named — imposter, and we didn't get any error
while running that code as we got for mysql.js code when
we changed the query, that's because Sequelize mapped
all the data according to our User model and then
executed the query ignoring imposter property and thus
no error.
What is ODM?
ODM is Object Document Mapping. It is like an ORM for
non-relational databases or distributed databases such as
MongoDB, i.e., mapping an object model and NoSQL
database (document databases, graph database, etc.).
Note: In document-oriented NoSQL databases,
documents are stored in JSON format in the collection
(a table in SQL databases is like a collection in NoSQL
databases).
Let’s take some examples:
Here we are using MongoDB in NodeJs:
Inserting document using an ODM such as Mongoose for
MongoDB:
const mongoose =
require("mongoose"
);
// Database Connection
mongoose.connect("mongodb://127.0.0.1:27017/
mydb1", {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
});
//user model
const User = mongoose.model("User", {
username: { type: String },
password: { type: String },
});
//new user object
const newUser = new User({
username: "john-doe",
password: "helloworld",
hello: "hello",
});
//inserting/saving the document in collection
newUser.save(function (error, result) {
if (error) {
console.log(error);
} else {
console.log(result);
}
});
In the above code (see line 19), we tried to add a
field hello which is not in our User model. After running
the above code, we check the collection users in
the mydb1 database in the MongoDB shell:
As we can see in the database the field hello is not
inserted, it's because in our User model we don’t have
such a field, and Mongoose mapped the data according to
the User model to the database.
ORM vs ODM
Now that we’ve discussed each of them, let us proceed to
discuss the differences between the two.
The key difference between the two is the type of
database these data-mappers are used for. Suppose we
have a relational database such as PostgreSQL, MySQL,
etc. in that case, we will use an ORM. On the other hand,
for non-relational databases such
as MongoDB, Cassandra, Redis, etc. we will use an ODM to
achieve the same results.
Introduction to Mongoose
Using JavaScript to work with our database:
To work with our database we have to install the mongoose
from npm.
const mongoose = require('mongoose');
mongoose.connect("mongodb://127.0.0.1/
movieApp") // we are using the
mongoose.connect method to connect to our
databse
.then(() => {
console.log("Connected
sucessfully!!")
})
.catch(() => {
console.log("Cannot connect to the
Database!!")
})
// now to work with mongoose we need to
create a model
// and before creating model we need to
define schema
//our model can be like
// {
// title:"America",
// year:1986,
// rating:9.2
// }
// to create a schema we have to use new
mongoose.schema
const movieSchema = new mongoose.Schema({
title: String,
year: Number,
rating: String
});
// now to create a model we use the model()
method
const Movie = mongoose.model('Movie',
movieSchema); // the first keyword is the
name for the collections which should always
be singular word and have 1st letter
uppercase. in the database it will be
converted to lowercase and to plural
// now we can create various instances of
the above model and save it to the mongo
database. As we are storing the modle in a
variable, this variable act as a class and
we can create various objects of this class
// const omg = new Movie({
// title: "OMG",
// year: 2013,
// rating: 9.1
// })
// now lets run this in the node shell
// aftering opening the node shell we can
write .load index.js it will load our file
in the terminal . if i type omg now it will
show the object justlike we se in the
mongodb.
// now if we want to save it we will use
// objectname.save() . it will store the
data in the mongo database. we can also
update it . suppose i want the rating to be
8.5 then i can write omg.rating=8.5 and
after that omg.save() . it will commit those
changes to mongo
//insert many
// now another way of inserting data is
// Movie.insertMany([
// {
// title: "Happy new year",
// year: 2014,
// rating: 7.3
// },
// {
// title: "Oppenheimer",
// year: 2023,
// rating: 9.2,
// },
// {
// title: "Barbie",
// year: 2023,
// rating: 9,
// }
// ])
// .then((data) => {
// console.log("data inserted
sucessfully!");
// console.log(data);
// })
// if we are inserting data like this then
we dont need to load and save it. we only
need to run it
// finding with mongoose
// for finding anything the mongoose returns
a promise that we need handle by adding a
callback.
// now lets open the node shell and load it.
after that we write Movie.find({}). now this
will return a promise and various stuff
which we actually dont need. now to get the
data we can add a .then() to it
// >
Movie.find({}).then((data)=>console.log(data
))
// > [
// {
// _id: new
ObjectId("64da365c4ee04e63f4be4921"),
// title: 'OMG',
// year: 2013,
// rating: '8.3',
// __v: 0
// },
// {
// _id: new
ObjectId("64da3bc05678a3a6f69bade7"),
// title: 'Happy new year',
// year: 2014,
// rating: '7.3',
// __v: 0
// },
// {
// _id: new
ObjectId("64da3bc05678a3a6f69bade8"),
// title: 'Oppenheimer',
// year: 2023,
// rating: '9.2',
// __v: 0
// },
// {
// _id: new
ObjectId("64da3bc05678a3a6f69bade9"),
// title: 'Barbie',
// year: 2023,
// rating: '9',
// __v: 0
// }
// ]
// >
Movie.find({rating:9.2}).then((data)=>consol
e.log(data))
// > [
// {
// _id: new
ObjectId("64da3bc05678a3a6f69bade8"),
// title: 'Oppenheimer',
// year: 2023,
// rating: '9.2',
// __v: 0
// }
// ]
// > Movie.find({year:{$gte:
2015}}).then((data)=>console.log(data))
// > [
// {
// _id: new
ObjectId("64da3bc05678a3a6f69bade8"),
// title: 'Oppenheimer',
// year: 2023,
// rating: '9.2',
// __v: 0
// },
// {
// _id: new
ObjectId("64da3bc05678a3a6f69bade9"),
// title: 'Barbie',
// year: 2023,
// rating: '9',
// __v: 0
// }
// ]
// > Movie.find({year:{$gte:
2013}}).then((data)=>console.log(data))
// > [
// {
// _id: new
ObjectId("64da365c4ee04e63f4be4921"),
// title: 'OMG',
// year: 2013,
// rating: '8.3',
// __v: 0
// },
// {
// _id: new
ObjectId("64da3bc05678a3a6f69bade7"),
// title: 'Happy new year',
// year: 2014,
// rating: '7.3',
// __v: 0
// },
// {
// _id: new
ObjectId("64da3bc05678a3a6f69bade8"),
// title: 'Oppenheimer',
// year: 2023,
// rating: '9.2',
// __v: 0
// },
// {
// _id: new
ObjectId("64da3bc05678a3a6f69bade9"),
// title: 'Barbie',
// year: 2023,
// rating: '9',
// __v: 0
// }
// ]
// > Movie.find({year:{$lte:
2015}}).then((data)=>console.log(data))
// > [
// {
// _id: new
ObjectId("64da365c4ee04e63f4be4921"),
// title: 'OMG',
// year: 2013,
// rating: '8.3',
// __v: 0
// },
// {
// _id: new
ObjectId("64da3bc05678a3a6f69bade7"),
// title: 'Happy new year',
// year: 2014,
// rating: '7.3',
// __v: 0
// }
// ]
// > Movie.findOne({year:{$gt:
2020}}).then((data)=>console.log(data))
// > {
// _id: new
ObjectId("64da3bc05678a3a6f69bade8"),
// title: 'Oppenheimer',
// year: 2023,
// rating: '9.2',
// __v: 0
// }
//
// we can also use a method findById that
takes the id and finds the object
// >
Movie.findById('64da365c4ee04e63f4be4921').t
hen(data=> console.log(data))
// > {
// _id: new
ObjectId("64da365c4ee04e63f4be4921"),
// title: 'OMG',
// year: 2013,
// rating: '8.3',
// __v: 0
// }
// updating with mongoose
// Movie.updateOne({title:"OMG"},
{year:2012}).then(res=> console.log(res))
// > {
// acknowledged: true,
// modifiedCount: 1,
// upsertedId: null,
// upsertedCount: 0,
// matchedCount: 1
// }
// > Movie.updateMany({title: {$in:["OMG",
"Barbie"]}},{rating:10}).then(res=>
console.log(res))
// Promise {
// <pending>,
// [Symbol(async_id_symbol)]: 1888,
// [Symbol(trigger_async_id_symbol)]: 1884
// }
// > {
// acknowledged: true,
// modifiedCount: 2,
// upsertedId: null,
// upsertedCount: 0,
// matchedCount: 2
// }
// > Movie.findOneAndUpdate({title:"OMG"},
{rating: 5}).then(m=> console.log(m))
// > {
// _id: new
ObjectId("64da365c4ee04e63f4be4921"),
// title: 'OMG',
// year: 2012,
// rating: '10',
// __v: 0
// }
// > Movie.findOneAndUpdate({title:"OMG"},
{rating: 6.2}, {new:true}).then(m=>
console.log(m))
// > {
// _id: new
ObjectId("64da365c4ee04e63f4be4921"),
// title: 'OMG',
// year: 2012,
// rating: '6.2',
// __v: 0
// }
// deleting with mongoose
// Movie.deleteOne({title:"OMG"}).then(msg=>
console.log(msg))
// Promise {
// <pending>,
// [Symbol(async_id_symbol)]: 1319,
// [Symbol(trigger_async_id_symbol)]: 1315
// }
// > { acknowledged: true, deletedCount: 1 }
// Movie.deleteMany({year:
{$lt:2023}}).then(msg=> console.log(msg))
// Promise {
// <pending>,
// [Symbol(async_id_symbol)]: 4035,
// [Symbol(trigger_async_id_symbol)]: 4031
// }
// > { acknowledged: true, deletedCount: 1 }
// >
Movie.findOneAndDelete({title:'Barbie'}).the
n(msg=> console.log(msg))
// Promise {
// <pending>,
// [Symbol(async_id_symbol)]: 4613,
// [Symbol(trigger_async_id_symbol)]: 4609
// }
// > {
// _id: new
ObjectId("64da3bc05678a3a6f69bade9"),
// title: 'Barbie',
// year: 2023,
// rating: '6',
// __v: 0
// }
Mongoose Schema Validation
const mongoose = require('mongoose');
mongoose.connect("mongodb://127.0.0.1/
shopApp")
.then(() => {
console.log("Connected
sucessfully!!")
})
.catch(() => {
console.log("Cannot connect to the
Database!!")
})
// we can add our own validation while
designing the schema. below are the few
properties that we can add to some
constraints
const productSchema = new mongoose.Schema(
{
name: {
type: String,
required: true, // boolean or a
functions , if true adds a required
validator,
maxlength: 20 // maximum of 20
characters are allowed . if more returns an
error
},
price: {
type: Number,
required: true,
min: 350,
max: 2000
},
onSale: {
type: Boolean,
default: false // any or
functions , set to a default value for the
path if not specified or provided. if the
value is function then the return value of
the functions is used as the default value.
},
categories: [String],
qty: {
online: {
type: Number,
default: 0,
},
inStore: {
type: Number,
default: 0
}
}
}
);
// we have string validators like
/*
lowercase
uppercase
match
enum
trim
minlength
maxlength
*/
/*
we have number validators like
min
max
enum
*/
const Product = mongoose.model('Product',
productSchema);
const bike = new Product(
{
name: " Biker Jackets ",
price: 1800,
onSale: true,
categories: ["Bike", "utility",
"bike Gear", "protection"],
qty: {
online: 200,
inStore: 1200
}
}
)
bike.save()
.then((data) => {
console.log(data);
})
.catch((e) => {
console.log('Uh oh , it seems to be
an error');
console.log(e);
})
Validating mongoose update
// while inserting any of our data it is
getting validated but during update , it
does not. for example suppose i update a
product name Biker Jackets price to
negative. i have specified that the
price ranges from 300 to 2000
// Product.findOneAndUpdate({ name: "
Biker Jackets " }, { price: 400 }, { new:
true })
// .then((data) => {
// console.log(data)
// })
// .catch((err) => {
// console.log(err)
// })
// it returns
/*
{
qty: { online: 200, inStore: 1200 },
_id: new
ObjectId("64dbb29d732bf3f28e6ebed6"),
name: ' Biker Jackets ',
price: -230,
onSale: true,
categories: [ 'Bike', 'utility', 'bike
Gear', 'protection' ],
__v: 0
}
*/
//that means it has updated the value
without validating. To check for
validation we can set the property
runValidator to true
// Product.findOneAndUpdate({ name: "
Biker Jackets " }, { price: -400 },
{ new: true, runValidators: true })
// .then((data) => {
// console.log(data)
// })
// .catch((err) => {
// console.log(err)
// })
// now it returns that the validation
failed
// Mongoose Validation Errors
// we can set up our error message using
an array in the min or max validator
// price: {
// type: Number,
// required: true,
// min: [0, "Price must be Greater
than 0!!"],
// max: 2000
// },
// name: " Biker Jackets ",
// price: 1800,
// onSale: true,
// categories: ["Bike",
"utility", "bike Gear", "protection"],
// qty: {
// online: 200,
// inStore: 1200
// }
// Product.insertMany({ name: "Bike
tyres", price: 1200, onSale: true,
categories: ['Bike', 'tyres',
'components'], qty: { online: 320,
inStore: 200 } })
// .then(data => {
// console.log(data)
// })
// .catch(err => {
// console.log(err)
// })
//it returned ==> price:
ValidatorError: Price must be Greater
than 0!!
// now what is there is a property that
takes only n values . suppose i have a
property size and it consist of S M L. i
can do that by using the enum property.
now lets provide a value outside of these
pre-defined values
// Product.insertMany({ name: "Biker
Shirts", price: 800, onSale: true,
categories: ['Bike', 'tShirts',
'lifestyle'], qty: { online: 320,
inStore: 200 }, size: 'XM' })
// .then(data => {
// console.log(data)
// })
// .catch(err => {
// console.log(err)
// })
// it returns==> Error: Product
validation failed: size: `XM` is not a
valid enum value for path `size`.
// Adding personal Instance validator
methods in Schema
// to add any methods to our schema it
has to be after the schema and before the
model
// syntax: Schema.methods.methodName=
function()
// we can also change value of these
properties using these methods
productSchema.methods.greet = function ()
{
console.log(` Hello`);
console.log(`- ${this.name}`); // now
we have access to each of the product
properties
}
// suppose i want to find a product and
create a method that changes the onSale
to opposite of what it currently holds
productSchema.methods.toggleOnSale =
function () {
this.onSale = !this.onSale; // here
this uses each property of the json data
return this.save();
}
// Adding model static methods
// just like we are adding instance
methods to instances of Model similarly
we can add methods to the model itself.
productSchema.statics.fireSale = function
() {
return this.updateMany({}, { onSale:
true, price: 450 });// here this uses
methods of the Product model
}
const Product = mongoose.model('Product',
productSchema);
// const findProduct = async () => {
// try {
// const foundProduct = await
Product.findOne({ name: "Bike tyres" });
// if (foundProduct) {
// console.log(foundProduct);
// await
foundProduct.toggleOnSale();
// console.log(foundProduct);
// } else {
// console.log("Product not
found");
// }
// } catch (error) {
// console.error("Error finding
product:", error);
// }
// }
// findProduct();
Product.fireSale().then(data =>
console.log(data));
Intro to Mongoose Virtual
const personSchema = new
mongoose.Schema({
first: String,
last: String
})
//In Mongoose, a virtual is a property
that is not stored in MongoDB. Virtuals
are typically used for computed
properties on documents.
//Suppose you have a User model. Every
user has an email, but you also want the
email's domain. For example, the domain
portion of 'test@gmail.com' is
'gmail.com'.
// defining a getter
personSchema.virtual('fullName').get(func
tion () {
return `${this.first + this.last}`
})
// the above method is not saved in the
database. it just working in the client
side
const Person = mongoose.model('Person',
personSchema);
// defining Mongoose middleware
/*
Middleware (also called pre and post
hooks) are functions which are passed
control during execution of asynchronous
functions. Middleware is specified on the
schema level and is useful for writing
plugins.
Mongoose has 4 types of middleware:
document middleware, model middleware,
aggregate middleware, and query
middleware.
Document middleware is supported for the
following document functions. In
Mongoose, a document is an instance of a
Model class. In document middleware
functions, this refers to the document.
To access the model, use
this.constructor.
validate
save
remove
updateOne
deleteOne
init (note: init hooks are synchronous)
Query middleware is supported for the
following Query functions. Query
middleware executes when you call exec()
or then() on a Query object, or await on
a Query object. In query middleware
functions, this refers to the query.
count
countDocuments
deleteMany
deleteOne
estimatedDocumentCount
find
findOne
findOneAndDelete
findOneAndRemove
findOneAndReplace
findOneAndUpdate
remove
replaceOne
update
updateOne
updateMany
validate
Aggregate middleware is for
MyModel.aggregate(). Aggregate middleware
executes when you call exec() on an
aggregate object. In aggregate
middleware, this refers to the
aggregation object.
aggregate
Model middleware is supported for the
following model functions. Don't confuse
model middleware and document middleware:
model middleware hooks into static
functions on a Model class, document
middleware hooks into methods on a Model
class. In model middleware functions,
this refers to the model.
insertMany
Here are the possible strings that can be
passed to pre()
aggregate
count
countDocuments
deleteOne
deleteMany
estimatedDocumentCount
find
findOne
findOneAndDelete
findOneAndRemove
findOneAndReplace
findOneAndUpdate
init
insertMany
remove
replaceOne
save
update
updateOne
updateMany
validate
All middleware types support pre and post
hooks.