Mongoose cho MongoDB, Nodejs

Mongoose là một thư viện dùng để quản lý mối quan hệ giữa dữ liệu trong MongoDB và Node.js. Nó hỗ trợ việc mô hình hóa đối tượng (ODM) và cung cấp các tính năng như xác thực cấu trúc dữ liệu và biểu diễn đối tượng trong MongoDB.

Mongoose cho MongoDB, Nodejs

MongoDB là một cơ sở dữ liệu NoSQL có khả năng lưu trữ các tệp JSON và không yêu cầu cấu trúc giống như cơ sở dữ liệu SQL. Điều này giúp tăng tốc độ phát triển ứng dụng và giảm độ phức tạp của việc triển khai.

Dưới đây là ví dụ về cách cấu trúc dữ liệu được lưu trữ trong MongoDB so với SQL:

Mongoose cho MongoDB, Nodejs

Các thuật ngữ liên quan:

Collections (Bộ sưu tập): Tương đương với các bảng trong các cơ sở dữ liệu quan hệ. Chúng có thể chứa nhiều tài liệu JSON.

Documents (Tài liệu): Tương đương với các bản ghi trong SQL. Trong MongoDB, các tài liệu thường được tổ chức trong cùng một tài liệu.

Fields (Trường): Tương tự như các cột trong một bảng SQL.

Schema (Lược đồ): Trong MongoDB không yêu cầu cấu trúc, nhưng trong SQL, lược đồ định nghĩa qua việc định nghĩa bảng. Trong Mongoose, lược đồ là một lớp định nghĩa cấu trúc dữ liệu được áp dụng cho các đối tượng ứng dụng.

Models (Mô hình): Là một lớp cao hơn có một lược đồ và tạo ra một thực thể tương tự với bản ghi trong cơ sở dữ liệu quan hệ.

Cài đặt MongoDB: Truy cập trang web MongoDB https://docs.mongodb.com/manual/installation/ để cài đặt MongoDB phù hợp với hệ điều hành của bạn và làm theo hướng dẫn.

  • Tạo một cơ sở dữ liệu sandbox miễn phí trên mLab.
  • Cài đặt MongoDB bằng Docker nếu bạn sử dụng Docker. Điều này bạn sẽ đi sâu vào một số vấn đề cơ bản của Mongoose bằng cách triển khai mô hình dữ liệu cho một sổ địa chỉ đơn giản.

Cài đặt NPM: Đi vào thư mục dự án và khởi tạo dự án bằng lệnh sau:

npm init -y

Cài đặt Mongoose và một thư viện xác thực bằng lệnh:

npm install mongoose validator

Lệnh cài đặt trên sẽ cài đặt phiên bản mới nhất của thư viện. Cú pháp Mongoose trong bài viết này áp dụng cho Mongoose v5 trở lên.

Kết nối cơ sở dữ liệu: Tạo tệp ./src/database.js trong thư mục gốc của dự án.

Tiếp theo, chúng ta sẽ tạo một phương thức kết nối với cơ sở dữ liệu.

Kết nối sẽ thay đổi tùy thuộc vào cài đặt của bạn.

let mongoose = require('mongoose');
const server = '127.0.0.1:27017'; // THAY THẾ BẰNG MÁY CHỦ DB
const database = 'fcc-Mail'; // THAY THẾ BẰNG TÊN CSDL

class Database {
  constructor() {
    this._connect()
  }

  _connect() {
    mongoose.connect(`mongodb://${server}/${database}`)
      .then(() => {
        console.log('Kết nối cơ sở dữ liệu thành công')
      })
      .catch(err => {
        console.error('Lỗi kết nối cơ sở dữ liệu')
      })
  }
}

module.exports = new Database()

Yêu cầu lệnh (‘mongoose’) sẽ trả về một đối tượng Singleton. Lần đầu tiên bạn nhập (‘mongoose’), nó tạo ra một lớp Mongoose và trả về nó. Trong các lần gọi tiếp theo, nó sẽ trả về cùng một phiên bản đã được tạo và trả lại cho bạn lần đầu tiên vì cách nhập / xuất mô-đun trong ES6.

Mongoose cho MongoDB, Nodejs

Tương tự như vậy, chúng ta đã biến lớp Database của mình thành một Singleton bằng cách trả lại một phiên bản lớp trong module.exports, vì chúng ta chỉ cần một kết nối duy nhất với cơ sở dữ liệu.

Có Thể Bạn Quan Tâm :   Google Play là gì? Những điều cần biết về Google Play

ES6 cho phép chúng ta dễ dàng tạo các Singleton pattern (mẫu đơn) vì mô-đun hoạt động như thế nào bằng cách lưu trữ phản hồi của một tệp đã nhập trước đó.

Mongoose Schema và Model

Mongoose model bao gồm Mongoose schema. Mongoose schema định nghĩa cấu trúc của tài liệu, các giá trị mặc định và xác thực, trong khi Mongoose model cung cấp một giao diện để tạo, truy vấn, cập nhật và xóa các bản ghi.

Để tạo một mô hình Mongoose, bạn cần ba phần chính:

  1. Tham chiếu Mongoose: let mongoose = require(‘mongoose’)
  2. Định nghĩa Schema: Một schema định nghĩa các thuộc tính của tài liệu qua một đối tượng, trong đó tên khóa tương ứng với tên thuộc tính trong tập hợp.
  3. Xuất mô hình: Chúng ta gọi hàm khởi tạo mô hình trên Mongoose và truyền nó tên bộ sưu tập và tham chiếu đến schema.

Ví dụ, hãy tạo một mô hình EmailModel với thuộc tính email:

let mongoose = require('mongoose')
let emailSchema = new mongoose.Schema({ email: String })
module.exports = mongoose.model('Email', emailSchema)

Chúng ta cũng có thể tạo một thể hiện của mô hình đã định nghĩa trước đó và điền nó bằng cách sử dụng cú pháp sau:

let EmailModel = require('./email')
let msg = new EmailModel({ email: '[email protected]' })

Hãy nâng cấp schema Email để email trở thành một trường bắt buộc và chuyển đổi giá trị thành chữ thường trước khi lưu nó. Chúng ta cũng có thể thêm một hàm xác thực để đảm bảo rằng giá trị là một địa chỉ email hợp lệ. Chúng ta sẽ tham khảo và sử dụng lại thư viện validator đã được cài đặt trước đó.

let mongoose = require('mongoose')
let validator = require('validator')

let emailSchema = new mongoose.Schema({
  email: {
    type: String,
    required: true,
    unique: true,
    lowercase: true,
    validate: (value) => {
      return validator.isEmail(value)
    }
  }
})

module.exports = mongoose.model('Email', emailSchema)

Các hoạt động cơ bản: Mongoose cung cấp một API mạnh mẽ và linh hoạt để thực hiện nhiều hoạt động phức tạp được hỗ trợ bởi MongoDB. Chúng ta sẽ không tập trung vào phần này vì nó nằm ngoài phạm vi của bài viết, nhưng hãy nhớ rằng hầu hết các hoạt động có thể được thực hiện theo nhiều cách khác nhau bằng cú pháp khác nhau hoặc thông qua kiến trúc ứng dụng.

Tạo bản ghi

Hãy tạo một thể hiện của mô hình Email và lưu nó vào cơ sở dữ liệu:

let EmailModel = require('./email')
let msg = new EmailModel({ email: '[email protected]' })

msg.save()
  .then(doc => {
    console.log(doc)
  })
  .catch(err => {
    console.error(err)
  })

Kết quả trả về khi lưu thành công:

{
  _id: 5a78fe3e2f44ba8f85a2409a,
  email: '[email protected]',
  __v: 0
}

Các trường sau được trả về (Trường giữa bằng kí tự gạch dưới “_”): _id (khóa chính), email (giá trị email), __v (phiên bản tài liệu).

Nếu bạn cố gắng tạo một bản ghi tiếp theo với cùng giá trị email, bạn sẽ nhận được lỗi vì chúng ta đã định nghĩa email là duy nhất.

Lấy bản ghi

Hãy lấy lại bản ghi mà chúng ta đã lưu trước đó. Lớp mô hình cung cấp một số phương thức tĩnh để thao tác với cơ sở dữ liệu. Bây giờ, chúng ta sẽ tìm kiếm hồ sơ mà chúng ta đã tạo sử dụng cụm từ tìm kiếm email.

EmailModel
  .find({ email: '[email protected]' }) // truy vấn tìm kiếm
  .then(doc => {
    console.log(doc)
  })
  .catch(err => {
    console.error(err)
  })

Tài liệu trả về sẽ giống như những gì chúng ta đã tạo:

{
  _id: 5a78fe3e2f44ba8f85a2409a,
  email: '[email protected]',
  __v: 0
}

Cập nhật bản ghi: Hãy chỉnh sửa bản ghi trên bằng cách thay đổi email và thêm một trường khác vào đó, tất cả trong một thao tác.

EmailModel
  .findOneAndUpdate(
    { email: '[email protected]' }, // truy vấn tìm kiếm
    { email: '[email protected]' }, // các trường:giá trị để cập nhật
    { new: true, runValidators: true } // tuỳ chọn
  )
  .then(doc => {
    console.log(doc)
  })
  .catch(err => {
    console.error(err)
  })

Tài liệu trả về sẽ chứa email đã cập nhật:

{
  _id: 5a78fe3e2f44ba8f85a2409a,
  email: '[email protected]',
  __v: 0
}

Xóa bản ghi: Chúng ta sử dụng phương thức findOneAndRemove để xóa một bản ghi. Nó sẽ trả lại các tài liệu ban đầu đã xóa:

EmailModel
  .findOneAndRemove({ email: '[email protected]' }) // truy vấn tìm kiếm
  .then(response => {
    console.log(response)
  })
  .catch(err => {
    console.error(err)
  })

Các cuộc gọi trợ giúp: Chúng ta đã nêu một số chức năng cơ bản như tạo, đọc, cập nhật và xóa (CRUD), nhưng Mongoose cũng cho phép định cấu hình các loại phương thức trợ giúp và thuộc tính khác. Chúng có thể được sử dụng để đơn giản hóa công việc làm việc với dữ liệu.

Có Thể Bạn Quan Tâm :   HỎI - ĐÁP dậy thì thành công là gì mà cộng đồng mạng hay sử dụng?

Hãy tạo một schema cho người dùng trong /src/models/user.js với các trườngfirstName và lastName:

let mongoose = require('mongoose')
let userSchema = new mongoose.Schema({
  firstName: String,
  lastName: String
})
module.exports = mongoose.model('User', userSchema)

Tài liệu thư viện: Tạo ra một thuộc tính ảo gọi là fullName có thể được sử dụng để đặt và lấy các giá trị của firstName và lastName dưới dạng một giá trị kết hợp khi đọc:

userSchema.virtual('fullName').get(function() {
  return this.firstName + ' ' + this.lastName
})

userSchema.virtual('fullName').set(function(name) {
  let str = name.split(' ')
  this.firstName = str[0]
  this.lastName = str[1]
})

Chú ý rằng phải sử dụng từ khóa function trong các callback để có thể truy cập vào đối tượng mô hình. Sử dụng mũi tên function sẽ thay đổi cách tham chiếu.

Bây giờ, chúng ta có thể đặt firstName và lastName bằng cách gán giá trị cho fullName:

let model = new UserModel()
model.fullName = 'Thomas Anderson'
console.log(model.toJSON()) // Xuất ra các trường của model dưới dạng JSON
console.log()
console.log(model.fullName) // Xuất ra tên đầy đủ

Mã trên sẽ xuất ra:

{
  _id: 5a78fe3e2f44ba8f85a2409a,
  firstName: 'Thomas',
  lastName: 'Anderson'
}

Các phương thức: Chúng ta có thể tạo các phương thức tuỳ chỉnh trên schema và truy cập chúng thông qua mô hình. Những phương thức này có thể truy cập đối tượng mô hình và có thể được sử dụng cho mục đích riêng. Ví dụ: Chúng ta có thể tìm tất cả những người dùng có cùng tên.

Trong ví dụ này, hãy tạo một hàm để trả về tên đệm cho người dùng hiện tại. Thêm một phương thức trợ giúp tùy chỉnh gọi là getInitials vào schema:

userSchema.methods.getInitials = function() {
  return this.firstName[0] + this.lastName[0]
}

Phương thức này có thể truy cập thông qua một mô-đun mô hình:

let model = new UserModel({ firstName: 'Thomas', lastName: 'Anderson' })
let initials = model.getInitials()
console.log(initials) // Kết quả: TA

Phương thức tĩnh: Tương tự như các phương thức instance, chúng ta cũng có thể tạo các phương thức tĩnh trên schema. Hãy tạo một phương thức để lấy tất cả người dùng trong cơ sở dữ liệu:

userSchema.statics.getUsers = function() {
  return new Promise((resolve, reject) => {
    this.find((err, docs) => {
      if(err) {
        console.error(err)
        return reject(err)
      }
      resolve(docs)
    })
  })
}

UserModel.getUsers()
  .then(docs => {
    console.log(docs)
  })
  .catch(err => {
    console.error(err)
  })

Thêm nữa, các trợ giúp và phương thức tĩnh là một cách tốt để triển khai một giao diện tương tác với cơ sở dữ liệu trong mô hình và bộ sưu tập. Middleware (Trung gian): Middleware là các hàm thực thi tại các giai đoạn cụ thể trong luồng xử lý. Mongoose hỗ trợ middleware cho các hoạt động sau:

  • Tổng hợp (aggregate)
  • Tài liệu (document)
  • Mô hình (model)
  • Truy vấn (query)
Có Thể Bạn Quan Tâm :   Hướng dẫn người bán cách xem mã vận đơn trên shopee

Ví dụ, các mô hình có các phương thức trước và sau có hai tham số:

  1. Loại sự kiện (‘init’, ‘validate’, ‘save’, ‘remove’)
  2. Một hàm callback được thực thi với tham chiếu này xem ví dụ mô hình:

Hãy thử một ví dụ bằng cách thêm hai trường createdAt và updatedAt vào schema của chúng ta:

let mongoose = require('mongoose')
let userSchema = new mongoose.Schema({
  firstName: String,
  lastName: String,
  createdAt: Date,
  updatedAt: Date
})
module.exports = mongoose.model('User', userSchema)

Khi gọi model.save(), các sự kiện pre(‘save’, …) và post(‘save’, …) được kích hoạt. Hãy thêm một Middleware trước khi lưu và đặt giá trị cho createdAt và updatedAt:

userSchema.pre('save', function(next) {
  let now = Date.now()
  this.updatedAt = now

  // Đặt giá trị cho createdAt chỉ khi nó là null
  if (!this.createdAt) {
    this.createdAt = now
  }

  // Gọi hàm tiếp theo trong chuỗi pre-save
  next()
})

Hãy tạo và lưu một mô hình:

let UserModel = require('./user')
let model = new UserModel({ fullName: 'Thomas Anderson' })

msg.save()
  .then(doc => {
    console.log(doc)
  })
  .catch(err => {
    console.error(err)
  })

Bạn sẽ thấy các giá trị cho createdAt và updatedAt khi bản ghi được tạo ra được in ra:

{
  _id: 5a7bbbeebc3b49cb919da675,
  firstName: 'Thomas',
  lastName: 'Anderson',
  updatedAt: 2018-02-08T02:54:38.888Z,
  createdAt: 2018-02-08T02:54:38.888Z,
  __v: 0
}

Giả sử chúng ta muốn theo dõi khi một bản ghi được tạo ra và cập nhật lần cuối trên mỗi bộ sưu tập trong cơ sở dữ liệu. Thay vì lặp lại quá trình trên, chúng ta có thể tạo một plugin và áp dụng nó cho tất cả các schema. Hãy tạo file ./src/model/plugins/timestamp.js và viết lại hàm trên dưới dạng một module có thể sử dụng lại:

module.exports = function timestamp(schema) {
  // Thêm hai trường vào schema
  schema.add({
    createdAt: Date,
    updatedAt: Date
  })

  // Tạo một middleware trước khi lưu
  schema.pre('save', function(next) {
    let now = Date.now()
    this.updatedAt = now

    // Đặt giá trị cho createdAt chỉ khi nó là null
    if (!this.createdAt) {
      this.createdAt = now
    }

    // Gọi hàm tiếp theo trong chuỗi pre-save
    next()
  })
}

Để sử dụng plugin này, chúng tôi chỉ cần truyền nó vào các schema mà chúng ta muốn áp dụng chức năng này:

let timestampPlugin = require('./plugins/timestamp')

emailSchema.plugin(timestampPlugin)
userSchema.plugin(timestampPlugin)

Xây dựng truy vấn

Mongoose cung cấp một API phong phú để xây dựng các hoạt động phức tạp được hỗ trợ bởi MongoDB.

UserModel.find() // tìm tất cả người dùng
  .skip(100) // bỏ qua 100 mục đầu tiên
  .limit(10) // giới hạn 10 mục
  .sort({ firstName: 1 }) // sắp xếp theo thứ tự tăng dần firstName
  .select({ firstName: true }) // chỉ chọn firstName
  .exec() // thực thi truy vấn
  .then(docs => {
    console.log(docs)
  })
  .catch(err => {
    console.error(err)
  })

Tổng kết

Chúng ta vừa mới tìm hiểu một số tính năng cơ bản của Mongoose. Đây là một thư viện phong phú và mạnh mẽ với nhiều tính năng hữu ích khi làm việc với mô hình dữ liệu trong các ứng dụng Node.js.

Bạn có thể tương tác trực tiếp với MongoDB bằng cách sử dụng MongoDB Driver, nhưng Mongoose giúp đơn giản hóa việc tương tác đó bằng cách mô hình hóa các mối quan hệ giữa dữ liệu và xác thực chúng một cách dễ dàng.

Tham khảo:

https://www.codementor.io/theoutlander/introduction-to-mongoose-for-mongodb-gw9xw34el

http://mongoosejs.com/

https://www.mongodb.com/

Back to top button