Skip to content

Commit

Permalink
♻️ refactor: service 계층에 있던 DB 접근 관련 로직을 Repository 계층으로 분리
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeVac513 committed Dec 2, 2024
1 parent fe03e1f commit 79da3a9
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 49 deletions.
58 changes: 57 additions & 1 deletion server/src/feed/feed.repository.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { DataSource, LessThan, Repository } from 'typeorm';
import { Feed } from './feed.entity';
import { Injectable } from '@nestjs/common';
import { BadRequestException, Injectable } from '@nestjs/common';
import { QueryFeedDto } from './dto/query-feed.dto';
import { SearchType } from './dto/search-feed.dto';

@Injectable()
export class FeedRepository extends Repository<Feed> {
Expand All @@ -26,4 +27,59 @@ export class FeedRepository extends Repository<Feed> {
relations: ['blog'],
});
}

async searchFeedList(
find: string,
limit: number,
type: SearchType,
offset: number,
) {
const queryBuilder = this.createQueryBuilder('feed')
.leftJoinAndSelect('feed.blog', 'rss_accept')
.addSelect(this.getMatchAgainstExpression(type, 'find'), 'relevance')
.where(this.getWhereCondition(type))
.setParameters({ find })
.orderBy('relevance', 'DESC')
.addOrderBy('feed.createdAt', 'DESC')
.skip(offset)
.take(limit);

return queryBuilder.getManyAndCount();
}

private getMatchAgainstExpression(type: string, parameter: string): string {
switch (type) {
case 'title':
return `MATCH(feed.title) AGAINST (:${parameter} IN NATURAL LANGUAGE MODE)`;
case 'blogName':
return `MATCH(rss_accept.name) AGAINST (:${parameter} IN NATURAL LANGUAGE MODE)`;
case 'all':
return `(MATCH(feed.title) AGAINST (:${parameter} IN NATURAL LANGUAGE MODE) + MATCH(rss_accept.name) AGAINST (:${parameter} IN NATURAL LANGUAGE MODE))`;
default:
throw new BadRequestException('검색 타입이 잘못되었습니다.');
}
}

private getWhereCondition(type: string): string {
switch (type) {
case 'title':
return 'MATCH(feed.title) AGAINST (:find IN NATURAL LANGUAGE MODE)';
case 'blogName':
return 'MATCH(rss_accept.name) AGAINST (:find IN NATURAL LANGUAGE MODE)';
case 'all':
return '(MATCH(feed.title) AGAINST (:find IN NATURAL LANGUAGE MODE) OR MATCH(rss_accept.name) AGAINST (:find IN NATURAL LANGUAGE MODE))';
default:
throw new BadRequestException('검색 타입이 잘못되었습니다.');
}
}

async findFeedById(feedId: number) {
return this.findOne({ where: { id: feedId } });
}

async updateFeedViewCount(feedId: number) {
await this.update(feedId, {
viewCount: () => 'view_count + 1',
});
}
}
60 changes: 12 additions & 48 deletions server/src/feed/feed.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
BadRequestException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { Injectable, NotFoundException } from '@nestjs/common';
import { FeedRepository } from './feed.repository';
import { QueryFeedDto } from './dto/query-feed.dto';
import { Feed } from './feed.entity';
Expand All @@ -19,6 +15,7 @@ import {
import { Response } from 'express';
import { cookieConfig } from '../common/cookie/cookie.config';
import { redisKeys } from '../common/redis/redis.constant';

@Injectable()
export class FeedService {
constructor(
Expand Down Expand Up @@ -96,58 +93,27 @@ export class FeedService {
const { find, page, limit, type } = searchFeedReq;
const offset = (page - 1) * limit;

const qb = this.feedRepository
.createQueryBuilder('feed')
.leftJoinAndSelect('feed.blog', 'rss_accept')
.addSelect(this.getMatchAgainstExpression(type, 'find'), 'relevance')
.where(this.getWhereCondition(type))
.setParameters({ find })
.orderBy('relevance', 'DESC')
.addOrderBy('feed.createdAt', 'DESC')
.skip(offset)
.take(limit);

const [result, totalCount] = await qb.getManyAndCount();
const [result, totalCount] = await this.feedRepository.searchFeedList(
find,
limit,
type,
offset,
);

const results = SearchFeedResult.feedsToResults(result);
const totalPages = Math.ceil(totalCount / limit);

return new SearchFeedRes(totalCount, results, totalPages, limit);
}

private getMatchAgainstExpression(type: string, parameter: string): string {
switch (type) {
case 'title':
return `MATCH(feed.title) AGAINST (:${parameter} IN NATURAL LANGUAGE MODE)`;
case 'blogName':
return `MATCH(rss_accept.name) AGAINST (:${parameter} IN NATURAL LANGUAGE MODE)`;
case 'all':
return `(MATCH(feed.title) AGAINST (:${parameter} IN NATURAL LANGUAGE MODE) + MATCH(rss_accept.name) AGAINST (:${parameter} IN NATURAL LANGUAGE MODE))`;
default:
throw new BadRequestException('검색 타입이 잘못되었습니다.');
}
}

private getWhereCondition(type: string): string {
switch (type) {
case 'title':
return 'MATCH(feed.title) AGAINST (:find IN NATURAL LANGUAGE MODE)';
case 'blogName':
return 'MATCH(rss_accept.name) AGAINST (:find IN NATURAL LANGUAGE MODE)';
case 'all':
return '(MATCH(feed.title) AGAINST (:find IN NATURAL LANGUAGE MODE) OR MATCH(rss_accept.name) AGAINST (:find IN NATURAL LANGUAGE MODE))';
default:
throw new BadRequestException('검색 타입이 잘못되었습니다.');
}
}

async updateFeedViewCount(feedId: number, ip: string, cookie, response) {
const redis = this.redisService.redisClient;
const [feed, hasCookie, hasIpFlag] = await Promise.all([
this.feedRepository.findOne({ where: { id: feedId } }),
this.feedRepository.findFeedById(feedId),
Boolean(cookie?.[`View_count_${feedId}`]),
redis.sismember(`feed:${feedId}:ip`, ip),
]);

console.log(hasIpFlag);
if (!feed) {
throw new NotFoundException(`${feedId}번 피드를 찾을 수 없습니다.`);
}
Expand All @@ -162,9 +128,7 @@ export class FeedService {

await Promise.all([
redis.sadd(`feed:${feedId}:ip`, ip),
this.feedRepository.update(feedId, {
viewCount: feed.viewCount + 1,
}),
this.feedRepository.updateFeedViewCount(feedId),
redis.zincrby(redisKeys.FEED_TREND_KEY, 1, feedId.toString()),
]);
}
Expand Down

0 comments on commit 79da3a9

Please sign in to comment.