Mongoose: Virtuals with Typegoose

ToastEggsToast·2021년 2월 7일
0

TIL

목록 보기
9/15
post-thumbnail

Virtuals

Virtuals are document properties that you can get and set but that do not get persisted to MongoDB. The getters are useful for formatting or combining fields, while setters are useful for de-composing a single value into multiple values for storage.

MongoDB에 직접 저장되지는 않지만, 가상의 필드로 존재할 수 있게 도와주는 mongoose api 중 하나이다.
virtuals를 사용하게 되면 localField, foreignField, ref를 통해 자동으로 값을 채워주기 때문에
필드 명과 대상 모델을 잘 지정해주면 더 편리하고, 다른 테이블에서 필요 정보를 찾을 불필요한 통신량을 줄일 수 있다.

Mongoose with Typegoose

Typegoose는 Mongoose model을 정의할 때 좀 더 쉽게 스키마 정의, 모델 정의를 할 수 있도록 도와준다.
타입스크립트, class 정의 등의 형태를 사용해 mongoose 단독으로 코드를 작성 할 때 보다 코드량을 줄일 수 있다.

class User {
  @prop({ required: true, trim: true, minlength: 2, maxlength: 10 })
  public name!: string;
}

const UserModel = getModelForClass(User);

와 같은 식으로 스키마 정의, 모델 정의를 해줄 수 있다.

Use Virtuals!

typegoose를 이용해 mongoose의 virtuals를 사용하는 것은 매-우 매우매우 간단하다.

class User {
  
  @prop({ required: true, trim: true, minlength: 2, maxlength: 10 })
  public name!: string;
  
  @prop({
    ref: 'UserBalance',
    foreignField: 'owner',
    localField: '_id',
  })
  public balance!: Ref<UserBalance>;
  
}

const UserModel = getModelForClass(User);

이렇게 하면 가상의 필드를 만들어 모델을 생성하는데 성공했다!!
하지만.. 이렇게 하고 api를 호출해주면.. balance값에 null 값이 돌아오고 만다.

그리고.. 이게 바로 내가 글을 작성하게 된 계기이기도 하지.. 후후...

Get value of Virtual Fields

addFields 사용하기

UserModelTC.addFields({
    subitems: {
        type: UserBalanceModelTC.NonNull.List,
        resolver: (item) => UserBalanceModel.find({ owner: item._id )}),
        description: 'Sub items with a custom type',
    }
});

addFields는 말 그대로 필드를 하나 새롭게 생성해주는 것을 의미한다.
virtual Field는 가상으로 존재하기 때문에 디비에 존재하지 않고, mongooses는 이런 가상의 필드에 대해 자동으로 필드를 생성해주지 않는다.
따라서 필드를 임의로 하나 추가해주고, resolver를 통해 필요한 값을 모델에서 찾아올 수 있도록 지정해준다.
결과값은 Array(값이 하나가 아니라 여러개가 될 수 있기 때문) 형태로 받을 수 있다.

처음 virtual Field에서 값을 받으려고 해도 아무리 해도 안 되길래 위의 방법을 사용했었다.

참고: graphql-compose-mongoose Issues #135

addRelation 사용하기

  UserModelTC.addRelation('balance', {
    prepareArgs: {
      filter: (source: DocumentType<User>) => ({
        owner: source._id,
      }),
    },
    projection: { balance: 1 },
    resolver: () => UserBalanceModelTC.mongooseResolvers.findOne()
  });

addRelation은 모델 간 관계를 생성해주는 방법이다.
prepareArgs에 특히 집중해야하는데, 나는 유저 필드에서 _id를 찾아, UserBalanceModel의 owner필드의 값과 일치하는 값을 찾으려 하기 때문에 위와 같이 코드를 작성해줘야한다.

Result

addFields를 통해 값을 얻으면

{
  [
    {
    "balance": {
      "id": "601a119...",
      "owner": "601a119...",
      "point": 0,
      "__typename": "UserBalance"
    },
  }
}

이렇게 Array의 형태로 값을 얻을 수 있고,

addRelation을 사용하면

{
  "balance": {
    "id": "601a119...",
    "owner": "601a119...",
    "point": 0,
    "__typename": "UserBalance"
  },
}

이렇게 Object형태의 예쁜 값을 얻을 수 있다^^*

profile
개발하는 반숙계란 / 하고싶은 공부를 합니다. 목적은 흥미입니다.

0개의 댓글