하나의 다큐먼트가 다른 다큐먼트의 ObjectId를 쓰는 경우가 있다. 그럴 때 그 ObjectId를 실제 객체로 치환하는 작업이 필요하다. 그 때 유용한게 mongoose의 populate기능이다.
아래 예시를 보자
1. 먼저 populate를 적용하기 위해서는 해당 ObjectId가 속해있는 모델을 넣어준다. 현재는 자기 자신(User)을 가리키지만 다른 컬렉션에도 적용이 가능하다.
//userSchema
const userSchema = new mongoose.Schema({
name: String,
friends: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}],
bestFriend: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}],
});
mongoose.model('User', userSchema);
이제 findOne을 통해 객체를 불러왔다고 하자
//db.users.findOne({ name: 'zero' }) 를 통해 불러온 객체
{
_id: { $oid: '574e9c0f9f663a1700fbe06e' },
name: 'zero',
friends: [{ $oid: '59a66f8372262500184b5363' }, { $oid: '574e8f46c9100617001c9cb9' }],
bestFriend: { $oid: '574e8f46c9100617001c9cb9' }
}
User.findOne({ name: 'zero' }).populate('bestFriend').exec((err, data) => {
console.log(data);
}); // 또는 populate({ path: 'bestFriend' })도 가능
그럼 data의 구조는 아래와 같다.
{
_id: { $oid: '574e9c0f9f663a1700fbe06e' },
name: 'zero',
friends: [{ $oid: '59a66f8372262500184b5363' }, { $oid: '574e8f46c9100617001c9cb9' }],
bestFriend: {
_id: { $oid: '574e8f46c9100617001c9cb9' },
name: 'hero',
friends: [{ $oid: '59a66f8372262500184b5363' }, { $oid: '574e9c0f9f663a1700fbe06e' }],
bestFriend: { $oid: '59a66f8372262500184b5363' }
}
}
bestFriend에 ObjectId가 해당하는 객체로 치환된 모습을 볼 수 있다.
populate할 때 bestFriend의 name과 bestFriend만 보고 싶다면 두 번째 인자로 넣어주면 된다.
User.findOne({ name: 'zero' }).populate('bestFriend', 'name bestFriend').exec((err, data) => {
console.log(data);
});
한번에 여러 objectId를 치환하기 위해 populate를 연달아 사용할 수도 있다.
User.findOne({ name: 'zero' }).populate('bestFriend').populate('friends').exec((err, data) => {
console.log(data);
});
하지만 populate는 $oid로 모두 조회를 해서 자바스크립트 단에서 합쳐주는 것이지 JOIN처럼 DB 자체에서 합쳐주는 것이 아니기 때문에 남용하면 성능 문제가 생길 확률이 크다.