Ví dụ Populate Mongoose trong NodeJs

Ví dụ Populate Mongoose trong NodeJs là vấn đề được nhiều lập trình viên quan tâm, bởi những kiến thức trên trường lớp là không đủ, vì vậy bài Ví dụ Populate Mongoose trong NodeJs sẽ chia sẻ tới bạn nhiều thông tin hữu ích

Mọi người vẫn hỏi tôi về việc học lập trình nói chung và NodeJS nói riêng có cần năng khiếu hay quá khó để tiếp cận hay không. Thì tôi có thể trả lời rằng, học lập trình cũng như việc bạn học tiếng Anh hay bất kỳ một ngôn ngữ khác, bởi lập trình...là ngôn ngữ của máy tính, để máy tính đọc và máy tính thực hiện theo yêu cầu của mình, bạn thích là học được.

Trong bài viết này chúng ta sẽ cùng nhau đi tìm hiểu về Populdate trong Mongoose.

Trong phiên bản từ 3.2 trở đi Mongose có hỗ trợ một aggregation operator hỗ trợ việc join các document có mối liên hệ với nhau là lockup. Nhưng phổ biến và mạnh mẽ hơn đó chính là populate(), cho phép bạn join các document từ các collections khác.

Populate là quá quá trình tự động thay thế các paths trong documents gốc bằng cách documents trong các documents khác. Chúng ta có thể gộp một hay nhiều document, objects hay tất cả object từ một query. Bên dưới là ví dụ cụ thể:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
 
const personSchema = Schema({
  _id: Schema.Types.ObjectId,
  name: String,
  age: Number,
  stories: [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
 
const storySchema = Schema({
  author: { type: Schema.Types.ObjectId, ref: 'Person' },
  title: String,
  fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
});
 
const Story = mongoose.model('Story', storySchema);
const Person = mongoose.model('Person', personSchema); 

Bên trên chúng ta có đã khởi tạo 2 Models. Person sẽ có một trường lưu trữ stories là một array ObjectID. Thuộc tính ref được sử dụng để cho Mongoose biết rằng trường này được join đến model nào, trong trường hợp này là model Story. Tất cả _ids mà chúng ta lưu trữ phải là _ids của một document trong Story model.

Chú ý: ObjectId, Number, String, và Buffer đều sử dụng được options ref. Tuy nhiên bạn nên sử dụng ObjectID ngoại trư trường hợp đặc biệt.

1. Lưu một refs

Bên trên chúng ta đã khởi tạo 2 Model, bây giờ mình sẽ thêm vào Person một document và đồng nghĩa với nó mình cũng thêm một document trong Story.

const author = new Person({
  _id: new mongoose.Types.ObjectId(),
  name: 'Nguyễ  Du',
  age: 0
});
 
author.save(function (err) {
  if (err) return handleError(err);
 
  const story1 = new Story({
    title: 'Truyện Kiều',
    author: author._id    // Lấy giá trị của ._id cho refs
  });
 
  story1.save(function (err) {
    if (err) return handleError(err);
    //Lưu thành công !!!
  });
}); 

2. Population

Tiếp theo, sau khi thêm một refs bạn có thể thực hiện gộp documents trong Story vào Person quá trình này gọi là populate, cũng gần tương tự như join trong MySQL. Bên dưới mình có ví dụ cụ thể :

Story.
  findOne({ title: 'Truyện Kiều' }).
  populate('author').
  exec(function (err, story) {
    if (err) return handleError(err);
    console.log('Tác giả %s', story.author.name);
    // prints "Tác giả Nguyễn Du"
  }); 

Tùy chọn các trường

Chúng ta cũng có thể tùy chỉnh các trường được trả về để tiết kiệm tài nguyên, ví dụ bên dưới mình sẽ chỉ trả về giá trị author.name mà thôi.

 Story.
  findOne({ title: 'Truyện Kiều' }).
  populate('author', 'name'). // Chỉ trả về Person name
  exec(function (err, story) {
    if (err) return handleError(err);
 
    console.log('Tác giả : %s', story.author.name);
    // prints "Tác giả : Nguyễn Du"
 
    console.log('Tuổi %s', story.author.age);
    // prints "Tuổi : null'
  }); 

Populate với nhiều Paths

Ngoài ra, chúng ta còn có thể populate với nhiều path khác nhau bằng cú pháp sau:

 Story.
  find(...).
  populate('fans').
  populate('author').
  exec(); 

Thêm điều kiện truy vấn

Bạn có thể thêm điều kiện truy vấn, trong ví dụ trên mình sẽ truy vấn fans dựa vào tuổi lớn hơn 50:

 Story.
  find(...).
  populate({
    path: 'fans',
    match: { age: { $gte: 50 } },
    select: 'name -_id'
  }).
  exec(); 

Ngoài ra populate còn hỗ trợ giới hạn các document, thêm object options như ví dụ:

  const stories = Story.find().sort({ name: 1 }).populate({
  path: 'fans',
  options: { limit: 2 } //giới hạn các document
});
 
stories[0].fans.length; // 2
stories[1].fans.length; // 0 

Nếu bạn muốn giới hạn trong mỗi document thì bạn có thể sử dụng perDocumentLimit :

 const stories = await Story.find().sort({ name: 1 }).populate({
  path: 'fans',
 
  perDocumentLimit: 2
});
 
stories[0].fans.length; // 2
stories[1].fans.length; // 2 

Populate với nhiều cấp

Ở đây mình có một Model như sau:

var userSchema = new Schema({
  name: String,
  friends: [{ type: ObjectId, ref: 'User' }]
}); 

Giả sử bạn muốn lấy tất cả bạn bè của một người nào đó, rồi lại lấy bạn bè của những người vừa lấy được =))) Chúng ta sẽ sử dụng cú pháp sau:

 User.
  findOne({ name: 'Val' }).
  populate({
    path: 'friends',
    // Get friends of friends - populate the 'friends' array for every friend
    populate: { path: 'friends' }
  }); 

refPath

Mongoose cũng có thể populate từ nhiều collections khác nhau dựa trên giá trị của thuộc tính trong document. Giả sử bạn đang xây dựng một Schema để lưu trữ bình luận. Người dùng có thể comment về bài đăng trên Blog hoặc Sản phẩm

const commentSchema = new Schema({
  body: { type: String, required: true },
  on: {
    type: Schema.Types.ObjectId,
    required: true,
    refPath: 'onModel'
  },
  onModel: {
    type: String,
    required: true,
    enum: ['BlogPost', 'Product']
  }
});
 
const Product = mongoose.model('Product', new Schema({ name: String }));
const BlogPost = mongoose.model('BlogPost', new Schema({ title: String }));
const Comment = mongoose.model('Comment', commentSchema); 

Lúc này khi lưu một comment bạn chỉ cần chọn Model mà bạn muốn dùng để populate :

const book = await Product.create({ name: 'Hoidapcode});
const post = await BlogPost.create({ title: 'Lập trình NodeJS' });
 
const commentOnBook = await Comment.create({
  body: 'Sách hay',
  on: book._id,
  onModel: 'Product'
});
 
const commentOnPost = await Comment.create({
  body: 'Thông tin rất hữu ích',
  on: post._id,
  onModel: 'BlogPost'
});
 
const comments = await Comment.find().populate('on').sort({ body: 1 });
comments[0].on.name; // "Hoidapcode"
comments[1].on.title; // "Lập trình NodeJS" 

Populate trong Middleware

Tham khảo thêm bài viết về Middleware. Bạn có thể thực hiện thao tác populate trong pre và posts hooks. Chúng ta có một vài ví dụ :

 // Thường dùng `populate()` khi `find()` được gọi
MySchema.pre('find', function() {
  this.populate('user');
}); 
// Thường gọi `populate()` sau khi `find()` được gọi. 
// Rât hữu ích trong trường hợp bạn muốn populate sau khi tìm được documents
MySchema.post('find', async function(docs) {
  for (let doc of docs) {
    if (doc.isPublic) {
      await doc.populate('user').execPopulate();
    }
  }
});
// `populate()` sau khi save. 
// Hưu ích cho việc gửi populate về API
MySchema.post('save', function(doc, next) {
  doc.populate('user').execPopulate().then(function() {
    next();
  });
});

Trên đây là những kiến thức cơ bản về Populate trong Mongoose. Mong bài viết này có thể giúp ích cho bạn cho việc lập trình với NodeJS, cảm ơn bạn đã quan tâm bài viết này.

Hy vọng với bài viết về Ví dụ Populate Mongoose trong NodeJs đã giải đáp giúp bạn phần nào về kiến thức lập trình NodeJS. Như tôi đã nói, ngôn ngữ lập trình không quan trọng bằng tư duy giải thuật, tư duy logic để giải quyết vấn đề.

Với những năm trước đây, lập trình viên là một cái nghề khó và kén chọn người học, đồng nghĩa với việc thu nhập hàng tháng của các lập trình viên luôn cao. Còn những năm gần đây, thì lập trình là môn học phổ thông, và ai cũng nên học một ngôn ngữ lập trình nào đó.

Nếu bạn còn bất kỳ câu hỏi nào trong việc học lập trình online, hãy gửi yêu cầu cho tôi qua email hoidapcode.com@gmail.com hoặc để lại comment bên dưới, tôi sẽ giải đáp trong vòng 24 giờ!

Blog hoidapcode.com là blog được tổng hợp tự động các bài học, thông tin về lập trình trên mạng internet. Nếu bạn có ý kiến hoặc đóng góp về bài viết này, hãy liên hệ với tôi!