Meteor: Sử dụng Publish & Subscribe với hàm tạo Query
meteor
17
Male avatar

Nguyen Quang Phuong viết ngày 25/09/2015

Có một thứ mà hầu như mọi lập trình viên đều không thích, đó là lặp lại code. Nếu như phải kể đến hai thứ mà lập trình viên không thích, thì đó là lặp lại code và code không bảo mật.

Chúng ta sẽ cùng tìm hiểu một số pattern giúp giải quyết hai vấn đề trên.

Một hàm publish đơn giản

Để tìm hiểu về pattern này, đầu tiên hãy cùng xem xét một ví dụ đơn giản về hàm publish trả về 10 bài post mới nhất:

// server
Meteor.publish('posts', function() {
  return Posts.find({}, {sort: {createdAt: -1}, limit: 10});
});

Ở phía client, chúng ta sẽ subscribe (bên trong router hoặc template):

// client
Meteor.subscribe('posts');

Và cuối cùng, query bài viết với:

// client
var latestPosts = Posts.find();

Mặc dù đoạn code phía trên hoạt động đúng cho trường hợp của chúng ta, nhưng nếu vì một lý do nào đó, chúng ta đã subscribe nhiều hơn là 10 bài post, thì lệnh find() sẽ không còn chắc chắn sẽ trả về đúng 10 bài post mới nhất. Chính vì vậy mà chúng ta sẽ phải thêm điều kiện query cho cả phía client:

// client
var latestPosts = Posts.find({}, {sort: {createdAt: -1}, limit: 10});

Tới đây, có lẽ bạn đã nhận ra chúng ta đã bị lặp lại code: câu query bên trong hàm Posts.find() bây giờ bị lặp lại cả client và hàm publish. Hãy cùng thử khắc phục vấn đề này!

Giải pháp sai

Chúng ta có thể giải quyết bằng cách chỉ định nghĩa query cho hàm Posts.find() ở phía client, sau đó truyền tham số lên server. Implement sẽ như sau:

// server
Meteor.publish('posts', function(parameters) {
  return Posts.find(parameters.find, parameters.options);
});

Ở phía client:

// client
var parameters = {
  find: {},
  options: {sort: {createdAt: -1}, limit: 10}
};

Meteor.subscribe('posts', parameters);
var latestPosts = Posts.find(parameters.find, parameters.options);

Giải pháp này không tốt vì chúng ta đã tạo ra một lỗ hổng bảo mật cực kỳ lớn. Trong khi chúng ta có thể chắc chắn rằng ứng dụng của mình chỉ truyền tham số sạch, gửi lên đúng parameters.findparameters.options, chúng ta không thể đảm bảo cho mọi client nói chung.

Không có gì ngăn cản một người dùng nào đó dùng console trình duyệt và subscribe với một bản dữ liệu parameters.find riêng, điều đó sẽ làm cho toàn bộ dự liệu của collection bị lộ ra, dù bạn có muốn hay không!

Dùng tham số tập trung

Chúng ta không thể tin tưởng vào client, vậy thay vào đó tại sao không thử tạo một object trung tâm, share giữa client và server, để giữ tham số Mongo? Nó sẽ giống như đoạn code sau:

// client & server
latestPost = {
  find: {},
  options: {sort: {createdAt: -1}, limit: 10}
}

// server
Meteor.publish('posts', function() {
  return Posts.find(latestPost.find, latestPost.options);
});

// client
Meteor.subscribe('posts');
var latestPosts = Posts.find(latestPost.find, latestPost.options);

Cách này cũng có vẻ ổn, nhưng chưa thực sự mềm dẻo vì chúng ta đã phải hard-code cho object. Ví dụ, điều gì sẽ xảy ra nếu chúng ta muốn client có thể tự quyết định giá trị limit cho subscribe thành 10, 20 hoặc 30 bài viết?

Hàm tạo Query

Vậy hãy cùng thử thêm một lần nữa, nhưng lần này chúng ta sẽ dùng hàm thay cho object:

// client & server
latestPost = function (limit) {
  return {
    find: {},
    options: {sort: {createdAt: -1}, limit: limit}
  };
}

// server
Meteor.publish('posts', function(limit) {
  return Posts.find(latestPost(limit).find, latestPost(limit).options);
});

// client
var limit = 20;
Meteor.subscribe('posts', limit);
var latestPosts = Posts.find(latestPost(limit).find, latestPost(limit).options);

Về bản chất, hàm này lấy yêu cầu cho tham số để query ("hãy cho tôi X bài viết mới nhất") và tạo ra thứ gì đó mà MongoDB có thể hiểu được ({}, {sort: {createdAt: -1}, limit: X}). Bởi vậy chúng ta sẽ gọi nó là hàm tạo query (Query Constructor).

Hàm tạo query của chúng ta còn có thể làm được nhiều hơn thế: ví dụ, có thể bạn muốn một giới hạn cho chính tham số limit là 100 chẳng hạn, để ngăn cản người dùng có thể query 10 triệu bản ghi một lúc, tránh quá tải cho server. Với pattern tạo query như trên, chúng ta chỉ phải giới hạn một chỗ duy nhất:

// client & server
latestPost = function (limit) {

  if (limit > 100) {
    limit = 100;
  }

  return {
    find: {},
    sort: {sort: {createdAt: -1}, limit: limit}
  };
}

Query View

Cho tới bây giờ, chúng ta mới chỉ xét đến một cách đơn để lọc dữ liệu (lấy bản ghi mới nhất). Tuy nhiên, trên thực tế, bạn sẽ muốn nhiều hơn một cách để truy vấn dữ liệu: sắp xếp theo thời gian tạo dữ liệu, thời gian sửa đổi gần nhất, độ phổ biến của bài viết... Hãy cùng gọi mỗi cách như vậy là một view (khái niệm này không liên quan đến phần View trong cấu trúc MVC).

Đầu tiên, mỗi view sẽ được định nghĩa bởi hàm riêng:

views = {};

// client & server
views.latestPosts = function (terms) {
  return {
    find: {},
    sort: {sort: {createdAt: -1}, limit: terms.limit}
  };  
}

views.mostPopularPosts = function (terms) {
  return {
    find: {},
    sort: {sort: {score: -1}, limit: terms.limit}
  };  
}

Để cho code thêm mềm dẻo, chúng ta sẽ truyền vào object là terms thay vì chỉ truyền vào limit. Điều này sẽ dễ dàng để truyền vào nhiều option trong tương lai, ví dụ như là terms.category, terms.date...

Thêm vào đó, hãy chú ý cách chúng ta đã dùng để loại bỏ limit. Chúng ta sẽ thêm vào hàm tạo query, một cách tổng quát hơn cho tất cả các view:

// client & server

queryConstructor = function (terms) {

  var viewFunction = views[terms.viewName]
  var parameters = viewFunction(terms);

  if (parameters.limit > 100) {
    parameters.limit = 100;
  }

  return parameters;

}

Cuối cùng, đây sẽ là cách chúng ta sử dụng hàm tạo query:

// server
Meteor.publish('posts', function(terms) {
  var parameters = queryConstructor(terms);
  return Posts.find(parameters.find, parameters.options);
});

// client
var terms = {
  viewName: Session.get('view'),
  limit: 20
}

Meteor.subscribe(terms);

var parameters = queryConstructor(terms);
var latestPosts = Posts.find(parameters.find, parameters.options);

Thực hành

Một khi bạn đã quen với pattern view/hàm tạo, việc thêm view sẽ cực kỳ đơn giản. Ví dụ, sau đây là cách mà Telescope  thiết lập một số view:

/**
 * New view
 */
Posts.views.add("new", function (terms) {
  return {
    options: {sort: {sticky: -1, postedAt: -1}}
  };
});

/**
 * Best view
 */
Posts.views.add("best", function (terms) {
  return {
    options: {sort: {sticky: -1, baseScore: -1}}
  };
});

/**
 * Pending view
 */
Posts.views.add("pending", function (terms) {
  return {
    find: {
      status: Posts.config.STATUS_PENDING
    },
    options: {sort: {createdAt: -1}},
    showFuture: true
  };
});

Chúng ta sẽ không đi sâu vào hàm tạo query của Telescope, tuy nhiên sau đây là một số task mà nó xử lý:

  • Định nghĩa một số option find/sort mặc định dùng trong trường hợp view hiện tại không cung cấp.
  • Thêm vào query sort bởi _id
  • Giới hạn số lượng bài viết
  • Giấu bài viết đã được lập lịch công khai trong tương lai

Kết luận

Đây là một patter đơn giản, nhưng rất hữu ích cho việc xử lý những vấn đề cơ bản chung trong Meteor khi publish và subscribe.

Nguồn: Managing Publications & Subscriptions With Query Constructors

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

Male avatar

Nguyen Quang Phuong

10 bài viết.
76 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
Male avatar
20 12
Chương 1 Giới thiệu Hãy thử tưởng tượng trong đầu một thí nghiệm như sau. Từ màn hình máy tính, mở 2 cửa sổ dẫn tới cùng một thư mục. Sau đó, bấm c...
Nguyen Quang Phuong viết gần 3 năm trước
20 12
Male avatar
18 0
Giới thiệu Bitcoin là gì? Bitcoin là tập hợp của nhiều khái niệm và công nghệ tạo nên thành phần cơ bản cho hệ thống tiền điện tử. Đơn vị tiền t...
Nguyen Quang Phuong viết 2 tháng trước
18 0
Male avatar
6 1
Ấn tượng đầu tiên là quan trọng, và quá trình cài đặt của Meteor nên khá dễ dàng. Trong hầu hết các trường hợp, bạn sẽ sẵn sàng chạy được trong vòn...
Nguyen Quang Phuong viết gần 3 năm trước
6 1
Bài viết liên quan
Male avatar
5 0
Publications và Subscriptions Trong chương này bạn sẽ: Hiểu được cách thức hoạt động của publications và subscriptions. Học xem gói Autopubli...
Nguyen Quang Phuong viết gần 3 năm trước
5 0
Male avatar
6 4
Routing Trong chương này bạn sẽ: Học về routing trong Meteor Tạo trang thảo luận bài viết, với URL độc nhất. Học cách làm sao để liên kết n...
Nguyen Quang Phuong viết gần 3 năm trước
6 4
White
1 0
Setup Cài đặt Meteor OSX/Linux curl https://install.meteor.com/ | sh Window https://install.meteor.com/windows Khởi tạo project Meteor meteor ...
Đào An viết 12 tháng trước
1 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


Male avatar
{{userFollowed ? 'Following' : 'Follow'}}
10 bài viết.
76 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!