# Lesson 9 - Database Relationships

## Lý thuyết về quan hệ

Trong cơ sở dữ liệu, một quan hệ là một cách để kết nối các bảng hoặc đối tượng dữ liệu khác nhau với nhau dựa trên các trường hoặc thuộc tính chung. Quan hệ cho phép bạn xác định và biểu diễn cách các dữ liệu có mối quan hệ với nhau, điều này giúp cải thiện tổ chức và hiệu quả của dữ liệu trong cơ sở dữ liệu. Dưới đây là một số lợi ích của việc sử dụng quan hệ trong cơ sở dữ liệu:

1. **Liên kết Dữ liệu**: Quan hệ cho phép bạn liên kết dữ liệu từ các bảng khác nhau bằng cách sử dụng các khóa ngoại. Điều này giúp kết nối thông tin từ nhiều nguồn dữ liệu và cung cấp một cái nhìn toàn diện về thông tin.
2. **Giảm Dữ Liệu Trùng Lặp**: Thay vì lặp lại thông tin trong mỗi bảng, quan hệ cho phép bạn lưu trữ thông tin chung ở một nơi duy nhất và sử dụng khóa ngoại để tham chiếu đến nó. Điều này giúp giảm dữ liệu trùng lặp và tiết kiệm không gian lưu trữ.
3. **Bảo Dựng Dữ Liệu Phức Tạp**: Bằng cách sử dụng quan hệ, bạn có thể xây dựng dữ liệu phức tạp hơn, bao gồm các tầng quan hệ, ví dụ: mối quan hệ giữa khách hàng và đơn đặt hàng trong một cơ sở dữ liệu bán hàng.
4. **Truy vấn Dữ Liệu Hiệu Quả**: Quan hệ cho phép bạn thực hiện các truy vấn phức tạp trên dữ liệu mà không cần dò qua toàn bộ cơ sở dữ liệu. Bạn có thể truy vấn dữ liệu từ nhiều bảng một cách hiệu quả thông qua các liên kết quan hệ.
5. **Bảo Trì Dễ Dàng**: Khi dữ liệu thay đổi, bạn chỉ cần cập nhật nó một lần và các thay đổi sẽ tự động lan tỏa đến các bảng khác thông qua quan hệ. Điều này giúp giảm nguy cơ dữ liệu không đồng nhất.
6. **Quản Lý Tính Toàn Vẹn**: Quan hệ cũng hỗ trợ tính toàn vẹn của dữ liệu. Bằng cách sử dụng ràng buộc và quy tắc quan hệ, bạn có thể đảm bảo dữ liệu trong cơ sở dữ liệu luôn tuân thủ các quy định và ràng buộc.

Quan hệ giúp bạn mô hình hóa dữ liệu một cách hiệu quả và cung cấp mối quan hệ giữa các dữ liệu khác nhau để cung cấp giá trị thực sự trong việc lưu trữ và truy cập thông tin.

Ví dụ về quan hệ phổ biến trong cơ sở dữ liệu bao gồm:&#x20;

**Quan Hệ Một Nhiều (One-to-Many)**:

Ví dụ về quan hệ một nhiều có thể liên quan đến khách hàng và đơn đặt hàng. Mỗi khách hàng có thể có nhiều đơn đặt hàng. Dưới đây là một ví dụ về dữ liệu:

* Lớp "Customers":

  ```markdown
  customer_id | customer_name
  -----------------------------
  1          | Alice
  2          | Bob
  ```
* Lớp "Orders":

  ```markdown
  odeorder_id | order_date | customer_id
  ----------------------------------
  101      | 2023-01-15 | 1
  102      | 2023-01-20 | 1
  103      | 2023-02-05 | 2
  ```

Trong ví dụ này, khách hàng có ID 1 (Alice) có hai đơn đặt hàng, trong khi khách hàng có ID 2 (Bob) chỉ có một đơn đặt hàng. Điều này là một ví dụ về quan hệ một nhiều giữa "Customers" và "Orders".

**Quan Hệ Nhiều Nhiều (Many-to-Many)**:

Ví dụ về quan hệ nhiều nhiều có thể liên quan đến học sinh và lớp học. Mỗi học sinh có thể đăng ký nhiều lớp học và mỗi lớp học có thể có nhiều học sinh. Dưới đây là một ví dụ về dữ liệu:

* Lớp "Students":

  ```markdown
  student_id | student_name
  ---------------------------
  1          | Alice
  2          | Bob
  ```
* Lớp "Classes":

  ```markdown
  class_id | class_name
  ---------------------
  101      | Math
  102      | Science
  ```
* Lớp trung gian "StudentClasses":

  ```markdown
  student_id | class_id
  ---------------------
  1          | 101
  1          | 102
  2          | 101
  ```

Trong ví dụ này, học sinh có ID 1 (Alice) đăng ký lớp học "Math" và "Science", trong khi học sinh có ID 2 (Bob) chỉ đăng ký lớp học "Math". Điều này là một ví dụ về quan hệ nhiều nhiều giữa lớp "Students" và "Classes".

1. **Quan Hệ Một Một (One-to-One)**:

   Ví dụ về quan hệ một một có thể liên quan đến khách hàng và tài khoản ngân hàng. Mỗi khách hàng có thể có một tài khoản ngân hàng và ngược lại, mỗi tài khoản ngân hàng chỉ thuộc về một khách hàng. Dưới đây là một ví dụ về dữ liệu:

   * Lớp "Customers":

     ```markdown
     customer_id | customer_name | account_id
     ---------------------------------------
     1          | Alice        | 101
     2          | Bob          | 102
     ```
   * Lớp "BankAccounts":

     ```markdown
     account_id | account_number
     ---------------------------
     101        | 12345
     102        | 67890
     ```

   Trong ví dụ này, khách hàng có ID 1 (Alice) có tài khoản ngân hàng với số tài khoản "12345", trong khi khách hàng có ID 2 (Bob) có tài khoản ngân hàng với số tài khoản "67890". Điều này là một ví dụ về quan hệ một một giữa Lớp "Customers" và "BankAccounts".

Với các cơ sở dữ liệu quan hệ, việc thiết kế được quan hệ giữa các lớp (bảng) sẽ yêu cầu thêm về các chuẩn hoá dữ liệu, ràng buộc, ...&#x20;

## Quan hệ trong MongoDB

Với các cơ sở dữ liệu NoSQL như MongoDB, sử dụng mô hình dữ liệu JSON-like (BSON) và không hỗ trợ các ràng buộc thường thấy trong quan hệ, như các ràng buộc khóa ngoại.

Tuy nhiên, Mongodb hỗ trợ các kiểu dữ liệu linh hoạt và các tài liệu có thể lồng ghép nhau, cho phép bạn mô phỏng quan hệ một trong cơ sở dữ liệu NoSQL này.&#x20;

Dưới đây là cách mô phỏng các quan hệ trong Mongodb:

1. **Quan hệ Một Nhiều (One-to-Many)**:

   Bạn có thể mô phỏng quan hệ một nhiều trong Mongodb bằng cách lưu trữ các tài liệu của bảng con trong tài liệu của bảng cha. Ví dụ, bạn có một Collection "Customers" và mỗi khách hàng có nhiều đơn đặt hàng. Trong tài liệu của khách hàng, bạn có thể lưu trữ một mảng các đơn đặt hàng.

   Ví dụ tài liệu cho khách hàng trong Mongodb:

   ```json
   {
     "customer_id": 1,
     "customer_name": "Alice",
     "orders": [
       {
         "order_id": 101,
         "order_date": "2023-01-15"
       },
       {
         "order_id": 102,
         "order_date": "2023-01-20"
       }
     ]
   }
   ```
2. **Quan Hệ Nhiều Nhiều (Many-to-Many)**:

   Quan hệ nhiều nhiều cũng có thể được mô phỏng bằng cách sử dụng mảng hoặc danh sách các tham chiếu đến các tài liệu của bảng liên quan. Ví dụ, bạn có một bảng "Students" và một bảng "Classes". Mỗi học sinh có thể tham gia nhiều lớp học và mỗi lớp học có thể có nhiều học sinh.

   Ví dụ tài liệu trong Mongodb:

   Tài liệu cho học sinh:

   ```json
   {
     "student_id": 1,
     "student_name": "Alice",
     "classes": [101, 102]
   }
   ```

   Tài liệu cho lớp học:

   ```json
   {
     "class_id": 101,
     "class_name": "Math",
     "students": [1, 2]
   }
   ```
3. **Quan Hệ Một Một (One-to-One)**:

   Quan hệ một một trong Mongodb có thể được mô phỏng bằng cách lưu trữ các thông tin của cả hai bảng trong cùng một tài liệu. Ví dụ, bạn có một bảng "Customers" và một bảng "BankAccounts". Mỗi khách hàng chỉ có một tài khoản ngân hàng và ngược lại, mỗi tài khoản ngân hàng chỉ thuộc về một khách hàng.

   Ví dụ tài liệu trong Mongodb:

   ```json
   {
     "customer_id": 1,
     "customer_name": "Alice",
     "bank_account": {
       "account_id": 101,
       "account_number": "12345"
     }
   }
   ```

Mongodb cho phép bạn thiết kế cơ sở dữ liệu một cách linh hoạt và tuỳ chỉnh để phù hợp với nhu cầu của ứng dụng của bạn. Tuy nhiên, việc mô phỏng quan hệ trong Mongodb thường cần xem xét cẩn thận về cách bạn tổ chức và truy xuất dữ liệu để đảm bảo hiệu suất và tính nhất quán.

## Một số lưu ý

Khi bạn thiết kế quan hệ cho MongoDB, dưới đây là một số kinh nghiệm quan trọng để xem xét:

1. **Tư duy Mô hình Dữ liệu Không SQL (NoSQL)**:

   MongoDB là một cơ sở dữ liệu NoSQL, nên hãy tư duy mô hình dữ liệu theo cách không SQL. Hãy quên điều kiện ràng buộc (constraints) và quan hệ SQL truyền thống và tập trung vào việc thiết kế dựa trên nhu cầu ứng dụng của bạn.

   Mặc dù MongoDB truy vấn rất nhanh, tuy nhiên, nếu quá lạm dụng, sẽ gây khó khăn trong quá trình thiết kế logic truy vấn. Việc tách, liên kết các collection theo 3 kiểu quy ước quan hệ: 1-1, 1-n, m-m, vẫn hoàn toàn có thể áp dụng với MongoDB.
2. **Lựa chọn Giữa Nhúng và Tham Chiếu**:

   Hãy xem xét cẩn thận lựa chọn giữa việc nhúng dữ liệu vào tài liệu hoặc sử dụng tham chiếu. Cân nhắc rằng lựa chọn này sẽ ảnh hưởng đến cách bạn truy vấn dữ liệu và hiệu suất.

   Cách thức Tham chiếu là một cách thông minh trong việc thiết kế, nhưng tuỳ vào yêu cầu nghiệp vụ mà ta cần phải linh hoạt, lựa chọn một trong hai hoặc kết hợp cả hai.
3. **Primary key**

   Khoá chính là từ khoá không thể thiếu trong bất kỳ CSDL nào, MongoDB cũng không ngoại lệ, với MongoDB, khoá chính là một field trong document (\_id).

   Với các CSDL SQL, sẽ mặc định không cho phép thay đổi trường này, tuy nhiên, với MongoDB, các dữ liệu document là dạng JSON, mỗi một trường đều có thể thay đổi, thậm chí là có thể xoá được (trường \_id cũng không ngoại l&#x1EC7;*).* Cần phải tuyệt đối cẩn thận với các thao tác CUD nếu xuất hiện thông tin trường dữ liệu \_id trong các thông tin cần cập nhật.

## Triển khai

Ví dụ xây dựng một ứng dụng quản lý sách với hai tài liệu: "Sách" và "Tác giả". Mỗi cuốn sách sẽ có một tác giả tham chiếu đến tài liệu "Tác giả". Dưới đây là cách bạn có thể thực hiện điều này bằng Mongoose:

1. Định nghĩa Mô hình "authors":

```javascript
import mongoose from 'mongoose';

const authorSchema = new mongoose.Schema({
  name: String,
  birthdate: Date,
});

const AuthorModel = mongoose.model('authors', authorSchema);
export default AuthorModel;
```

2. Định nghĩa Mô hình "books" với trường tham chiếu "authors":

```javascript
import mongoose from 'mongoose';

const bookSchema = new mongoose.Schema({
  title: String,
  author: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'authors', // Tham chiếu đến collection authors
  },
});

const BookModel = mongoose.model('books', bookSchema);

export default BookModel;
```

3. Tạo và lưu trữ dữ liệu:

```javascript
import AuthorModel from './authorModel';
import BookModel from './bookModel';

// Tạo một tác giả
const author = new AuthorModel({
  name: 'J.K. Rowling',
  birthdate: new Date('1965-07-31'),
});

// Lưu tác giả vào cơ sở dữ liệu
author.save((err, savedAuthor) => {
  if (err) {
    console.error(err);
    return;
  }

  // Tạo một cuốn sách tham chiếu đến tác giả
  const book = new BookModel({
    title: 'Harry Potter and the Sorcerer\'s Stone',
    author: savedAuthor._id, // Tham chiếu đến tác giả
  });

  // Lưu cuốn sách vào cơ sở dữ liệu
  book.save((err) => {
    if (err) {
      console.error(err);
    }
  });
});
```

Khi truy vấn cuốn sách, ta có thể sử dụng `.populate()` để lấy thông tin của tác giả mà cuốn sách tham chiếu:

```javascript
BookModel.findOne({ title: 'Harry Potter and the Sorcerer\'s Stone' })
  .populate('author')
  .exec((err, book) => {
    if (err) {
      console.error(err);
      return;
    }
    console.log('Tên cuốn sách:', book.title);
    console.log('Tên tác giả:', book.author.name);
  });
```

## Thực hành

Bài tập về User, cùng bài Post, Comment từ các bài học trước, hãy thực hiện refactor cho các model và yêu cầu.

## Kết

Qua nội dung này, ta sẽ có thể tối ưu được việc quản trị dữ liệu, thiết kế dữ liệu một cách hợp lý với MongoDB, một dạng CSDL NoSQL. Ví dụ trên minh họa cách thiết kế tham chiếu quan hệ giữa các tài liệu sử dụng Mongoose trong MongoDB
