티스토리 뷰

728x90
반응형

몽고디비에서 필드의 존재여부에 따라 상황별 쿼리문 어떻게 만들지?

 

오랜만에 mongoose와 mongodb 옵션을 활용해보니 다시 어색하고 가물가물했다.

nosql의 단점은 sql에 비해 쿼리문이 복잡하다.

그만큼 다큐먼트에 있는 옵션들을 활용할 수록 원하는 데이터를 뽑아내기도하고 관리할 수 있다.

 

아래코드와 같이, typeorm은 분기를 하여 상황별로 쿼리문을 추가할 수 있다.

 

const query = somehow
  .createQueryBuilder('users')
  .where(`user.deletedAt IS NOT NULL`)

if (params.hasImage) {
  query.andWhere(`user.image IS NOT NULL`)
}

if (params.q != '') {
  query.andWhere(`user.name LIKE :q`, { q: params.q })
}

const users = await sql.getMany()

 

반면 몽구스는 if문으로 분기하여 유연하게 쿼리문을 짤 수 없다 ㅠㅠㅠ

그리고 몽고디비와 몽구스에서는 상황별로 다르게 나타내려면 어떻게 쿼리문 작성해야되는지 생각이 떠오르지 않았다.

그래서 구글링을 했다 (파워당당)

 

typeorm에 익숙해지면 mongoose/mongodb 가 헷갈리고

반대로 mongoose/mongodb가 익숙해지면 typeorm이 갑자기 낯설다.

솔직히 개인적인 생각이지만, sql도 까먹기 쉬운데 몽고디비는 find() update() delete() 빼면 다 포맷각이다.

필자는 기억력이 좋지 않아서 그런지... 잠시나마 기억을 하려면 블로그에 남겨서라도 해야 기억이 날까말까다.

그러니 틈틈이 기록하고 몽고디비랑 mysql 둘다 번갈아서 자주 접하도록 해야지! 😙

mongodb 다큐먼트를 참조해서 한번 쿼리문을 짜보는 문제사항과 해결과정을 소개하고자한다.


문제사항

 

주문마감일(closeDate) 필드는 Date 타입이지만 null을 허용하고, 기본값을 null 로 설계 했다.

상품 데이터가 생성 하게되면,  상품데이터의 구성하는 필드중 하나인 주문마감일 은 자동으로 null 값으로 세팅된다.

 

상품의 상세정보중 

'판매자가 올린 다른 상품들' 데이터를 구해야하는데

이 다른 상품들의 주문마감일이 다를 수 있다는 것이다.

상품들중 어떤 상품은 주문마감일이 없는 상품이 존재할 수 있으며

그 상품의 주문마감일 데이터는 null 이다.

 

반대로, 주문마감일이 존재하는 상품인 경우에는

현재시점에서는 상품의 주문마감일이 지났는지 여부도 체크해야되는 상황이다.


[ mongodb $exists ]

$exists 활용목적은  closeDate(주문마감일) 데이터가 존재여부를 체킹하기위해서 이다.

$exists의 결과값은 boolean 형태이며, 이 옵션을 쓰려면 계속 안에 $neq 옵션을 넣어야한다.

해결 이후, 이 내용을 블로그를 작성하다가... $exists를 포함시켜서 코드를 다르게 리팩토링해봤다.

 

1) $exists 옵션을 제외했을 때 

const query = this.productModel.find({
      deletedAt: null, // 삭제가 안된 상태
      user: sellerId, // 셀러 아이디
      _id: { $nin: [productId] }, // 조회아이디(productId) 외 셀러가 올린 다른 상품
      $cond: {
        if: { closeDate: !null }, // 주문마감일이 존재한다면
        then: { closeDate: { $gte: new Date() } }, // 오늘보다 미래인지 체크
        else: null,
      },
});

 

2) $exists 옵션을 넣어서 했을 때

const query = this.productModel.find({
      deletedAt: null, // 삭제가 안된 상태
      user: sellerId, // 셀러 아이디
      _id: { $nin: [productId] }, // 조회아이디(productId) 외 셀러가 올린 다른 상품
      $cond: {
        if: { $exists: { closeDate: { $neq: null } } }, // 주문마감일이 존재한다면($exists추가)
        then: { closeDate: { $gte: new Date() } }, // 오늘보다 미래인지 체크
        else: null,
      },
});

 

 

 

$exists — MongoDB Manual

Docs Home → MongoDB Manual $existsSyntax: { field: { $exists: } }When is true, $exists matches the documents that contain the field, including documents where the field value is null. If is false, the query returns only the documents that do not contain

www.mongodb.com

 

 

[ mongodb $cond ]

$cond : { if : 조건문 , then: 조건문에서 true일때 실행영역, else: 조건문에서 false일때 실행영역 } 

조건문에 맞을 때와 맞지않을 때 각각의 실행영역이 존재한다.

다큐먼트를 읽었을때 아 내가 찾는 방안이다! 라는 생각이 들었고, $cond 옵션을 활용해서 쿼리문을 작성하기로 했다.

 

$cond (aggregation) — MongoDB Manual

Docs Home → MongoDB Manual $condEvaluates a boolean expression to return one of the two specified return expressions.The $cond expression has one of two syntaxes:{ $cond: { if: , then: , else: } }Or:{ $cond: [ , , ] }$cond requires all three arguments (i

www.mongodb.com

 

 

[ mongodb $nin ]

포함하면 안되는 대상들을 나타낸다.

$nin을 사용하게된 이유는  '현재 조회하려는 상품'  외  '판매자가 올린 다른 상품들' 을 구해야되기 때문이다.

'현재 조회하려는 상품' 과 '판매자가 올린 다른 상품들'의 판매자는 모두 동일하다.

 

$nin — MongoDB Manual

Docs Home → MongoDB Manual $ninSyntax: { field: { $nin: [ , ... ] } }$nin selects the documents where:the field value is not in the specified array orthe field does not exist.If the field holds an array, then the $nin operator selects the documents whose

www.mongodb.com

 

 

[ mongodb $gt ]

 

$gt — MongoDB Manual

Docs Home → MongoDB Manual $gtSyntax: {field: {$gt: value} }$gt selects those documents where the value of the field is greater than (i.e. >) the specified value.For most data types, comparison operators only perform comparisons on fields where the BSON

www.mongodb.com

비교연산이며 '크다' 이다.

날짜 비교 할 때도 숫자비교처럼 $gt, $gte , $lte, $lt 를 쓰는게 맞나 의심을 해봤지만

비교연산으로 외 다른 방법으로 비교할 수 있는 방법이 떠오르지 않아서

비교연산으로 현재(new Date() )와 '주문마감일' 을 비교했다.

 

[ mongodb $currentDate ]

문제해결과정에서는 사용하지 않았지만

상품의 '주문마감일' 데이터가 null 이 아닐때

'주문마감일' 지났는지 안지났는지 여부를 판단하기 위해서는

현재 가 '주문마감일' 보다 미래인지 아닌지를 체킹해야한다.

현재와 '주문마감일' 비교할 때, '현재'를 어떻게 나타나야되는 방법을 생각하다가 $currentDate 를 찾아보게되었다.

시도해본 결과 쿼리문 형식오류에러가 떠서,  $currentDate를 사용하지 않았고 현재를 new Date()로 구현해서 비교를 했다.

 

추후 $currentDate 를 활용해서 문제상황이 아닌 다른 api 비즈니스 로직을 만들때 사용해볼 예정이다.

 

 

$currentDate — MongoDB Manual

Docs Home → MongoDB Manual $currentDateThe $currentDate operator sets the value of a field to the current date, either as a Date or a timestamp. The default type is Date.The $currentDate operator has the form:{ $currentDate: { : , ... } } can be either:a

www.mongodb.com

 


[내가 작성한 코드]

  async findSellerOtherProducts(
    sellerId: string,
    productId: string,
  ): Promise<ProductInfo[]> {
    const query = this.productModel.find({
      deletedAt: null, // 삭제가 안된 상태
      user: sellerId, // 셀러 아이디
      _id: { $nin: [productId] }, // 조회아이디(productId) 외 셀러가 올린 다른 상품
      $cond: {
        if: { closeDate: !null }, // 주문마감일이 존재한다면
        then: { closeDate: { $gte: new Date() } }, // 오늘보다 미래인지 체크
        else: null,
      },
    });

    const otherProducts = await query;

    return otherProducts;
  }

 

코드리뷰를 받아서 개선의 기회가 왔으면 좋겠지만

며칠 뒤의 내가 읽으면, 오늘 작성한 이 코드도 바보코드가 될 수 있으니...

한번 미래의 내가 읽어봐서 더 좋은 아이디어가 떠올라서 개선했으면 좋겠다. (미래의 나야 리팩토링을 부탁해 ^^)

728x90
반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함