Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Node.js
Code

Giới thiệu về Mongoose cho MongoDB và Node.js

by
Length:LongLanguages:
This post is part of a series called An Introduction to Mongoose for MongoDB and Node.js.
Bulk Import a CSV File Into MongoDB Using Mongoose With Node.js

Vietnamese (Tiếng Việt) translation by Caitlyn Cat Tien (you can also view the original English article)

Mongoose là một framework JavaScript thường được sử dụng trong ứng dụng Node.js với database là MongoDB. Trong bài viết này, tôi sẽ giới thiệu cho bạn Mongoose và MongoDB, và quan trọng hơn là đâu là nơi các công nghệ này phù hợp với ứng dụng của bạn.

MongoDB là gì?

Hãy bắt đầu với MongoDB. MongoDB là database (một cơ sở dữ liệu) lưu dữ liệu của bạn dưới dạng tài liệu. Thông thường, các tài liệu (document) này giống với cấu trúc giống như JSON:

Một document được đặt trong một collection. Ví dụ, ví dụ document bên trên định nghĩa một đối tượng user. Đối tượng user này sau đó thường là một phần của collection được gọi là users.

Một trong những yếu tố quan trọng với MongoDB là tính linh hoạt trong cấu trúc của nó. Mặc dù trong ví dụ đầu tiên, đối tượng user chứa thuộc tính firstNamelastName, các thuộc tính này không bắt buộc trong mọi document của mỗi user, đó là một phần trong collection của users. Đây là điều làm cho MongoDB rất khác với các database SQL như MySQL hoặc Microsoft SQL Server, các database đó yêu cầu schema được định nghĩa rõ ràng cho từng đối tượng mà nó lưu trữ.

Khả năng tạo các dynamic object được lưu trữ dưới dạng documents trong database chính là lúc Mongoose hữu dụng.

Mongoose là gì?

Mongoose là một Object Document Mapper (ODM). Điều này có nghĩa là Mongoose cho phép bạn định nghĩa các object (đối tượng) với một schema được định nghĩa rõ ràng, được ánh xạ tới một MongoDB document.

Mongoose cung cấp một số lượng đáng kinh ngạc các chức năng cho việc tạo ra và làm việc với các schema. Mongoose hiện có 8 SchemaTypes. Đó là:

  1. String
  2. Number
  3. Date
  4. Buffer
  5. Boolean
  6. Mixed
  7. ObjectId
  8. Array

Mỗi loại dữ liệu cho phép bạn xác định:

  • giá trị mặc định
  • hàm xác thực
  • cho biết một trường bắt buộc
  • hàm get cho phép bạn thao tác dữ liệu trước khi nó được trả về như một object
  • hàm set cho phép bạn thao tác dữ liệu trước khi nó được lưu vào cơ sở dữ liệu
  • tạo indexes cho phép dữ liệu được nạp nhanh hơn

Ngoài các tùy chọn phổ biến này, một số kiểu dữ liệu nhất định cho phép bạn tùy chỉnh thêm cách dữ liệu được lưu trữ và truy xuất từ một database. Ví dụ, một kiểu dữ liệu String cũng cho phép bạn chỉ định các tùy chọn bổ sung sau đây:

  • chuyển đổi nó thành chữ thường
  • chuyển đổi nó thành chữ in hoa
  • cắt dữ liệu trước khi lưu lại
  • một biểu thức chính quy có thể giới hạn dữ liệu được phép lưu trong quá trình xác thực
  • một enum có thể định nghĩa một danh sách các string (chuỗi) hợp lệ

Các thuộc tính Number and Date đều hỗ trợ chỉ định một giá trị tối thiểu và tối đa cho phép cho field đó.

Bạn nên quen thuộc với hầu hết 8 kiểu dữ liệu. Tuy nhiên, có một số ngoại lệ có thể xảy ra với bạn, chẳng hạn như Buffer, Mixed, ObjectIdArray.

Kiểu dữ liệu Buffer cho phép bạn lưu dữ liệu nhị phân. Một ví dụ phổ biến về dữ liệu nhị phân sẽ là hình ảnh hoặc tệp được mã hóa, chẳng hạn như tài liệu PDF.

Kiểu data Mixed biến thuộc một tính thành một field "anything goes" (thứ gì cũng được). Field này so sánh nhà phát triển có thể sử dụng MongoDB ra sao vì nó không có cấu trúc xác định. Hãy cảnh giác với việc sử dụng kiểu dữ liệu này vì bạn sẽ mất nhiều tính năng tuyệt vời mà Mongoose cung cấp, chẳng hạn như xác thực dữ liệu và phát hiện các thay đổi của entity để tự động biết để cập nhật thuộc tính khi lưu.

Kiểu dữ liệu ObjectId thường chỉ định một liên kết đến một tài liệu khác trong database của bạn. Ví dụ: nếu bạn có một bộ collection về books (sách) và author (tác giả), book document có thể chứa thuộc tính ObjectId có tham chiếu đến một author cụ thể của document.

Kiểu dữ liệu Array cho phép bạn lưu trữ các mảng giống như JavaScript. Với kiểu dữ liệu Array, bạn có thể thực hiện các chức năng phổ biến của JavaScript array, chẳng hạn như push, pop, shift, slice, v.v.

Tóm tắt

Trước khi tiếp tục và tạo ra một số code, tôi chỉ muốn tóm tắt lại những gì chúng ta vừa học được. MongoDB là một database cho phép bạn lưu trữ các document với cấu trúc động. Các document này được lưu bên trong một collection.

Mongoose là một thư viện JavaScript cho phép bạn định nghĩa các schema với dữ liệu được định kiểu rõ ràng. Khi một schema được định nghĩa, Mongoose cho phép bạn tạo một Model dựa trên một schema cụ thể. Model của Mongoose sau đó được ánh xạ tới một MongoDB document thông qua định nghĩa schema của Model.

Khi bạn đã xác định các schema và model của mình, Mongoose chứa nhiều hàm khác nhau cho phép bạn xác thực tính hợp lệ, lưu, xóa và truy vấn dữ liệu của bạn bằng các hàm MongoDB phổ biến. Tôi sẽ nói về việc này nhiều hơn với các ví dụ code cụ thể.

Cài đặt MongoDB

Trước khi chúng ta có thể bắt đầu tạo các schema và model của Mongoose, MongoDB phải được cài đặt và cấu hình. Tôi khuyến nghị bạn nên truy cập trang TMongoDB's Download page. Có một số tùy chọn khác nhau đã có sẵn để cài đặt. Tôi đã liên kết với Community Server. Điều này cho phép bạn cài đặt một phiên bản cụ thể cho hệ điều hành của bạn. MongoDB cũng cung cấp một Enterprise Server và hỗ trợ cài đặt đám mây. Khi toàn bộ các cuốn sách có thể được viết về việc cài đặt, điều chỉnh và giám sát MongoDB, thì tôi sẽ bám chặt vào Community Server.

Một khi bạn đã tải về và cài đặt MongoDB cho hệ điều hành của bạn lựa chọn, bạn sẽ cần phải bắt đầu khởi động database. Tôi khuyên bạn nên truy cập vào phần tài liệu của MongoDB về cách cài đặt MongoDB Community Edition.

Tôi sẽ đợi bạn cấu hình MongoDB. Khi bạn đã sẵn sàng, chúng ta có thể chuyển sang thiết lập Mongoose để kết nối với database MongoDB vừa được cài đặt của bạn.

Thiết lập Mongoose

Mongoose là một framework JavaScript, và tôi sẽ sử dụng trong một ứng dụng Node.js. Nếu bạn đã cài đặt Node.js, bạn có thể chuyển sang bước tiếp theo. Nếu bạn chưa cài đặt Node.js, tôi khuyên bạn nên bắt đầu bằng cách truy cập trang Node.js Download page và chọn trình cài đặt cho hệ điều hành của bạn.

Với Node.js đã được thiết lập và sẵn sàng hoạt động, tôi sẽ tạo một ứng dụng mới và sau đó cài đặt Mongoose NPM Package.

Với dấu nhắc lệnh được đặt ở nơi bạn muốn cài đặt ứng dụng của mình, bạn có thể chạy lệnh sau:

Để khởi tạo ứng dụng của tôi, tôi để mọi thứ làm giá trị mặc định của chúng. Bây giờ tôi sẽ cài đặt gói mongoose như sau:

Với tất cả điều kiện cần có đã được cấu hình, chúng ta hãy kết nối với một database MongoDB. Tôi đã đặt đoạn code sau bên trong file index.js vì tôi đã chọn nó làm nơi khởi đầu cho ứng dụng của mình:

Dòng code đầu tiên gồm thư viện mongoose. Tiếp theo, tôi mở một kết nối đến database mà tôi gọi là mongoose_basics bằng cách sử dụng hàm connect.

Hàm connect chấp nhận hai tham số tùy chọn khác. Tham số thứ hai là đối tượng của các tùy chọn, ở đó bạn có thể định nghĩa username và password, nếu cần. Tham số thứ ba, cũng có thể là tham số thứ hai nếu bạn không có tùy chọn nào, là một hàm callback sau khi cố gắng kết nối. Hàm callback có thể được sử dụng theo một trong hai cách:

Để tránh phải giới thiệu về JavaScript Promises, tôi sẽ sử dụng cách đầu tiên. Dưới đây là file index.js được cập nhật:

Nếu có lỗi xảy ra khi kết nối với database, một exception sẽ được trả lại và tất cả các xử lý tiếp theo sẽ bị hoãn. Khi không có lỗi, tôi đã ghi lại một thông báo thành công đến console.

Mongoose bây giờ được thiết lập và kết nối với một cơ sở dữ liệu được gọi là mongoose_basics. Kết nối MongoDB của tôi không sử dụng username, password hoặc một port đặc biệt nào. Nếu bạn cần đặt các tùy chọn này hoặc bất kỳ tùy chọn nào khác trong khi kết nối, tôi khuyên bạn nên xem lại Mongoose Documentation on connecting. Tài liệu này giải thích chi tiết về nhiều tùy chọn có sẵn cũng như cách tạo nhiều kết nối, connection pooling, replicas, v.v.

Với một kết nối thành công, chúng ta hãy chuyển sang định nghĩa một schema cho Mongoose.

Định nghĩa schema cho Mongoose

Trong phần giới thiệu, tôi đã cho thấy một đối tượng user có chứa hai thuộc tính: firstNamelastName. Trong ví dụ sau, tôi đã dịch document đó thành một schema của Mongoose:

Đây là một schema rất cơ bản chỉ chứa hai đặc tính mà không có thuộc tính nào liên kết với nó. Hãy mở rộng ví dụ này bằng cách chuyển đổi các thuộc tính tên và họ thành các đối tượng con của thuộc tính name. Thuộc tính name sẽ bao gồm cả tên và họ. Tôi cũng sẽ thêm một thuộc tính created theo kiểu Date.

Như bạn có thể thấy, Mongoose cho phép tôi tạo các schema rất linh hoạt với nhiều kết hợp khả dĩ khác nhau theo cách tôi có thể tổ chức data của mình.

Trong ví dụ tiếp theo này, tôi sẽ tạo ra hai schema mới để minh họa cách tạo mối quan hệ với một lược đồ khác: authorbook. Schema của book sẽ chứa một tham chiếu đến schema của author.

Trên đây là schema của author đã mở rộng dựa trên các khái niệm schema của user mà tôi đã tạo trong ví dụ trước. Để liên kết Author và Book với nhau, thuộc tính đầu tiên của schema của author là một thuộc tính _id, và đó kiểu schema ObjectId. _id là cú pháp phổ biến để tạo khóa chính trong Mongoose và MongoDB. Sau đó, giống như schema user, tôi đã định nghĩa một thuộc tính name có chứa tên và họ của tác giả.

Mở rộng theo schema user, author chứa một số loại schema theo kiểu String. Tôi cũng đã thêm một loại schema Buffer có thể chứa ảnh hồ sơ của author. Thuộc tính cuối cùng là ngày tạo ra author; tuy nhiên, bạn có thể nhận thấy nó được tạo ra hơi khác vì nó đã có một giá trị mặc định đã được xác định "now". Khi author được lưu vào database, thuộc tính này sẽ được đặt thành date/time hiện tại.

Để hoàn thành các ví dụ schema, hãy tạo một schema của book có một tham chiếu đến author bằng cách sử dụng kiểu schema ObjectId:

Lược đồ book chứa một số thuộc tính của kiểu String. Như đã đề cập ở trên, nó chứa một tham chiếu đến lược đồ author. Để minh họa thêm các định nghĩa schema mạnh mẽ, schema book cũng chứa một Array các ratings. Mỗi xếp hạng bao gồm summary, detail, numberOfStars, và createddate (ngày tạo ra).

Mongoose cho phép bạn tạo ra các schema có tham chiếu đến các schema khác, hoặc như trong ví dụ trên với thuộc tính ratings, nó cho phép bạn tạo một Array các thuộc tính con có thể chứa trong một schema có liên quan (như book đến author) hoặc nội tuyến như trong ví dụ trên (với book cho một Array ratings).

Tạo và lưu những model của Mongoose

Khi các schema của author book chứng minh tính linh hoạt của Mongoose, tôi sẽ tiếp tục sử dụng các schema đó và lấy ra một model AuthorBook từ chúng.

Một Model của Mongoose, khi được lưu lại, tạo một Document trong MongoDB với các thuộc tính như đã được định nghĩa từ schema mà nó bắt nguồn.

Để minh hoạ cho việc tạo và lưu một object, trong ví dụ tiếp theo này, tôi sẽ tạo ra một số object: một Model Author và một số Model cho Book. Khi được tạo ra, các object này sẽ được lưu lại thành MongoDB bằng cách sử dụng phương thức save của Model.

Trong ví dụ trên, tôi đã vô tình dẫn một tham chiếu đến hai cuốn sách mới đây nhất của tôi. Ví dụ bắt đầu bằng cách tạo và lưu một jamieObject được tạo ra từ một model Author. Bên trong hàm save của jamieObject, nếu một lỗi xảy ra, ứng dụng sẽ xuất ra một exception. Khi việc lưu lại thành công, bên trong hàm save, hai đối tượng book được tạo ra và lưu lại. Tương tự như jamieObject, nếu một lỗi xảy ra khi đang lưu, một lỗi được xuất ra; nếu không, thông báo thành công sẽ được xuất ra trong bảng điều khiển.

Để tạo tham chiếu tới Author, đối tượng book chỉ ra cả tham chiếu đến khóa chính _id của schema author trong thuộc tính author của lược đồ book.

Xác thực dữ liệu trước khi lưu lại

Khá phổ biến với các dữ liệu mà sẽ \tạo ra một model được nhập vào bởi một form trên một website. Vì lý do này, bạn nên xác nhận tính hợp lệ của dữ liệu này trước khi lưu Model vào MongoDB.

Trong ví dụ tiếp theo này, tôi đã cập nhật schema author trước đó để thêm xác nhận vào các thuộc tính sau: firstName, twitter, facebook linkedin.

Thuộc tính firstName đã được gán cho thuộc tính required. Bây giờ khi tôi gọi hàm save, Mongoose sẽ trả về một lỗi với một thông báo cho biết thuộc tính firstName là bắt buộc. Tôi đã chọn không cho thuộc tính lastName thành required trong trường hợp Cher hoặc Madonna là author trong database của tôi.

Các thuộc tính twitter, facebooklinkedin đều có các trình xác thực tùy chỉnh rất giống nhau được áp dụng cho chúng. Họ đảm bảo rằng các giá trị bắt đầu với tên miền tương ứng của các mạng xã hội. Các field này không bắt buộc, vì vậy trình xác thực sẽ chỉ được áp dụng khi dữ liệu được cung cấp cho thuộc tính đó.

Tìm kiếm và cập nhật dữ liệu

Giới thiệu về Mongoose sẽ không hoàn chỉnh nếu không có ví dụ về tìm kiếm bản ghi (record) và cập nhật một hoặc nhiều thuộc tính trên đối tượng đó.

Mongoose cung cấp một số chức năng khác nhau để tìm dữ liệu cho một Model cụ thể. Các hàm find, findOnefindById.

Hàm find findOne đều chấp nhận một object làm đầu vào cho phép các tìm kiếm phức tạp, trong khi findById chỉ chấp nhận một giá trị duy nhất với hàm callback (sẽ có một ví dụ theo sau). Trong ví dụ tiếp theo này, tôi sẽ minh họa cách tìm tất cả các sách chứa string "mvc" trong tiêu đề.

Bên trong hàm find, tôi đang tìm kiếm chuỗi "mvc" không phân biệt chữ hoa chữ thường trong thuộc tính title. Điều này được thực hiện bằng cách dùng cú pháp giống với việc tìm một string với JavaScript.

Việc gọi hàm find cũng được liên kết với các phương thức truy vấn khác, chẳng hạn như where, and, or, limit, sort, any, v.v.

Hãy mở rộng ví dụ trước nhằm giới hạn kết quả của chúng ta với năm book đầu tiên và sắp xếp theo ngày tạo (created date) giảm dần. Nó sẽ trả về tối đa năm book gần đây nhất có chứa "mvc" trong tựa đề.

Sau khi áp dụng hàm find, thứ tự của các hàm khác không quan trọng vì tất cả các hàm có kết nối được biên dịch cùng nhau thành một truy vấn duy nhất và không được thực hiện cho đến khi hàm exec được gọi.

Như tôi đã đề cập trước đó, hàm findById được thực hiện hơi khác một chút. Hàm này thực hiện ngay lập tức và chấp nhận một hàm callback, thay vì cho phép một chuỗi kết nối các hàm. Trong ví dụ tiếp theo, tôi đang truy vấn một author cụ thể bằng _id của họ.

_id trong trường hợp của bạn có thể hơi khác. Tôi đã sao chép _id này từ một console.log trước đó khi tìm danh sách các book có "mvc" trong tựa đề.

Khi một object đã được trả về, bạn có thể sửa đổi bất kỳ thuộc tính nào của object đó để cập nhật. Khi bạn đã thực hiện các thay đổi cần thiết, bạn gọi phương thức save, giống như khi bạn đang tạo object. Trong ví dụ tiếp theo, tôi sẽ mở rộng ví dụ findbyId và cập nhật thuộc tính linkedin của author.

Sau khi một author được truy xuất thành công, thuộc tính linkedin được thiết lập và hàm save được gọi. Mongoose có thể phát hiện rằng thuộc tính linkedin đã được thay đổi và nó sẽ gửi một thông báo cập nhật tới MongoDB chỉ cho các thuộc tính đã được sửa đổi. Nếu xảy ra lỗi khi lưu, một exception sẽ được gửi đi và sẽ tạm dừng ứng dụng. Khi thành công, một thông báo thành công được ghi vào console.

Mongoose cũng cung cấp hai hàm bổ sung để tìm kiếm một đối tượng và lưu lại cùng lúc với các hàm được đặt tên khá phù hợp: findByIdAndUpdate findOneAndUpdate. Hãy nâng cấp ví dụ trước để sử dụng findByIdAndUpdate.

Trong ví dụ trước, các thuộc tính để cập nhật được cung cấp dưới dạng một đối tượng cho tham số thứ hai của hàm findByIdAndUpdate. Hàm callback bây giờ là tham số thứ ba. Khi cập nhật thành công, object author được trả về có chứa thông tin đã được cập nhật. Điều này được ghi vào console để xem các thuộc tính đã được cập nhật của tác giả.

Ví dụ code sau tất cả

Trong suốt bài viết này, tôi đã cung cấp các trích đoạn code nhỏ xác định một hành động rất cụ thể, chẳng hạn như tạo schema, tạo model, v.v. Hãy kết hợp chúng lại với nhau trong một ví dụ đầy đủ.

Đầu tiên, tôi đã tạo hai file bổ sung: author.jsbook.js. Các tệp này chứa các định nghĩa schema tương ứng của chúng và việc tạo ra model. Dòng mã cuối cùng giúp cho model sẵn sàng để sử dụng trong file index.js.

Hãy bắt đầu với file author.js:

Tiếp theo là file book.js:

Và cuối cùng, file index.js được cập nhật:

Trong ví dụ trên, tất cả các hoạt động của Mongoose được chứa trong hàm connect. Các file authorbook được bao kèm với hàm require sau khi liên kết với thư viện mongoose.

Với MongoDB đang chạy, bây giờ bạn có thể hoàn thành ứng dụng Node.js đầy đủ bằng lệnh sau đây:

Sau khi tôi lưu một số data vào database của mình, tôi đã cập nhật file index.js với các hàm find như sau:

Một lần nữa, bạn có thể chạy ứng dụng bằng lệnh: node index.js.

Tóm tắt

Sau khi đọc bài viết này, bạn sẽ có thể tạo các Model và Schema cực kỳ linh hoạt cho Mongose, áp dụng xác thực đơn giản hoặc phức tạp, tạo và cập nhật tài liệu và cuối cùng tìm kiếm các tài liệu đã được tạo.

Hy vọng bây giờ bạn cảm thấy thoải mái khi sử dụng Mongoose. Nếu bạn đang tìm hiểu thêm, tôi sẽ đề nghị bạn xem các Mongoose Guides, các hướng dẫn này nghiên cứu sâu hơn các chủ đề nâng cao như population, middleware, promises, v.v.

Happy hunting (poor Mongoose animal reference)!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.