통계 데이터를 받는 api 작성중 typeorm으로 쿼리문을 작성할 일이 생겼다.
평소에 typeorm으로 객체에 매핑하는것에 익숙치 않던 난 연슴겸 많이 사용하는 repository.find({}) 형식으로 코드를 작성 예정이었다.
SELECT DATE_TRUNC('DAY', POSTS.CREATED_AT) :: DATE AS DAY
, COUNT(POSTS.ID) AS COUNT_POST
FROM POSTS
INNER JOIN POST_TAG ON POSTS.ID = POST_TAG.POST_ID
INNER JOIN TAGS ON POST_TAG.TAG_ID = TAGS.ID
WHERE TAGS.TAG = '성수맛집' AND POSTS.CREATED_AT >= '2023-10-19'
AND POSTS.CREATED_AT <= '2023-10-26'
GROUP BY POSTS.CREATED_AT
기존 쿼리문인 Raw Query SQL이다.
이것을 typeORM에 맞춰 코드를 짜려니까 문제가 생겼다.
const result = await this.repository.fin({
relations: ['POST_TAG', 'TAGS'],
})
형식으로 조인해야하는데 select 부분에서 특정 조인한 테이블의 컬럼을 따로 선언할 수 없었다... 저러면 3개의 테이블의 컬럼들을 가져와야하는 매우 비효율적인 방식인것같아 찾아봤으나
방법을 찾지 못하고 결국 querybuilder를 사용해보기로 했다.
typeorm의 객체 매핑하여 짜는 코드보다 raw쿼리 방식이란 더 비슷하여 훨씬 편한듯 하다.
간단한 코드가 아니라면 querybuilder를 사용하는게 맘편할것같다.
const queryBuilder = this.postRepository.createQueryBuilder('post')
.innerJoin('post.tags', 'tag')
.select('DATE(post.created_at)', 'date')
.addSelect('COUNT(*)', 'count')
.where('tag.tag_name = :tagName', { tagName });
.andWhere('post.created_at >= :startDate', { startDate });
.andWhere('post.created_at <= :endDate', { endDate });
.groupBy('date');
조회 시작날짜, 종료날짜, 해시 태그를 각 변수에 넣어주면 된다. 개인적으로 훨씬 직관적일 뿐만 아니라 변수에 따라 조건문이나 select문이 달라질 경우
const queryBuilder = this.postRepository.createQueryBuilder('post')
.leftJoin('post.tags', 'tag')
.select('DATE(post.created_at)', 'date')
.addSelect('COUNT(*)', 'count')
.where('tag.tag_name = :tagName', { tagName });
if (startDate) {
queryBuilder.andWhere('post.created_at >= :startDate', { startDate });
}
if (endDate) {
queryBuilder.andWhere('post.created_at <= :endDate', { endDate });
}
queryBuilder
.groupBy('date')
.orderBy('date');
이런식으로 작성하던 queryBuilder에 이어서 조건을 붙여주면 된다.
앞으로 쿼리문을 작성해야할 때는 상황에 맞게 사용해야할 것 같다.
간단한 쿼리일 경우 관계형 데이터베이스의 데이터를 프로그래밍 객체에 매핑하는 ORM 코드를 사용하겠지만 개인적으로 복잡해 공부가 더 필요하다.
조금 더 복잡한 쿼리이면서 변수나 다른 상황에 따라 쿼리문이 달라지거나 하는 경우는 querybuilder를 사용하는게 좋은 것 같다. 프로시저를 사용하는 것도 좋지만 querybuilder가 훨씬 쉽다고 생각한다.