Skip to content

Commit

Permalink
Merge pull request #523 from Scents-Note/dev
Browse files Browse the repository at this point in the history
dev -> main
  • Loading branch information
neverlish authored Jun 27, 2023
2 parents 498409c + b35c604 commit 5848d9c
Show file tree
Hide file tree
Showing 18 changed files with 883 additions and 923 deletions.
746 changes: 538 additions & 208 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"scripts": {
"_prestart": "npm install",
"start": "cross-env-shell NODE_ENV=development REDIS_DB_ID=2 \"npm run build && node ./dist/bin/www\"",
"dev": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/index.ts",
"dev": "nodemon --watch 'src/**/*.ts' -r tsconfig-paths/register --exec 'ts-node' src/app.ts",
"test": "cross-env NODE_ENV=test REDIS_DB_ID=3 mocha --recursive --exit -t 5000 ./tests/test_unit/**/*.spec.ts ./tests/test_unit/**/*.spec.js -r ts-node/register -r tsconfig-paths/register",
"test-unit": "cross-env NODE_ENV=test REDIS_DB_ID=3 mocha --recursive --exit -t 5000 ./tests/test_unit/**/*.spec.ts ./tests/test_unit/**/*.spec.js -r ts-node/register -r tsconfig-paths/register ",
"test-integral": "cross-env NODE_ENV=production REDIS_DB_ID=1 mocha --recursive --exit -t 5000 ./tests/test_integral/**/*.spec.ts -r ts-node/register -r tsconfig-paths/register --parallel",
Expand All @@ -24,6 +24,7 @@
"private": true,
"dependencies": {
"@babel/plugin-proposal-decorators": "^7.22.3",
"@opensearch-project/opensearch": "^1.2.0",
"@types/swagger-ui-express": "^4.1.3",
"@types/winston": "^2.4.4",
"aws-sdk": "^2.1101.0",
Expand Down Expand Up @@ -74,6 +75,7 @@
"@types/morgan": "^1.9.3",
"@types/node": "^17.0.45",
"@types/node-cron": "^3.0.2",
"@types/sinon": "^10.0.15",
"@types/supertest": "^2.0.12",
"@types/swagger-jsdoc": "^6.0.1",
"@types/validator": "^13.7.17",
Expand All @@ -84,6 +86,7 @@
"mocha": "^10.2.0",
"nodemon": "^2.0.22",
"sequelize-cli": "^6.4.1",
"sinon": "^15.2.0",
"supertest": "^6.2.2",
"swagger-jsdoc": "^6.1.0",
"swagger-ui-express": "^4.3.0",
Expand Down
57 changes: 23 additions & 34 deletions src/controllers/Perfume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,9 @@ import { ResponseDTO, SimpleResponseDTO } from '@response/index';
import {
PerfumeIntegralDTO,
ListAndCountDTO,
PerfumeSearchResultDTO,
PerfumeThumbDTO,
PerfumeThumbWithReviewDTO,
PerfumeThumbKeywordDTO,
PerfumeSearchDTO,
PagingDTO,
} from '@dto/index';
import { GenderMap } from '@src/utils/enumType';
Expand Down Expand Up @@ -194,41 +192,32 @@ const getPerfume: RequestHandler = (
* description: Token is missing or invalid
* x-swagger-router-controller: Perfume
* */
const searchPerfume: RequestHandler = (
const searchPerfume: RequestHandler = async (
req: Request | any,
res: Response,
next: NextFunction
): any => {
const loginUserIdx: number = req.middlewareToken.loginUserIdx || -1;
const perfumeSearchRequest: PerfumeSearchRequest =
PerfumeSearchRequest.createByJson(req.body);
const pagingRequestDTO: PagingRequestDTO = PagingRequestDTO.createByJson(
req.query
);
logger.debug(
`${LOG_TAG} likePerfume(userIdx = ${loginUserIdx}, query = ${JSON.stringify(
req.query
)}, body = ${JSON.stringify(req.body)})`
);
const perfumeSearchDTO: PerfumeSearchDTO =
perfumeSearchRequest.toPerfumeSearchDTO(loginUserIdx);
Perfume.searchPerfume(perfumeSearchDTO, pagingRequestDTO.toPageDTO())
.then((result: ListAndCountDTO<PerfumeSearchResultDTO>) => {
return result.convertType(PerfumeResponse.createByJson);
})
.then((response: ListAndCountDTO<PerfumeResponse>) => {
LoggerHelper.logTruncated(
logger.debug,
`${LOG_TAG} searchPerfume's result = ${response}`
);
res.status(StatusCode.OK).json(
new ResponseDTO<ListAndCountDTO<PerfumeResponse>>(
MSG_GET_SEARCH_PERFUME_SUCCESS,
response
)
);
})
.catch((err: Error) => next(err));
) => {
const loginUserIdx = req.middlewareToken.loginUserIdx;
const perfumeSearchDTO = PerfumeSearchRequest.createByJson(
req.body
).toPerfumeSearchDTO(loginUserIdx);
const pagingRequestDTO = PagingRequestDTO.createByJson(req.query);

try {
const result = await Perfume.searchPerfume(
perfumeSearchDTO,
pagingRequestDTO.toPageDTO()
);
const response = result.convertType(PerfumeResponse.createByJson);
res.status(StatusCode.OK).json(
new ResponseDTO<ListAndCountDTO<PerfumeResponse>>(
MSG_GET_SEARCH_PERFUME_SUCCESS,
response
)
);
} catch (err: Error | any) {
next(err);
}
};

/**
Expand Down
12 changes: 6 additions & 6 deletions src/controllers/definitions/request/perfume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ import { PerfumeSearchDTO } from '@src/data/dto';
* type: integer
* */
class PerfumeSearchRequest {
readonly keywordList: number[];
readonly brandList: number[];
readonly ingredientList: number[];
readonly keywordList?: number[];
readonly brandList?: number[];
readonly ingredientList?: number[];
readonly searchText: string;
constructor(
keywordList: number[],
brandList: number[],
ingredientList: number[],
keywordList: number[] | undefined,
brandList: number[] | undefined,
ingredientList: number[] | undefined,
searchText: string
) {
this.keywordList = keywordList;
Expand Down
141 changes: 0 additions & 141 deletions src/dao/PerfumeDao.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
PagingDTO,
PerfumeDTO,
PerfumeInquireHistoryDTO,
PerfumeSearchResultDTO,
PerfumeThumbDTO,
} from '@dto/index';

Expand Down Expand Up @@ -38,36 +37,6 @@ const PERFUME_THUMB_COLUMNS: string[] = [

import redis from '@utils/db/redis';

const SQL_SEARCH_PERFUME_SELECT: string =
'SELECT ' +
'p.perfume_idx AS perfumeIdx, p.brand_idx AS brandIdx, p.name, p.english_name AS englishName, p.image_url AS imageUrl, p.created_at AS createdAt, p.updated_at AS updatedAt, ' +
'b.brand_idx AS "Brand.brandIdx", ' +
'b.name AS "Brand.name", ' +
'b.english_name AS "Brand.englishName", ' +
'b.first_initial AS "Brand.firstInitial", ' +
'b.image_url AS "Brand.imageUrl", ' +
'b.description AS "Brand.description", ' +
'b.created_at AS "Brand.createdAt", ' +
'b.updated_at AS "Brand.updatedAt" ' +
'FROM perfumes p ' +
'INNER JOIN brands b ON p.brand_idx = b.brand_idx ' +
'WHERE p.deleted_at IS NULL AND (:whereCondition) ' +
'ORDER BY :orderCondition ' +
'LIMIT :limit ' +
'OFFSET :offset';
const SQL_SEARCH_BRAND_CONDITION: string = ' p.brand_idx IN (:brands)';
const SQL_SEARCH_KEYWORD_CONDITION: string =
'(SELECT COUNT(DISTINCT(jpk.keyword_idx)) FROM join_perfume_keywords jpk WHERE jpk.perfume_idx = p.perfume_idx AND jpk.keyword_idx IN (:keywords) GROUP BY jpk.perfume_idx) = (:keywordCount) ';
const SQL_SEARCH_INGREDIENT_CONDITION: string =
'(SELECT COUNT(DISTINCT(i.category_idx)) FROM notes n INNER JOIN ingredients i ON i.ingredient_idx = n.ingredient_idx WHERE n.perfume_idx = p.perfume_idx AND n.ingredient_idx IN (:ingredients) GROUP BY n.perfume_idx) = (:categoryCount) ';

const SQL_SEARCH_PERFUME_SELECT_COUNT: string =
'SELECT ' +
'COUNT(p.perfume_idx) as count ' +
'FROM perfumes p ' +
'INNER JOIN brands b ON p.brand_idx = b.brand_idx ' +
'WHERE p.deleted_at IS NULL AND (:whereCondition) ';

const SQL_SEARCH_RANDOM_PERFUME_WITH_MIN_REVIEW_COUNT: string =
'SELECT ' +
'`Perfume`.perfume_idx AS perfumeIdx, `Perfume`.brand_idx AS brandIdx, `Perfume`.name, `Perfume`.english_name AS englishName, `Perfume`.image_url AS imageUrl, `Perfume`.created_at AS createdAt, `Perfume`.updated_at AS updatedAt, ' +
Expand Down Expand Up @@ -97,116 +66,6 @@ const defaultOption: { [key: string]: any } = {
};

class PerfumeDao {
/**
* 향수 검색
*
* @param {number[]} brandIdxList
* @param {number[]} ingredientIdxList
* @param {number[]} categoryIdxList
* @param {number[]} keywordIdxList
* @param {string} searchText
* @param {PagingDTO} pagingDTO
* @returns {Promise<Perfume[]>} perfumeList
*/
async search(
brandIdxList: number[],
ingredientIdxList: number[],
categoryIdxList: number[],
keywordIdxList: number[],
searchText: string,
pagingDTO: PagingDTO
): Promise<ListAndCountDTO<PerfumeSearchResultDTO>> {
logger.debug(
`${LOG_TAG} search(brandIdxList = ${brandIdxList}, ` +
`ingredientIdxList = ${ingredientIdxList}, ` +
`keywordList = ${keywordIdxList}, ` +
`searchText = ${searchText}, ` +
`pagingDTO = ${pagingDTO}`
);
let orderCondition = pagingDTO.sqlOrderQuery('p.perfume_idx ASC');

let whereCondition: string = '';
if (
ingredientIdxList.length +
keywordIdxList.length +
brandIdxList.length >
0
) {
const arr: number[][] = [
ingredientIdxList,
keywordIdxList,
brandIdxList,
];
const conditionSQL: string[] = [
SQL_SEARCH_INGREDIENT_CONDITION,
SQL_SEARCH_KEYWORD_CONDITION,
SQL_SEARCH_BRAND_CONDITION,
];
whereCondition = arr
.reduce((prev: string[], cur: number[], index: number) => {
if (cur.length > 0) {
prev.push(conditionSQL[index]);
}
return prev;
}, [])
.join(' AND ');
}
if (searchText && searchText.length > 0) {
if (whereCondition.length) {
whereCondition = `${whereCondition} AND `;
}
whereCondition = `${whereCondition} (MATCH(p.name, p.english_name) AGAINST('${searchText}*' IN BOOLEAN MODE)`;
if (brandIdxList.length == 0) {
whereCondition = `${whereCondition} OR (MATCH(b.name, b.english_name) AGAINST ('${searchText}*' IN BOOLEAN MODE))`;
}
whereCondition = `${whereCondition} )`;
orderCondition =
`case when MATCH(p.name, p.english_name) AGAINST('${searchText}') then 0 ` +
`when MATCH(p.name, p.english_name) AGAINST('${searchText}*' IN BOOLEAN MODE) then 1 ` +
`else 2 end, ${orderCondition}`;
}
const countSQL: string = SQL_SEARCH_PERFUME_SELECT_COUNT.replace(
':whereCondition',
whereCondition.length > 0 ? whereCondition : 'TRUE'
);
const selectSQL: string = SQL_SEARCH_PERFUME_SELECT.replace(
':whereCondition',
whereCondition.length > 0 ? whereCondition : 'TRUE'
).replace(':orderCondition', orderCondition);

if (ingredientIdxList.length == 0) ingredientIdxList.push(-1);
if (brandIdxList.length == 0) brandIdxList.push(-1);
if (keywordIdxList.length == 0) keywordIdxList.push(-1);
const [{ count }] = await sequelize.query<{ count: number }>(countSQL, {
replacements: {
keywords: keywordIdxList,
brands: brandIdxList,
ingredients: ingredientIdxList,
categoryCount: categoryIdxList.length,
keywordCount: keywordIdxList.length,
},
type: QueryTypes.SELECT,
raw: true,
});
const rows: PerfumeSearchResultDTO[] = (
await sequelize.query(selectSQL, {
replacements: {
keywords: keywordIdxList,
brands: brandIdxList,
ingredients: ingredientIdxList,
categoryCount: categoryIdxList.length,
keywordCount: keywordIdxList.length,
limit: pagingDTO.limit,
offset: pagingDTO.offset,
},
type: QueryTypes.SELECT,
raw: true,
nest: true,
})
).map(PerfumeSearchResultDTO.createByJson);
return new ListAndCountDTO(count, rows);
}

/**
* 향수 조회
*
Expand Down
12 changes: 6 additions & 6 deletions src/data/dto/PerfumeSearchDTO.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
class PerfumeSearchDTO {
readonly keywordIdxList: number[];
readonly brandIdxList: number[];
readonly ingredientCategoryList: number[];
readonly keywordIdxList?: number[];
readonly brandIdxList?: number[];
readonly ingredientCategoryList?: number[];
readonly searchText: string;
readonly userIdx: number;
constructor(
keywordIdxList: number[],
brandIdxList: number[],
ingredientCategoryList: number[],
keywordIdxList: number[] | undefined,
brandIdxList: number[] | undefined,
ingredientCategoryList: number[] | undefined,
searchText: string,
userIdx: number
) {
Expand Down
56 changes: 0 additions & 56 deletions src/data/dto/PerfumeSearchResultDTO.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/data/dto/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export * from './PerfumeDTO';
export * from './PerfumeIntegralDTO';
export * from './PerfumeSearchDTO';
export * from './PerfumeInquireHistoryDTO';
export * from './PerfumeSearchResultDTO';
export * from './PerfumeSummaryDTO';
export * from './PerfumeThumbDTO';
export * from './PerfumeThumbWithReviewDTO';
Expand Down
4 changes: 2 additions & 2 deletions src/models/tables/Perfume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export class Perfume extends Model {
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
})
Notes: Note;
Notes: Note[];

@BelongsToMany(() => User, {
foreignKey: 'userIdx',
Expand Down Expand Up @@ -139,5 +139,5 @@ export class Perfume extends Model {
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
})
perfumeKeywords: JoinPerfumeKeyword[];
JoinPerfumeKeywords: JoinPerfumeKeyword[];
}
Loading

0 comments on commit 5848d9c

Please sign in to comment.