[Database] Bài 9 – Viết Code Quản Lý Database (Kết Thúc)

Trong bài viết này chúng ta sẽ cùng nói về khái niệm View trong quản lý Database nói chung. Đây cũng là bài viết cuối cùng về code triển khai database đơn giản trước khi chúng ta quay lại Sub-Series ExpressJS để viết code xử lý cho các route và hoàn thành trang blog cá

Trong bài viết này chúng ta sẽ cùng nói về khái niệm View trong quản lý Database nói chung. Đây cũng là bài viết cuối cùng về code triển khai database đơn giản trước khi chúng ta quay lại Sub-Series ExpressJS để viết code xử lý cho các route và hoàn thành trang blog cá nhân như đã dự định.

View

Chúng ta sẽ xuất phát với một tính năng giả định của trang chủ mà bạn đang xây dựng cho blog cá nhân. Giả sử bạn muốn bổ sung một tính năng tự động tải thêm các đoạn entry giới thiệu tới các bài viết khác khi người dùng cuộn trang web vượt qua 10 entry được tải sẵn.

Tính năng này cũng rất thường thấy trong thiết kế của các trang mạng xã hội hoặc các trang web chia sẻ các nội dung đa phương tiện như audio, video, … mang tính chất cập nhật thường xuyên.

Như vậy mỗi khi người dùng cuộn qua đoạn entry cuối cùng thì sẽ có một yêu cầu gửi tới server để truy vấn thêm vài bản ghi Article tính từ id tiếp theo của entry cuối cùng. Và lúc này chúng ta thấy rằng chúng ta đang cần làm việc với tập kết quả tiềm năng của thao tác select trong nhóm procedure/Article rất thường xuyên.

Có khi thì chúng ta cần select 10 bài viết đầu tiên để render ra trang chủ lần đầu. Và ở một thời điểm khác, khi người dùng cuộn qua entry cuối cùng thì chúng ta lại cần select 5 bài viết tính từ id tiếp theo. Và rất có thể trong tương lai rất có thể bạn còn muốn thực hiện thêm thao tác làm thống kê số lượt đọc cho mỗi trang bài viết.

Lúc này chúng ta thấy tập kết quả tiềm năng của select bao gồm tất cả 1001 bài viết trong database lại đang là chất liệu cho nhiều thao tác truy vấn tổ hợp khác. Và như vậy thì sẽ thật tiện lợi nếu như chúng ta có thể biểu thị tất cả 1001 bản ghi này ở dạng một nguồn dữ liệu trừu tượng và có thể được lặp qua khi cần sử dụng tới. Nguồn dữ liệu này có thể được xem như một bảng dữ liệu bao gồm các bản ghi được biểu thị bởi các hàng dữ liệu và mỗi cột là một trường dữ liệu @id, title, short-title, v.v…

+--------------+-----------------------+---------------+-----------------+
|        @id   |   title               |   .........   |   category-id   |
+--------------+-----------------------+---------------+-----------------+
|       0000   |   Làm Thế Nào .....   |   .........   |            00   |
+--------------+-----------------------+---------------+-----------------+
|       0001   |   Cách Chèn Ảnh ...   |   .........   |            01   |
+--------------+-----------------------+---------------+-----------------+
|       ....   |   .................   |   .........   |            ..   |
+--------------+-----------------------+---------------+-----------------+
|   Infinity   |   Chưa Đăng Tải ...   |   .........   |      Infinity   |
+--------------+-----------------------+---------------+-----------------+

Bây giờ chúng ta sẽ mô phỏng nguồn dữ liệu trừu tượng này trong phần mềm quản lý database đơn giản đang xây dựng. Chúng ta sẽ tạo ra một thư mục dành riêng cho các View database/view và viết code tạo ra một object View chứa 1001 bản ghi Article. Lý tưởng ở đây vẫn là việc chúng ta không cần phải tải toàn bộ 1001 bản ghi Article vào môi trường phần mềm cùng lúc mà chỉ đơn giản là cung cấp một phương thức cho phép code bên ngoài có thể lặp tuần tự qua nguồn dữ liệu này.

const readAllRecordId =require("../procedure/sub-procedure/read-all-record-ids--async-throw");const selectArticleById =require("../procedure/Article/select-by-id--async-throw");const Article =require("../type/Article");const ArticleView =class{/* --- Index data into the view instance */staticasyncindexData(
      out_instance =newArticleView(),
      in_order ="default",/* default | reversed */
      in_partial = Article.fieldNames
   ){/* --- Initialize allRecordIds */
      out_instance.allRecordIds =[];awaitreadAllRecordId(Article.name, out_instance.allRecordIds);/* --- Order the id-list (if needed) */if(in_order =="default"){/* keep the order */;}else
         out_instance.allRecordIds.reverse();/* --- Initialize fieldNames */
      out_instance.fieldNames = in_partial;return ArticleView;}/* --- Provide iterator method */
   async *[Symbol.asyncIterator](){for(var recordId ofthis.allRecordIds){var selected =newArticle();awaitselectArticleById(recordId, selected,this.fieldNames);yield selected;}// for .. of}};// ArticleView

module.exports = ArticleView;

Việc khởi tạo thuộc tính allRecordIds sẽ cần sử dụng thao tác async do đó chúng ta không thể sử dụng constructor. Thay vào đó thì một phương thức static async có tên là indexData được sử dụng để truy vấn danh sách id của tất cả các bản ghi và đặt vào object đã được khởi tạo rỗng.

Về việc sử dụng Symbol để tạo ra phương thức asyncIterator hỗ trợ vòng lặp for await ... of thì chúng ta đã thực hiện trong bài viết về Date & Symbol của Sub-Series JavaScript.

Bây giờ thì chúng ta sẽ thử lặp qua toàn bộ 5 bản ghi Article đang có trong database thông qua View này.

const ArticleView =require("./database/view/Article");voidasyncfunction(){var view =newArticleView();await ArticleView.indexData(view,"reversed",["@id","title","short-title","category-id"])forawait(var record of view){
      console.log(record);}}();// void
npm test

Article(4) [Map] {
  '@id' => 'Infinity',
  'title' => 'Cách Chèn Ảnh & Các Liên Kết',
  'short-title' => 'Ảnh & Liên Kết',
  'category-id' => 'Infinity'
}
Article(4) [Map] {
  '@id' => '0003',
  'title' => 'Sử Dụng Các Nội Dung Nhúng',
  'short-title' => 'Các Nội Dung Nhúng',
  'category-id' => '01'
}
Article(4) [Map] {
  '@id' => '0002',
  'title' => 'Cách Chèn Ảnh & Các Liên Kết',
  'short-title' => 'Ảnh & Liên Kết',
  'category-id' => '01'
}
Article(4) [Map] {
  '@id' => '0001',
  'title' => 'Làm Thế Nào Để Tạo Ra Một Trang Web?',
  'short-title' => 'Giới Thiệu HTML',
  'category-id' => '01'
}
Article(4) [Map] {
  '@id' => '0000',
  'title' => 'Series Tự Học Lập Trình Web Một Cách Thật Tự Nhiên',
  'short-title' => 'Tự Học Lập Trình Web',
  'category-id' => '00'
}

Và như vậy là chúng ta đã có được một nguồn dữ liệu trừu tượng để làm chất liệu cho các thao tác select tổ hợp. Bây giờ chúng ta có thể viết lại code cho thủ tục select-by-category-id của nhóm procedure/Article bằng cách sử dụng View này làm nguồn truy vấn. Logic xử lý của select-by-category-id bây giờ sẽ trở nên đơn giản và dễ quan sát hơn nhiều.

const ArticleView =require("../../view/Article");const Article =require("../../type/Article");

module.exports =async(
   in_categoryId ="",
   out_selected =[],
   in_top =Infinity,
   in_order ="default",/* default | reversed */
   in_partial = Article.fieldNames
)=>{/* --- Create view & index data */var view =newArticleView();await ArticleView.indexData(view, in_order, in_partial)/* --- Loop & Select each record to check */forawait(var record of view){/* --- Limit the result set */if(out_selected.length == in_top)break/* out of the loop */;else{/* keep collecting record */;}/* --- Collect the Article if category-id matches */if(record.get("category-id")!= in_categoryId){/* record is not matched */;}else
         out_selected.push(record);}// for ... of};

Và code chạy thử sẽ chọn ra Top 2 bản ghi Article mới nhất của danh mục HTML.

const databaseManager =require("./database/manager");const Article =require("./database/type/Article");voidasyncfunction(){var selected =[];await databaseManager.execute(
      Article.name,"select-by-category-id",/* in_recordId */"01",/* out_selected */   selected,/* out_top */2,/* out_order */"reversed",/* out_partial */["@id","title","short-title","category-id"]);

   console.log(selected);}();// void
npm test

[
  Article(4) [Map] {
    '@id' => '0003',
    'title' => 'Sử Dụng Các Nội Dung Nhúng',
    'short-title' => 'Các Nội Dung Nhúng',
    'category-id' => '01'
  },
  Article(4) [Map] {
    '@id' => '0002',
    'title' => 'Cách Chèn Ảnh & Các Liên Kết',
    'short-title' => 'Ảnh & Liên Kết',
    'category-id' => '01'
  }
]

Kết thúc bài viết

Như vậy là chúng ta đã được gặp khái niệm View trong quản lý Database nói chung và đã tạo ra một View để sử dụng cho các thao tác truy vấn tổ hợp trên các bản ghi Article. Bạn có thể tạo ra những View khác nữa cho các kiểu bản ghi còn lại là Category, ArticleJoinCategory, v.v… dựa trên thao tác select-by-id cơ bản. Những View này còn được gọi với một cái tên khác là Non-indexed View – có nghĩa là View chưa có dữ liệu sẵn mà chỉ khi nào được khởi tạo và chạy View.indexData thì mới có thể được sử dụng.

Các phần mềm quản lý Relational Database thường hỗ trợ chúng ta tạo ra các View kiểu Non-Indexed như trên và bên cạnh đó còn có thêm các View kiểu Indexed. Các Indexed View được triển khai bằng cách tạo ra các tệp dữ liệu thực từ các bản ghi kết quả tiềm năng và các tệp này được cập nhật đồng bộ liên tục với các tệp dữ liệu gốc.

Ví dụ chúng ta có thể tạo ra một Indexed View cho kiểu ArticleJoinCategory bằng cách tạo ra một thư mục lưu các tệp dữ liệu là các bản ghi tương ứng với kết quả truy vấn từ các tệp dữ liệu gốc trong thư mục data/Articledata/Category. Sau đó xây dựng các Procedure để kích hoạt việc cập nhật các tệp dữ liệu của View khi các tệp dữ liệu gốc trong hai thư mục kia có sự thay đổi.

Ở thời điểm hiện tại thì các Indexed View thực sự chưa có nhiều ý nghĩa đối với việc xây dựng trang blog đơn giản của chúng ta. Thêm vào đó là việc viết code triển khai cũng sẽ đem lại sự phức tạp không cần thiết. Do đó chúng ta sẽ ghi chú thêm điểm này và tạm thời dừng lại với phần giới thiệu Indexed View thật mơ hồ. 😄

Bây giờ thì chúng ta đã có thể quay trở lại Sub-Series ExpressJS để viết code xử lý các route và đăng tải trang blog cá nhân đơn giản lên Glitch.com. Hẹn gặp lại bạn trong những bài viết tiếp theo.

(Sắp đăng tải) [ExpressJS] Bài 6 – Viết Code Cho Các Route Xử Lý Yêu Cầu

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ