본문 바로가기

Database/MongoDB

mongoose collection의 관계 설정

몽고디비는 관계 설정을 두 가지 방법으로 할 수 있다.

 

레퍼런스 방식과 임베디드 방식이 있다.

레퍼런스 방식을 사용하면 서브 객체에서 주 객체를 참조하기 때문에

주 객체의 값 하나를 바꾸더라도 모두에게 적용된다는 장점이 있다.

대신 데이터를 검색할 때 참조하는 객체를 찾아서 가져와야 하니 느리다는 단점이 있다.

 

임베디드 방식은 주 객체 안에 서브 객체를 담는다.

레퍼런스 방식과 달리 주 객체 안에 서브 객체의 데이터가 다 있기 때문에

별다른 검색 쿼리 필요없이 값을 금방 찾을 수 있다는 장점이 있다.

다만 데이터를 수정할 때에 주 객체 안에 있는 서브 객체의 값을 모두 수정해야 하는데

중간에 오류가 생겨 다른 게 업데이트 되지 않을 수 있다는 단점이 있다.

 

임베디드 방식에서 서브 객체에 필요한 값만 담는 하이브리드 방식도 있다.

예를 들어 장바구니 주 객체의 서브 객체에 물건이 있다면 가격 속성만 담을 수 있다.

 

 

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');