MongoDB) 서로 다른 Collection 연결하기 (mongoose)

김명성·2022년 6월 26일
0

User Collection에 Place Collection 여러개를 push 하고 싶을 때 연결하는 방법.

Place Schema

const mongoose = require('mongoose');

const Schema = mongoose.Schema;

const placeSchema = new Schema({
    // 1단계 스키마 
    title:{type:String, required:true},
    description:{type:String, required:true},
    image:{type:String, required:true},
    address:{type:String, required:true},
    location: {
      lat:{type:Number, required:true},
      lng:{type:Number, required:true}
    },
    // MongoDB에게 creator는 User Collection내에 존재하는 ID임을 알려주고(mongoose.Types.ObjectId) ref를 통해 User Schema와 연결한다.
    // collection끼리 연결해주는 mongoose의 populate method에 중요하게 사용된다.
    creator:{type: mongoose.Types.ObjectId, required:true, ref: 'User'}
})
// model은 생성자 함수를 반환하기에 대문자로 시작하고, 단수형을 사용한다.
// model의 첫번째 인수는 컬렉션의 이름이 된다.
// 2번째 인수로 작성한 스키마를 인수로 전달하여, place 생성자 함수를 통해 
// 생성된 컬렉션에 들어가는 documents들이 해당 스키마의 구조를 갖게 만든다.

module.exports = mongoose.model('Place',placeSchema)

User Schema


const mongoose = require('mongoose');
// mongoose unique validator를 통해서 unique이메일을 확인하고, 추가할 수 있다.
const uniqueValidator = require('mongoose-unique-validator')
const Schema = mongoose.Schema;

const userSchema = new Schema({
    // 1단계 스키마 
    name:{type:String, required:true},
    // unique를 통해서 쿼리 프로세스 속도를 빠르게 한다.
    email:{type:String, required:true, unique: true},
    password:{type:String, required:false, minlength: 8},
    image: {type: String, required: true},
    // User collection과 Place collection을 연결한다.
    // 한 user가 여러개의 places를 가질 수 있으므로 배열 형태로 저장.
    places: [{type: mongoose.Types.ObjectId, required:true, ref: 'Place'}]
})


//unique-validator는 unique의 존재 유무도 파악한다.
userSchema.plugin(uniqueValidator);





// model은 생성자 함수를 반환하기에 대문자로 시작하고, 단수형을 사용한다.
// model의 첫번째 인수는 컬렉션의 이름이 된다.
// model의 2번째 인수로 작성한 스키마에 객체를 인수로 전달하여, place 생성자 함수를 통해 생성된 컬렉션에 들어가는 documents들이 해당 스키마의 구조를 갖게 만든다.

module.exports = mongoose.model('User',userSchema)

User - Place 간 연결

Transaction - session의 관계 이해

  // place schema로 생성한 Place생성자 함수로 객체 인자 전달 
  const createdPlace = new Place({
    title,
    description,
    address,
    location:coordinates,
    image: 'https://lh5.googleusercontent.com/p/AF1QipMH_S511fGMCBX4Wjx5mgNamkO_oycZScZbp4Oe=w426-h240-k-no',
    creator
  })

  
  let user;

  try{
    user = await User.findById(creator);
  }catch(err){
    const error = new HttpError('Creating place failed. please try again',500)
    return next(error)
  }

  if(!user){
    const error = new HttpError('Could not find user for provided id',404);
    return next(error);
  }

  try{
    // save method는 promise를 반환하며 스키마 생성자 함수를 통해 추가한 documents를 저장하기 위해 필요한 MongoDB 코드를 처리하는 method이다.
    //await createdPlace.save();
    
    // 2개 이상의 Collection에서의 작업 수행.

    // Place collection에 creator Document를 업데이트하고,
    // User collection의 places에 Place Collection의 places가 등록되게 만들려면 save method로는 할 수 없다. 
    // 연관되지 않거나 서로 다른 여러개의 작업들을 실행할 수 있어야 한다.
    // 그리고 여러개의 작업 중 하나가 실패하면 전체 작업을 실행을 취소할 수도 있어야 한다.
    // 즉 하나라도 실패하면 바로 catch문으로 넘어가야 한다.
    // 여러개의 작업이 모두 성공할 때에만 문서를 변경하고 싶을 때에는 transactions와 session을 사용해야 한다.
    // transaction이 하는 일 : 서로 분리된 여러 작업을 수행하고, 개별적으로 취소할 수 있다.
    // transaction은 session을 기반으로 만들어진다.
    // 따라서 transaction으로 작업하려면 먼저 session을 시작하고, tarnsaction이 성공하면
    // session이 종료된 뒤 transaction이 commit 된다.
    // 쉽게 설명하자면, 여러개의 작업들을 하나 씩(transaction) 수행하고,
    // 최종적으로 완료 되었을 때에만 tarnsaction이 작업한 결과들을 session이 최종 승인(committed)한다는 것이다. 


    // 1. transactions을 수행 할 session 생성
    const session = await mongoose.startSession()
    // 2. transaction start
    session.startTransaction();
    // 3. 실제 작업 1. createdPlace 객체를 session에 저장한다.
    // transactions 전체가 성공해야하기에 먼저 sessions에 저장.
    await createdPlace.save({session: session});
    
    // 4. model 간 연결
    // 여기서 사용하는 push는 javascript의 arrayMethod가 아닌, mongoose에서 제공하는 method로서 델간의 연결 관계를 설정한다.
    // user는 User collection의 creator document다.
    user.places.push(createdPlace)
    // 5. 실제 작업 2. user에 save한다.
    await user.save({session: session})
    // 6. transactions 종료
    session.commitTransaction();
    
  }catch(err){
    const error = new HttpError('Creating place failed, please try again',500)
    return next(error);
  }



  console.log(user);
  // 생성은 201
  res.status(201).json({place: createdPlace})
}

0개의 댓글