Middleware trong Mongoose

Phùng Hùng

Phùng Hùng

Teacher and Programmer

Các kiểu middleware

Mongoose có 4 kiểu middleware, ta phân biệt chúng bằng giá trị của this và các phương thức ta có thể áp dụng:

  • Document Middleware

    • this là document hiện tại.
    • Các phương thức sau của document có thể áp dụng được middleware này:
      • validate
      • save
      • remove
      • updateOne
      • deleteOne
  • Query Middleware

    • this là query
    • Các phương thức sau của query và model được hỗ trợ:
      • count
      • deleteMany
      • deleteOne
      • find
      • findOne
      • findOneAndDelete
      • findOneAndRemove
      • findOneAndUpdate
      • remove
      • update
      • updateOne
      • updateMany
  • Model Middleware

    • this là model
    • chỉ phương thức sau của model được hỗ trợ:
      • insertMany
  • Aggregate Middleware

    • this là aggregation object.
    • chạy khi bạn gọi exec() trên aggregate object.

Tìm hiểu về Mongoose Schema

Phùng Hùng

Phùng Hùng

Teacher and Programmer

Định nghĩa một Schema

Schema giúp định nghĩa "shape" của các document trong một collection. Mỗi collection sẽ có một schema tương ứng.

Để tạo một schema, sử dụng class mongoose.Schema:

const mongoose = require('mongoose');
const blogSchema = new mongoose.Schema({
title: String, // viết tắt của `{type: String}`
author: String,
body: String,
// mảng các embed document có
// hai trường `body` và `date`
comments: [
{
body: String,
date: Date
}
],
// Kiểu Date và khai báo giá trị
// mặc định là ngày giờ tạo document
data: {
type: Date,
default: Date.now
}
hidden: Boolean,
// nested document
// truy cập qua
// meta.votes và meta.favs
meta: {
votes: Number,
favs: Number
}
});

Tìm hiểu thêm về Schema trong Mongoose

Phùng Hùng

Phùng Hùng

Teacher and Programmer

Tạo model từ Schema

const Blog = mongoose.model('Blog', blogSchema);

Tạo phương thức cho document

Từ Schema ta có thể tạo ra một phương thức mới cho mọi document qua đôi tượng schema.methods.

Ví dụ sau tạo ra phương thức findSimilarTypes cho các document của animalSchema:

// define a schema
const animalSchema = new Schema({ name: String, type: String });
// assign a function to the "methods" object of our animalSchema
animalSchema.methods.findSimilarTypes = function (cb) {
return mongoose.model('Animal').find({ type: this.type }, cb);
};
const Animal = mongoose.model('Animal', animalSchema);
const dog = new Animal({ type: 'dog' });
dog.findSimilarTypes((err, dogs) => {
console.log(dogs); // woof
});
  • Chú ý là không nên dùng hàm mũi tên để tạo phương thức.
  • this trỏ đến document.

Tạo phương thức tĩnh cho Model

// Assign a function to the "statics" object of our animalSchema
animalSchema.statics.findByName = function (name) {
return this.find({ name: new RegExp(name, 'i') });
};
// Or, equivalently, you can call `animalSchema.static()`.
animalSchema.static('findByBreed', function (breed) {
return this.find({ breed });
});
const Animal = mongoose.model('Animal', animalSchema);
let animals = await Animal.findByName('fido');
animals = animals.concat(await Animal.findByBreed('Poodle'));
  • Chú ý là không nên dùng hàm mũi tên để tạo phương thức tĩnh.
  • this trỏ đến model.

Tạo chỉ mục

Có thể tạo trong SchemaType hoặc sử dụng phương thức index của schema.

const animalSchema = new Schema({
name: String,
type: String,
tags: { type: [String], index: true }, // field level
});
animalSchema.index({ name: 1, type: -1 }); // schema level

Khi ứng dụng khởi động, Mongoose tự động gọi createIndex để tạo các chỉ mục được khai báo trong Schema. Sự kiện index được bắn ra khi createIndex thực hiện xong cho mọi chỉ mục.

// Will cause an error because mongodb has an _id index by default that
// is not sparse
animalSchema.index({ _id: 1 }, { sparse: true });
const Animal = mongoose.model('Animal', animalSchema);
Animal.on('index', error => {
// "_id index cannot be sparse"
console.log(error.message);
});

Hành vi này giúp thuận tiện cho bạn trong quá trình phát triển ứng dụng, nhưng khi triển khai sản phẩm bạn nên tắt nó đi vì nó làm giảm hiệu năng:

mongoose.connect('mongodb://localhost', { autoIndex: false });
// hoặc
animalSchema.set('autoIndex', false);
// hoặc
new Schema({..}, { autoIndex: false });

Thuộc tính ảo

// define a schema
const personSchema = new Schema({
name: {
first: String,
last: String,
},
});
// Định nghĩa thuộc tính ảo
personSchema.virtual('fullName').get(function () {
return this.name.first + ' ' + this.name.last;
});

Có thể định nghĩa cả getter và setter cho thuộc tính ảo:

personSchema
.virtual('fullName')
.get(function () {
return this.name.first + ' ' + this.name.last;
})
.set(function (v) {
this.name.first = v.substr(0, v.indexOf(' '));
this.name.last = v.substr(v.indexOf(' ') + 1);
});

Populate trong Mongoose

Phùng Hùng

Phùng Hùng

Teacher and Programmer

Mongoose có populate() là cách thức mạnh mẽ hơn thay thế cho $lookup của MongoDB.

Population là quá trình tự động thay thế một path cụ thể bằng các document trong một collection khác. Chúng ta có thể populate một document, nhiều document, một đối tượng, nhiều đối tượng hoặc tất cả các đôi tượng trả về từ một query.

Ví dụ:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const personSchema = new Schema({
_id: Schema.Types.ObjectId,
name: String,
age: Number,
stories: [{ type: Schema.Types.ObjectId, ref: 'Story' }],
});
const storySchema = new Schema({
author: { type: Schema.Types.ObjectId, ref: 'Person' },
title: String,
fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }],
});
const Story = mongoose.model('Story', storySchema);
const Person = mongoose.model('Person', personSchema);

Chúng ta đã tạo hai model. Model Person của chúng ta có trường stories là mảng các ObjectId. Tùy chọn ref cho biết ta muốn Mongoose sử dụng population, trong trường hợp này là đối với model Story. Tất cả _id được lưu trong stories sau khi population sẽ trở thành các document có _id tương ứng trong Story.

Truy vấn dữ liệu bằng Mongoose

Phùng Hùng

Phùng Hùng

Teacher and Programmer

Các phương thức để truy vấn dữ liệu từ Model

  • Các phương thức để đọc dữ liệu:

    • Model.find()
    • Model.findById()
    • Model.findOne()
  • Các phương thức để cập nhật dữ liệu:

    • Model.findByIdAndUpdate()
    • Model.findOneAndUpdate)()
    • Model.findOneAndReplace()
    • Model.updateOne()
    • Model.updateMany()
    • Model.replaceOne()
  • Các phương thức để xóa dữ liệu

    • Model.deleteOne()
    • Model.deleteMany()
    • Model.findByIdAndDelete()
    • Model.findByIdAndRemove()
    • Model.findOneAndDelete()
    • Model.findOneAndRemove()

Subdocument trong Mongoose

Phùng Hùng

Phùng Hùng

Teacher and Programmer

Subdocument là các document được nhúng trong các document khác. Trong Mongoose, bạn tạo subdocument bằng cách lống schema này trong schema khác. Mongoose có hai cách kí hiệu subdocument:

  • mảng các subdocument
  • một subdocument đơn.
const childSchema = new Schema({ name: String });
const parentSchema = new Schema({
// Array of subdocuments
children: [childSchema],
// Single nested subdocuments. Caveat: single nested subdocs only work
// in mongoose >= 4.2.0
child: childSchema,
});

Đối với mảng các subdocument có thể sử dụng cú pháp ngắn gọn hơn mà không phải tạo childSchemanhư sau:

const parentSchema = new Schema({
// Array of subdocuments
children: [{ name: String }],
});

Validation trong Mongoose

Phùng Hùng

Phùng Hùng

Teacher and Programmer

Trước khi bắt đầu, bạn cần biết một số quy tắc sau đây đối với validation trong mongoose:

  • Validation phải được định nghĩa trong đối tượng SchemaType.
  • Validation bản chất là một pre('save') middleware do mongoose tự tạo ra.
  • Bạn có thể vô hiệu hóa validation trước khi lưu document bằng tùy chọn validateBeforeSave: schema.set('validateBeforeSave', true).
  • Bạn có thể chạy validation thủ công bằng phương thức validate hoặc validateSync.
  • Có thể đánh dấu một trường là không hợp lệ bằng phương thức invalidate.
  • Các validator không thể chạy với giá trị undefined. Ngoại lệ duy nhất là required validator.
  • Validation hoạt động bất đồng bộ theo cơ chế đệ quy; khi gọi save, validation ở subdocument đươc thực hiện trước.
  • Có thể tùy chỉnh được quá trình validation.

Tìm hiểu nhanh về React Hook Form

Phùng Hùng

Phùng Hùng

Teacher and Programmer

Cài đặt

npm install react-hook-form

Ví dụ

Đoạn mã sau mô tả cách sử dụng cơ bản của React Hook Form:

import React from 'react';
import { useForm } from 'react-hook-form';
const App = () => {
const { register, handleSubmit, watch, errors } = useForm();
const onSubmit = data => console.log(data);
console.log(watch('example')); // theo dõi giá trị trường bằng cách truyền tên của nó
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input name="example" defaultValue="test" ref={register} />
<input name="exampleRequired" ref={register({ required: true })} />
{errors.exampleRequired && <span>This field is required</span>}
<input type="submit" />
</form>
);
};
export default App;

Phương thức register

Cho phép bạn đăng ký một input với React Hook Form, chỉ khi một trường được đăng ký nó mới được xác thực và submit.