Session 04: MongoDB and Mongoose
Install MongoDB and tools
-
To be able to experiment with the code examples, you will need access to a MongoDB database.
-
You can download a free MongoDB database at https://www.mongodb.com.
-
Install (MacOS):
https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-os-x
-
Install (Windows):
https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-windows
-
Compass Tool:
-
Extension for VS Code:
-
PaaS: Get started right away with a MongoDB cloud service at https://www.mongodb.com/cloud/atlas.
Install MongoDB driver and Mongoose
Download and install mongodb package:
npm install mongodb --save
npm install mongoose --save
Mongoose SchemaTypes
- String
- Number
- Date
- Buffer
- Boolean
- Mixed
- ObjectId
- Array
- Decimal128
- Map
- Schema
Mongoose Built-in Validators
https://mongoosejs.com/docs/validation.html#built-in-validators
Create Mongoose Model
Category
const mongoose = require('mongoose');
const { Schema, model } = mongoose;
const categorySchema = new Schema({
name: { type: String, required: true },
description: String,
});
const Category = model('Category', categorySchema);
module.exports = Category;
Product
const mongoose = require('mongoose');
const { Schema, model } = mongoose;
const productSchema = new Schema({
name: { type: String, required: true },
price: { type: Number, required: true, min: 0, default: 0 },
discount: { type: Number, min: 0, max: 75, default: 0 },
stock: { type: Number, min: 0, default: 0 },
categoryId: { type: Schema.Types.ObjectId, ref: 'Category', required: true },
supplierId: { type: Schema.Types.ObjectId, ref: 'Supplier', required: true },
});
const Product = model('Product', productSchema);
module.exports = Product;
Manipulate & read data with Mongoose
Create
const { default: mongoose } = require('mongoose');
const { Category } = require('../models');
mongoose.connect('mongodb://localhost:27017/training-database');
try {
const data = {
name: 'Thời trang',
description: 'Mô tả ...',
};
const newItem = new Category(data);
newItem.save().then((result) => {
console.log(result);
});
} catch (err) {
console.log(err);
}
Update
const { default: mongoose } = require('mongoose');
const { Category } = require('../models');
mongoose.connect('mongodb://localhost:27017/training-database');
try {
const id = '63623f180b99f96759d9cc30';
const data = { name: 'New Name' };
Category.findByIdAndUpdate(id, data, {
new: true,
}).then((result) => {
console.log(result);
});
} catch (err) {
console.log(err);
}
Delete
const { default: mongoose } = require('mongoose');
const { Category } = require('../models');
mongoose.connect('mongodb://localhost:27017/training-database');
try {
const id = '6362647d5ff5a41f96d24df6';
Category.findByIdAndDelete(id).then((result) => {
console.log(result);
});
} catch (err) {
console.log(err);
}
Get all data
const { default: mongoose } = require('mongoose');
const { Category } = require('../models');
mongoose.connect('mongodb://localhost:27017/training-database');
try {
Category.find().then((result) => {
console.log(result);
});
} catch (err) {
console.log(err);
}
Get by Id
const { default: mongoose } = require('mongoose');
const { Category } = require('../models');
mongoose.connect('mongodb://localhost:27017/training-database');
try {
const id = '636263af9766a403ebf7328c';
Category.findById(id).then((result) => {
console.log(result);
});
} catch (err) {
console.log(err);
}
Mongoose Virtuals
In Mongoose, a virtual is a property that is not stored in MongoDB. Virtuals are typically used for computed properties on documents.
First virtual
productSchema.virtual('total').get(function () {
return (this.price * (100 - this.discount)) / 100;
});
Virtuals in JSON
By default, Mongoose does not include virtuals when you convert a document to JSON. For example, if you pass a document to Express' res.json() function, virtuals will not be included by default.
To include virtuals in res.json(), you need to set the toJSON schema option to { virtuals: true }
.
Example:
productSchema.set('toJSON', { virtuals: true });
const Product = model('Product', productSchema);
module.exports = Product;
Virtuals in console.log()
// Virtuals in console.log()
productSchema.set('toObject', { virtuals: true });
const Product = model('Product', productSchema);
module.exports = Product;
Virtuals with Lean
Virtuals are properties on Mongoose documents. If you use the lean option, that means your queries return POJOs (Plain old Javascript object) rather than full Mongoose documents. That means no virtuals if you use lean().
Setup
const mongooseLeanVirtuals = require('mongoose-lean-virtuals');
Create s schema
const userSchema = new mongoose.Schema({ name: String });
userSchema.virtual('lowercase').get(function () {
return this.name.toLowerCase();
});
Apply plugin
userSchema.plugin(mongooseLeanVirtuals);
Later
You must pass virtuals: true
to lean()
, otherwise lowercase
won't be in res
const res = await UserModel.find().lean({ virtuals: true });
Limitations of virtuals
Mongoose virtuals are not stored in MongoDB, which means you can't query based on Mongoose virtuals.
Virtuals with Populate
Mongoose also supports populating virtuals. A populated virtual contains documents from another collection. To define a populated virtual, you need to specify:
- The ref option, which tells Mongoose which model to populate documents from.
- The localField and foreignField options. Mongoose will populate documents from the model in ref whose foreignField matches this document's localField.
// Virtual with Populate
productSchema.virtual('category', {
ref: 'Category',
localField: 'categoryId',
foreignField: '_id',
justOne: true,
});
try {
Product.find({})
.populate('category')
.then((result) => {
console.log(result);
});
} catch (err) {
console.log(err);
}