몽고디비는 관계 설정을 두 가지 방법으로 할 수 있다.
레퍼런스 방식과 임베디드 방식이 있다.
레퍼런스 방식을 사용하면 서브 객체에서 주 객체를 참조하기 때문에
주 객체의 값 하나를 바꾸더라도 모두에게 적용된다는 장점이 있다.
대신 데이터를 검색할 때 참조하는 객체를 찾아서 가져와야 하니 느리다는 단점이 있다.
임베디드 방식은 주 객체 안에 서브 객체를 담는다.
레퍼런스 방식과 달리 주 객체 안에 서브 객체의 데이터가 다 있기 때문에
별다른 검색 쿼리 필요없이 값을 금방 찾을 수 있다는 장점이 있다.
다만 데이터를 수정할 때에 주 객체 안에 있는 서브 객체의 값을 모두 수정해야 하는데
중간에 오류가 생겨 다른 게 업데이트 되지 않을 수 있다는 단점이 있다.
임베디드 방식에서 서브 객체에 필요한 값만 담는 하이브리드 방식도 있다.
예를 들어 장바구니 주 객체의 서브 객체에 물건이 있다면 가격 속성만 담을 수 있다.
1. Using References (Normalization)
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/excercise')
.then(() => console.log('몽고디비가 연결됐습니다.'))
.catch((error) => console.log('몽고디비 연결에 실패했습니다.', error));
const Product = mongoose.model('Product', new mongoose.Schema({
name: String,
price: Number
}));
const Shop = mongoose.model('Shop', new mongoose.Schema({
name: String,
product: { // Product 콜렉션의 ObjectId를 받도록 설정
type: mongoose.Schema.Types.ObjectId,
ref: 'Product'
}
}));
async function createShop(name, product) {
const course = new Course({
name,
product
});
const result = await shop.save();
console.log(result);
}
// createShop('bookstore', 'productId');
async function createProduct(name, price) {
const course = new Product({
name,
price
});
const result = await product.save();
console.log(result);
}
// createProduct('pen', 1000);
async function listShops() {
const shops = await Shop
.find()
.populate('product') // productId에 해당하는 객체를 가져온다.
// .populate('product', 'name -_id')로 하면 name 속성만 가져오고 _id 속성은 제외한다.
.select('name');
console.log(shops);
}
// listShops();
2. Using Embedded (Denormalization)
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/excercise2')
.then(() => console.log('몽고디비가 연결됐습니다.'))
.catch((error) => console.log('몽고디비 연결에 실패했습니다.', error));
const productSchema = new mongoose.Schema({
name: String,
price: Number
});
const Product = mongoose.model('Product', productSchema);
const Shop = mongoose.model('Shop', new mongoose.Schema({
name: String,
product: productSchema // schema를 직접 넣는다.
}));
// 아래처럼 validate를 설정할 수도 있다.
// product: { type: productSchema, required: true }
async function createShop(name, product) {
const shop = new Shop({
name,
product
});
const result = await shop.save();
console.log(result);
}
// 여기서 두 번째 인자에 인스턴스를 만들어 넣어준다.
// createShop('bookstore', new Product({ name: 'note', price: 2000 }));
async function updateProduct(shopId) {
const shop = await Shop.findById(shopId);
shop.product.price = 1000
shop.save(); // product에서는 save를 할 수 없다.
}
// updateProduct('shopId');
// 위처럼 찾지 않고 바로 업데이트 하는 방법
async function updateProductDirectly(shopId) {
const shop = await Shop.update({ _id: shopId }, {
$set: {
'product.price': 1000
}
});
}
// updateProductDirectly('shopId');
async function removeProduct(shopId) {
const shop = await Shop.update({ _id: shopId }, {
$unset: {
'product': '' // product 속성을 없앤다.
}
});
}
// removeProduct('shopId');
3. 배열에 넣는 방법
위 예제처럼 하나가 아닌 여러 객체를 관계적으로 묶어주려면
다음의 것들로 바꿔본다.
// 스케마 설정을 배열로
const Shop = mongoose.model('Shop', new mongoose.Schema({
name: String,
product: [productSchema] // products로 표기하는 게 맞다. 여기서는 헷갈리니 그대로 둔다.
}));
// 생성할 때 배열로 넣어준다.
createShop('bookstore', [
new Product({ name: 'note', price: 2000 }),
new Product({ name: 'magazine', price: 5000 }),
]);
// 배열에 sub 객체를 더 추가하려면
async function addProduct(shopId, product) {
const shop = await Shop.findById(shopId);
shop.product.push(product);
shop.save();
}
// addProduct('shopId', new Product({ name: 'newspaper', price: 1000 }));
// 배열에 있는 sub 객체 하나를 삭제하려면
async function removeProduct(shopId, productId) {
const shop = await Shop.findById(shopId);
const product = shop.product.id(productId);
product.remove();
shop.save();
}
// removeProduct('shopId', 'productId');
'Database > MongoDB' 카테고리의 다른 글
mongoose에서 잘못된 id가 들어왔을 때 에러 핸들링 (0) | 2020.07.06 |
---|---|
mongoose의 유용한 schema type option (0) | 2020.07.05 |
mongoose를 이용한 validate 설정 (1) | 2020.07.05 |
mongoose를 이용한 MongoDB CRUD 방법 (0) | 2020.07.04 |
맥에서 MongoDB 설치하는 방법 (0) | 2020.07.01 |