-
Notifications
You must be signed in to change notification settings - Fork 5
Mongoose에서 참조 사용 방법(SQL의 Join처럼): Populate
MongoDB version 3.2 이상부터 join과 비슷한 $lookup 집계 연산자를 지원하고 있다. 이와 같은 기능으로 Mongoose는 populate()
를 지원한다. populate()
는 다른 collecition에 있는 documents를 참조할 수 있도록 한다.
Population은 document에서 다른 collection의 document에 대한 경로를 자동으로 대체하는 절차이다. query로 단일 document부터 multipule document, single & multiple plain object 뿐만 아니라 모든 object를 받아올 수 있다.
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);
ref
option은 population 동안에 어떤 모델을 사용할지를 나타낸다. 타입은 별 다른 이유가 없으면 ObjectId
를 사용하길 권장한다.
저장할 때는 다른 property를 저장할 때와 동일하고, 단지 _id
값만 할당해 주면 된다.
const author = new Person({
_id: new mongoose.Types.ObjectId(),
name: 'Ian Fleming',
age: 50
});
author.save(function (err) {
if (err) return handleError(err);
const story1 = new Story({
title: 'Casino Royale',
author: author._id // assign the _id from the person
});
story1.save(function (err) {
if (err) return handleError(err);
// that's it!
});
});
Story.
findOne({ title: 'Casino Royale' }).
populate('author').
exec(function (err, story) {
if (err) return handleError(err);
console.log('The author is %s', story.author.name);
// prints "The author is Ian Fleming"
});
Populate할 때 원래의 _id
를 명시해주지 않아도 mongoose가 자동으로 그 값을 document로 반환해 준다.
직접 populate할 property를 지정할 수도 있다. document는 ref
가 참조하고 있는 model의 instance이어야만 한다.
Story.findOne({ title: 'Casino Royale' }, function(error, story) {
if (error) {
return handleError(error);
}
story.author = author;
console.log(story.author.name); // prints "Ian Fleming"
});
populated()
함수를 호출해서 field가 populate됐는지 확인할 수 있다. truthy value
를 반환하면 populate된 것이다.
story.populated('author'); // truthy
story.depopulate('author'); // Make `author` not populated anymore
story.populated('author'); // undefined
Mongoose가 자동으로 _id
에 대해서는 getter를 추가하기 때문에 populate되지 않아도 _id
를 불러올 수 있다.
story.populated('author'); // truthy
story.author._id; // ObjectId
story.depopulate('author'); // Make `author` not populated anymore
story.populated('author'); // undefined
story.author instanceof ObjectId; // true
story.author._id; // ObjectId, because Mongoose adds a special getter
일치하는 document가 없다면 null
을 반환한다. 일반적인 SQL에서의 left join과 비슷하게 동작한다고 보면 된다.
await Person.deleteMany({ name: 'Ian Fleming' });
const story = await Story.findOne({ title: 'Casino Royale' }).populate('author');
story.author; // `null`
schema에서 array로 정의했다면 empty array를 반환한다.
특정 field만 받아오고 싶다면 populate()
메소드의 두번째 인자에 값을 넣어주면 된다.
Story.
findOne({ title: /casino royale/i }).
populate('author', 'name'). // only return the Persons name
exec(function (err, story) {
if (err) return handleError(err);
console.log('The author is %s', story.author.name);
// prints "The author is Ian Fleming"
console.log('The authors age is %s', story.author.age);
// prints "The authors age is null'
});
여러 참조된 document들을 불러오고 싶다면
Story.
find(...).
populate('fans').
populate('author').
exec();
같은 경로에 대해서 여러번 부르면 마지막 요청만 유효하다.
특정 옵션에 맞는 document만 불러 오려면
Story.
find().
populate({
path: 'fans',
match: { age: { $gte: 21 } },
// Explicitly exclude `_id`, see http://bit.ly/2aEfTdB
select: 'name -_id'
}).
exec();
방금의 경우에서 match
옵션으로 Story
document를 걸러낼 수는 없다. 만약 매칭되는 document가 없다면 fans
에 빈 배열이 있는 Story
document가 반환된다.
const story = await Story.
findOne({ title: 'Casino Royale' }).
populate({ path: 'author', name: { $ne: 'Ian Fleming' } }).
exec();
story.author; // `null`
만약 author name으로 필터링을 하고 싶으면 denormalization 이용해야한다.
출처
🏡Home
- Apollo References
- Schema Directives
- Apollo Client - Local State
- GraphQL Execution
- Apollo Server Execution
- Apollo Client Cache
- Apollo Client Execution
- Mongoose-Populate