Asked Jul 31st, 2018 7:31 AM 2382 0 0
  • 2382 0 0
0

Giúp mình về lookup và populate trong Mongodb với

Share
  • 2382 0 0

Chào mọi người. Mình có 1 app đang làm sử dụng mongodb. Do mới tiếp xúc nên có vấn đề mình gặp phải cần mọi người trợ giúp.

Mình có 3 model liên quan cụ thể các field phía dưới. Từ Model Review, mình muốn lấy toàn bộ reviews của Book. router api dạng: domain/book_id/reviews. Mỗi review sẽ có comments nên get luôn các comment của review tương ứng( và info tác giả của comment đó). Trong từng comment có trường user_id. Ở đây mình dùng các table riêng biệt.

Lúc trước minh dùng foEarch lấy ra từng ID rồi truy vấn để lấy thông tin. Nhưng thấy nó Load mất thời gian. Sau đó thì tìm hiểu đc cái lookup và populate này.

Mình dùng câu truy vấn như này ở Model review.

async testLookup(book_id, limit, page){
return await Review.aggregate([
		{ $match: { "book_id": new mongoose.Types.ObjectId(book_id)} },
        { $lookup: {
            from: 'comments',
            localField: '_id',
            foreignField: 'review_id',
            as: 'comments'
        }},
   ]);
} 

Kết quả sẽ ra được như thế này. Mình muốn là ở cái user_id sẽ hiển thị Object thông tin User đó luôn. Kiểu như bình thường sử dụng populate thì sẽ là Comment.user_id ấy.

( "user_id": {
               "_id": "5b4f090bbcb0521b24759a9c",
               "email": "[email protected]",
               "is_admin": false,
               "username": "hhhhh"
           }
)

Thông tin các Model

Review

var mongoose = require('mongoose'), Schema = mongoose.Schema
, ObjectId = Schema.ObjectId;
var ReviewSchema = new mongoose.Schema({
  user_id: { type: ObjectId, ref: 'User' },
  book_id: { type: ObjectId },
  star: { type: Number, default: 0 },
  content: String
},
{
    timestamps: true   
}
);

Comment

var mongoose = require('mongoose'),
 Schema = mongoose.Schema,
 ObjectId = Schema.ObjectId;
var CommentSchema = new mongoose.Schema({
  user_id: { type: ObjectId, ref: 'User' },
  review_id: { type: ObjectId, ref: 'Review' },
  content: String
});
Truong Dang @xdangminhtruongx
Jul 31st, 2018 7:41 AM

Theo ý kiến của mình, thì mình nghĩ bạn nên set quan hệ embedded giữa Comment vào trong Review như thế này sẽ ổn hơn nè...

var mongoose = require('mongoose'), Schema = mongoose.Schema

, ObjectId = Schema.ObjectId;

var ReviewSchema = new mongoose.Schema({

user_id: { type: ObjectId, ref: 'User' },

book_id: { type: ObjectId },

star: { type: Number, default: 0 },

content: String ,
comments : [
		{
			user_id : {  type: ObjectId, ref: 'User'}, // Hoặc set luôn thông tin người comment vào đây cũng được
                       // user_name : String, 
			content : String,
                       createdOn: { type: Date, 'default': Date.now }
		}
	],
});
+1
| Reply
Share
Huu Hung @huuhung96
Jul 31st, 2018 7:55 AM

@xdangminhtruongx Sau 1 hồi lâu search thì mình cũng thấy 1 số người họ tạo quan hệ kiểu này. Nhưng mình thấy nó kiểu ngược ngược so với thói quen dùng quan hệ trong rails và laravel trước đây . Mình cũng biết là mongo lưu được cả colection. Nhưng mình vẫn muốn lưu ở dạng ObjectID của nó chứ không set cứng để khi người dùng thay đổi thông tin thì nó cũng đổi theo.

0
| Reply
Share
Truong Dang @xdangminhtruongx
Jul 31st, 2018 7:58 AM

@huuhung96 Thường thì họ toàn set quan hệ emmbeded như thế này, tại Mongodb họ khuyến khích làm vậy mà. Mình cũng thường làm như vậy, mình có thiết kế một cái Database cho trang web bán hàng bằng Mongodb này, hy vọng có ích với bạn : https://github.com/dangminhtruong/havana/tree/master/model

+1
| Reply
Share
Huu Hung @huuhung96
Jul 31st, 2018 12:21 PM

@xdangminhtruongx Nếu giờ mình điều chỉnh Model giốg bạn ở trên (Chỗ comment lưu ObjectId ) thì 1 query có thể lấy đc hết thông tin ra k bạn nhỉ? Dạng như này

review : {
content: "...",
star: ".."
user : {
username: "..",
email: ".."
},// Tác giả review
comments :[{
content: "...",
user : {
username: "..",
email: ".."
},// Tác giả comment
{...},
}]
}

Nếu dùng lookup thì lấy đc 3 cái là review, user( tác giả review), và comment. Tác giả comment mình thấy chưa lấy đc bạn à.

Lý do mình rất cần như này vì chỗ thông tin comment m không chỉ cần username mà còn 1 số trường khác nữa. Nếu set vào hết như này thì thông tin nó k thống nhất. Do mình sử dụng api này cho angular. Còn có router get comments theo ID review nữa. (Cái này thì đơn giản vì từ comment model dùng populate là là đc user info luôn. )

Còn cái review này nếu không có cách khác thì thôi đành set cứng theo kiểu shop của bạn. Cũng giúp truy vấn nhanh hơn.

0
| Reply
Share
Truong Dang @xdangminhtruongx
Jul 31st, 2018 1:48 PM

@huuhung96 Mình nghĩ khi query Schema Review thì populate kiểu này chắc chắn là được:

.populate('book') // Trường hợp bạn muốn lấy luôn cả thông tin chi tiết sách
.populate({
	path: 'comments.user_id',
	select: 'name email'
})

Cái MongoDB này Lookup với aggregate của nó hại não lắm đó. Mình nghĩ MongoDB chỉ phù hợp với những mô hình cơ sở dữ liệu đọc nhiều ghi ít, logic trên database không qúa phức tạp. Nếu không thì rõ là mệt... 😂 😂 😂

0
| Reply
Share
Huu Hung @huuhung96
Jul 31st, 2018 1:57 PM

@xdangminhtruongx Mừng quá luôn. Mình cũng vừa mò vào Model bill.js và router admin.js trong repo của bạn thì tìm ra được cái này. Hóa ra populate dạng cấp con như này vẫn được ý detais.product_id. 😄 Thanks bạn nhiều nhé đã hỗ trợ . Tiếc qua trong phần question này không vote được gì cả 😄 Giờ lại đi sửa lại action post review để push array comment vào nữa là xem như ổn. 😄

0
| Reply
Share
Truong Dang @xdangminhtruongx
Jul 31st, 2018 2:00 PM

@huuhung96 Thật tốt khi giúp được bạn, cám ơn bạn nhé. Hình như bạn vừa upvote mấy bài viết cho mình lận ^^

0
| Reply
Share
Huu Hung @huuhung96
Jul 31st, 2018 2:15 PM

@xdangminhtruongx Do mình đang làm cái app dạng project book review system như bên framgia nhưng dùng nodejs và angular 6 phía front end. Mà 2 thằng này thì nên đi kèm với mongodb cho nó hợp về tốc độ 😄. Cũng như làm quen chút về NoSQL. Cứ bắt đầu từ mấy câu truy vấn cơ bản trước, document của nó cũng chẳng dễ thương chút nào nên đụng đâu phải search với hỏi chỗ đó 😄 Cho mình hỏi ké thêm 1 chút mình thấy bạn có sử dụng socket Io. Mình cũng đang tính làm cái notification. Khi có người comment thì sẽ có thông báo reatime. Mình nghĩ ra cơ chế nó như này. Không biết có ổn không.

Khi user truy cập vào app. Sẽ tự động tạo riêng có nó 1 cái room dạng tên là room-[user_id] -> [user_id này là ObjectID của user trong database của web]. Bản thân nó cũng đã có riêng 1 cái socket id khi vào nhưng do mình không thể xác định đc nên tạo tên room custom sẽ dễ quản lý, kiểm soát. Khi sự kiện comment (hay sự kiễn bất kỳ tác đến đến user này) diễn ra. Mình cũng sẽ get được user_id của người được comment đó. Sau đó sẽ emit về cho cái room-[user_id] đó message. Về phía database, trong table lưu message sẽ có 1 trường seen kiểu boolean. Khi người dùng click thông báo nào thì sẽ gửi https request lên update seen thành true cho cái message đó.

0
| Reply
Share
Truong Dang @xdangminhtruongx
Jul 31st, 2018 2:29 PM

@huuhung96 Ý tưởng của bạn cũng ổn đó emit event theo ObjectId thì chắc chắn là unique rồi... Nếu bạn là để học thì làm đại đi, sai thì lại ngồi sửa cuối cùng sẽ tìm được cái tốt nhất cho mà coi. Làm cho công ty mới lo chớ, làm để nâng cao kỹ năng thì hem sợ lắm... 😂 😂😂

0
| Reply
Share