[MongoDB] mongoose

Younghwan Cha·2023년 3월 11일
0

Database

목록 보기
7/16
post-thumbnail

Schema

mongoDB 에는 schema 가 존재하지 않기 때문에 mongoose 를 사용해서 schema 를 생성해준다.

export const CatSchema = new mongoose.Schema({
  name: { type: String, required: true },
  age: { type: Number, required: true },
  breed: { type: String },
});

foreign key 를 구현하는 방법에는 2가지가 있다.

ref

이 경우는 보통 _id 를 참조할 때 사용한다

const SettlementSchema = new mongoose.Schema({
  // ...
  testId: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Test',
    default: null // 기본적으로 null 값을 할당하여 선택적임을 나타냄
  }
});

virtual

1번 처럼 _id 를 참조하는 것이 아닌 일반 컬럼을 참조하고 싶을 때 사용한다

ReservationSchema.virtual('place', {
  ref: 'testModel',
  localField: 'id',
  foreignField: 'testId',
  justOne: true,
});

populate

두 경우 모두 populate 함수를 통해서 참조해서 사용하면 된다.
[mongoose populate] https://mongoosejs.com/docs/tutorials/virtuals.html#populate

const result = await Test.findOne().populate('place');

1 번의 경우 값이 DB 에 쌓이지만, 2번의 경우 값이 쌓이지 않는다.
그렇기 때문에 2번이 데이터가 적게 쌓여 저장 측면에서 이점이 있지만, 아무래도 관계로 정의되어 있기 때문에 1번이 더 직관적인면이 있다.

ObjectId 가 중첩되어 있을 경우 populate 를 populate 할 수 있다.

const user = await User.findById(id).populate({
  path: "videos",
  populate: {
    path: "owner",
    model: "User",
  },
});

이 경우 vides 객체에 존재하는 ObjectId 값을 실제 User 값으로 가져온다.


Interface

ts 를 사용할 경우 interface 를 통해서 document 의 타입을 정의해주어야한다

[mongoose typescript] https://mongoosejs.com/docs/typescript.html


mongoose 의 모든 것은 Schema 로부터 시작한다. 각 schema 는 MongoDB collection 과 mapping 되고
해당 collection 내에서 Model( Document) 의 shape 을 정의한다.


Models

ModelSchema 정의로부터 compile 된 생성자이다.
이 Model 의 instance 가 Document 이다.

const schema = new mongoose.Schema({ name: 'string', size: 'string' });
const Tank = mongoose.model('Tank', schema);

mongoose.model() 의 첫번째 인자는 model 이 포함된 collection 의 단일인자 이름이다.
Mongoose 는 자동으로 이를 lowercase 복수형으로 변환한다.
따라서, 상단의 Tank model 은 db dp tanks collection 으로 저장된다.

[mongoose model] https://mongoosejs.com/docs/models.html


Documents

mongoose documents 는 MongoDB 에 저장된 documents 와 1대1 mapping 된다.
위에서 언급한 것과 같이 각 document 는 Model 의 instance 이다.

  • 참고로, find 와 같은 연산에서 반환되는 값은 Document Object 이기 떄문에 일반 js Object 와 동일하게 취급해서는 안된다. 동일하게 취급하려면, toObject() 함수를 통해서 일반 object 로 변환해야한다.

exec()

find, findOne 등의 메서드 뒤에 exec()을 붙이든 안 붙이든 기능은 동일하다.

대신 exec()을 사용하면 유사 프로미스가 아닌 온전한 프로미스를 반환값으로 얻을 수 있으며, 에러가 났을 때 stack trace에 오류가 발생한 코드의 위치가 포함되기 때문에 공식 문서에서도 exec()을 사용할 것을 권장하고 있다.

[exec() 의 역할] https://tesseractjh.tistory.com/166


[ts in mongoose] https://blog.devgenius.io/typescript-in-mongoose-9994fca6987b
[nestjs MongoDB] https://docs.nestjs.com/techniques/mongodb

_doc

model.findOne({}) 으로 결과값을 반환 받았다고 해보자.
우리가 보는 결과는 result._doc 의 값이다.
실제로 console.log({ ...result }) 를 통해서 반환하는 결과값은 다음과 같다

{
  "$__": {
      ...
  },
  "$isNew": false,
  "_doc": {
      ...
  },
}

따라서, 해당 객체에 새로운 키를 추가하고 싶을 경우에는
result.new = 'new' 가 아닌 result._doc.new = 'new' 를 사용해야 한다.

delete vs remove

detele

Creates a findOneAndDelete query: atomically finds the given document and deletes it, 
and returns the document as it was before deletion.

remove

Creates a findOneAndRemove query: atomically finds the given document and deletes it.

delete 와 remove 는 같은 기능을 하지만 delete 의 경우 삭제 이전 상태를 반환해준다.

bulk insert

  async updateDatabase(serverIamportDataList: any) {
    const bulkOps = [];
    const failedIamportDataList = [];
  
    for (const iamportData of serverIamportDataList) {
      if (!iamportData) {
        failedIamportDataList.push(iamportData);
      } else {
        bulkOps.push({
          updateOne: {
            filter: { imp_uid: iamportData.imp_uid },
            update: { $set: iamportData },
            upsert: true
          }
        });
      }
    }
  
    if (bulkOps.length > 0) {
      const result = await this.paymentModel.bulkWrite(bulkOps);
      console.log('Bulk operation result:', result);
    }
  
    return failedIamportDataList;
  }
Bulk operation result: BulkWriteResult {
  insertedCount: 0,
  matchedCount: 40,
  modifiedCount: 0,
  deletedCount: 0,
  upsertedCount: 0,
  upsertedIds: {},
  insertedIds: {}
}

// insertedCount: 이 필드는 새로 삽입된 문서의 수를 나타냅니다. 여기서는 새로운 문서가 삽입되지 않았기 때문에 0입니다.

// matchedCount: 이 필드는 쿼리와 일치하는 문서의 수를 나타냅니다. 여기서는 40개의 문서가 쿼리와 일치하였습니다. updateOne 또는 updateMany 작업에서 일치하는 문서의 수를 나타냅니다.

// modifiedCount: 이 필드는 실제로 수정된 문서의 수를 나타냅니다. 여기서는 1개의 문서가 실제로 수정되었습니다. 문서가 이미 요청된 내용과 동일한 경우에는 수정되지 않았다고 간주됩니다.

// deletedCount: 삭제된 문서의 수를 나타냅니다. 이 경우 삭제된 문서가 없으므로 0입니다.

// upsertedCount: 이 필드는 upsert 작업을 통해 새로 삽입된 문서의 수를 나타냅니다. upsert는 해당 문서가 존재하지 않을 때 새로 삽입하는 작업입니다. 여기서는 새로운 문서가 삽입되지 않았기 때문에 0입니다.

// upsertedIds: upsert 작업으로 삽입된 각 문서의 ID를 나타냅니다. 이 경우 새로 삽입된 문서가 없으므로 비어 있습니다.

// insertedIds: 새로 삽입된 각 문서의 ID를 나타냅니다. 이 경우에도 새로 삽입된 문서가 없으므로 비어 있습니다.
profile
개발 기록

0개의 댓글