-
Notifications
You must be signed in to change notification settings - Fork 6
Mongoose
MongoDB ODM 중 가장 유명한 ODM이다. document를 DB에서 조회할 때 자바스크립트 객체로 바꿔주는 역할을 한다.
npm i mongoose
- 강제 schema
- populate
- promise 사용 가능
- query builder
몽고디비에서 document에 아무 값이나 넣어도 문제가 생기지 않는데 이것이 편해보이지만 실제로 개발할 때 문제가 생기는 경우가 많다. 이러한 문제를 해결하기 위해 몽구스에서 스키마를 도입했다.
몽구스는 작성한 스키마를 기준으로 데이터를 먼저 검사한다. 스키마에 어긋나는 데이터가 있으면 에러를 발생시킨다. 스키마를 설정할 때 인덱스까지 생성할 수 있다.
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
email: { type: String, required: true, unique: true, lowercase: true },
password: { type: String, required: true, trim: true },
nickname: String, // 다른 옵션이 없다면 그냥 type을 적어줘도 된다.
birth: { type: Date, default: Date.now },
point: { type: Number, default: 0, max: 50},
image: imageSchema,
likes: [String],
any: [mongoose.Schema.Types.Mixed ],
id: mongoose.Schema.Types.ObjectId,
});
userSchema.index({ email: 1, nickname: 1 });
module.exports = mongoose.model('User', userSchema);
type을 지정한다.
필수적인 property인지를 나타낸다.
유일한 값인지를 나타낸다.
소문자만 들어간다는 것을 나타낸다.
기본값을 지정할 수 있다.
공백을 제거한다.
최소값과 최대값을 지정한다.
스키마의 한 property 값을 검증한다.
const UserSchema = new mongoose.Schema({
(...생략...)
age: {
type: Number,
validate(value) {
if (value < 0) {
throw new Error("Age must be a postive number");
}
},
},
(...생략...)
validator library도 있어서 라이브러리를 사용할 수도 있다. validator.js
스키마에 사용자 정의 메소드를 붙이면 document에서 사용자 정의 메소드를 사용할 수 있다.
userSchema.methods.comparePassword = function(pw ,cb) {
if (this.password === pw) {
cb(null, true);
} else {
cb('password 불일치');
}
};
// 나중에 user 다큐먼트를 받게 되면
user.comparePassword('비밀번호', function(err, result) {
if (err) {
throw err;
}
console.log(result);
});
스키마에 붙여 특정 동작의 이전과 이후에 취할 행동을 정의할 수 있다.
// save 이전에 할 동작
userSchema.pre('save', function(next) {
if (!this.email) { // email 필드가 없으면 에러 표시 후 저장 취소
throw '이메일이 없습니다';
}
if (!this.createdAt) { // createdAt 필드가 없으면 추가
this.createdAt = new Date();
}
next();
});
// find 이후에 할 동작
userSchema.post('find', function(result) {
console.log('저장 완료', result);
});
몽구스 document는 몽고비디의 document와 1대1 매핑이 되어서 표현된다. 각 몽구스 document는 몽구스 model의 인스턴스이다.
mongoose.Model
은 mongoose.Document
를 상속한다.
mongoose.model()
의 첫번째 인자는 해당 collection을 단수로 나타내는 문자열이다.
몽구스는 첫번째 인자로 컬렉션 이름을 만든다. User이면 소문자화를 한 뒤에 복수형으로 바꿔서 users 컬렉션이 된다. collection의 이름을 명시적으로 지정하고자 할 때는 schema 생성시 option을 지정한다.
모델로 document 인스턴스를 생성한다.
const Tank = mongoose.model('Tank', yourSchema);
const small = new Tank({ size: 'small' });
//small document
- find, findOne, findById
- save: 최종적으로 컬렉션에 저장하려면 save 메소드를 호출한다.
- create: 모델 인스턴스, 즉, document를 생성하지 않고 바로 추가한다.
- remove
스키마를 다른 스키마에 중첩할 수 있다.
const childSchema = new Schema({ name: 'string' });
const parentSchema = new Schema({
// Array of subdocuments
children: [childSchema],
// Single nested subdocuments. Caveat: single nested subdocs only work
// in mongoose >= 4.2.0
child: childSchema
});
몽고디비를 사용하다보면 하나의 document가 다른 document의 ObjectId를 쓰는 경우가 있다. 그럴 때 ObjectId를 실제 객체로 치환하는 작업이 필요하다.
만약 user 컬렉션이 다음과 같으면,
{
_id: { $oid: '574e9c0f9f663a1700fbe06e' },
name: 'zero',
friends: [{ $oid: '59a66f8372262500184b5363' }, { $oid: '574e8f46c9100617001c9cb9' }],
bestFriend: { $oid: '574e8f46c9100617001c9cb9' }
}
{
_id: { $oid: '574e8f46c9100617001c9cb9' },
name: 'hero',
friends: [{ $oid: '59a66f8372262500184b5363' }, { $oid: '574e9c0f9f663a1700fbe06e' }],
bestFriend: { $oid: '59a66f8372262500184b5363' }
}
{
_id: { $oid: '59a66f8372262500184b5363' },
name: 'nero',
friends: [{ $oid: '574e9c0f9f663a1700fbe06e' }, { $oid: '574e8f46c9100617001c9cb9' }],
bestFriend: { $oid: '574e9c0f9f663a1700fbe06e' }
}
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);
ref에 해당 ObjectId가 속해있는 모델을 넣어준다. 그리고 document를 가져올 때 어느 부분을 실제 객체로 치환해서 가져올지 정한다.
User.findOne({ name: 'zero' }).populate('bestFriend').exec((err, data) => {
console.log(data);
}); // 또는 populate({ path: 'bestFriend' })도 가능
populate가 DB 단에서 join을 해주는 것이 아니라 oid로 모두 조회를 해서 자바스크립트 단에서 합쳐주는 것이기 때문에 성능이 좋지 않다.
쉽게 쿼리를 만들어주는 역할을 한다.
- where: 어떤 필드를 조작할지 정하는 쿼리
- equals, ne
- exists
- gt, lt, gte, lte
- in, nin, all
- and, or, nor
- size: 배열의 요소가 일치하는지 확인
- mod: 나머지가 일치하는지 확인
- slice: 배열의 특정 부분만 가져온다.
- distinct: 지정한 필드 값에서 중복을 제거해서 가져온다.
- regex: 정규식에 일치하는 것을 가져온다.
- select: 특정 필드만 가져오는 기능
- skip: 앞의 몇 개를 건너뛴다.
- limit: 개수를 제한한다.
- sort