-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: projects using native sql query sorting
- Loading branch information
Allen Zhang (张涛)
committed
Feb 5, 2024
1 parent
f3301d6
commit 7efc083
Showing
7 changed files
with
130 additions
and
116 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
139 changes: 50 additions & 89 deletions
139
packages/canyon-backend/src/project/services/get-projects.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,100 +1,61 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { PrismaService } from '../../prisma/prisma.service'; | ||
import { Project } from '../project.model'; | ||
import { percent } from '../../utils/utils'; | ||
import { getProjectByID } from 'src/adapter/gitlab.adapter'; | ||
// import { getProjectByID } from '../adapter/gitlab.adapter'; | ||
function parseGitLabUrl(gitLabUrl) { | ||
// 匹配 GitLab URL 的正则表达式 | ||
const gitLabRegex = /^(?:https?:\/\/)?([^\/]+)\/(.+)$/; | ||
|
||
// 尝试匹配正则表达式 | ||
const match = gitLabUrl.match(gitLabRegex); | ||
|
||
if (match) { | ||
// 提取匹配的组和仓库名 | ||
const groupAndRepo = match[2].split('/'); | ||
const groupName = groupAndRepo.slice(0, -1).join('/'); | ||
const repositoryName = groupAndRepo.slice(-1)[0]; | ||
return { groupName, repositoryName }; | ||
} else { | ||
// 如果没有匹配到,返回 null | ||
return { groupName: null, repositoryName: null }; | ||
} | ||
import { Prisma } from '@prisma/client'; | ||
function underscoreToCamelCase(string) { | ||
return string.replace(/_([a-z])/g, function (match, letter) { | ||
return letter.toUpperCase(); | ||
}); | ||
} | ||
function camelCaseToUnderscore(string) { | ||
return string.replace(/([A-Z])/g, '_$1').toLowerCase(); | ||
} | ||
|
||
@Injectable() | ||
export class GetProjectsService { | ||
constructor(private readonly prisma: PrismaService) {} | ||
async invoke(current, pageSize, keyword, bu) { | ||
const whereCondition: any = { | ||
OR: [ | ||
// { description: { contains: keyword } }, | ||
// { name: { contains: keyword } }, | ||
{ pathWithNamespace: { contains: keyword, mode: 'insensitive' } }, | ||
{ id: { contains: keyword, mode: 'insensitive' } }, | ||
], | ||
}; | ||
|
||
if (bu.length > 0) { | ||
whereCondition['bu'] = { in: bu }; | ||
} | ||
const total = await this.prisma.project.count({ | ||
where: whereCondition, | ||
}); | ||
|
||
const projects = await this.prisma.project.findMany({ | ||
where: whereCondition, | ||
skip: (current - 1) * pageSize, | ||
take: pageSize - 0, | ||
}); | ||
|
||
const rows = []; | ||
|
||
for (let i = 0; i < projects.length; i++) { | ||
const coverages = await this.prisma.coverage.findMany({ | ||
where: { | ||
projectID: projects[i].id, | ||
covType: 'all', | ||
}, | ||
}); | ||
async invoke(current, pageSize, keyword, _bu, field, order) { | ||
const bu = | ||
_bu.length > 0 | ||
? _bu | ||
: await this.prisma.project | ||
.groupBy({ | ||
by: ['bu'], | ||
_count: { | ||
bu: true, | ||
}, | ||
}) | ||
.then((r) => r.map((item) => item.bu)); | ||
|
||
const limit = pageSize; // pageSize控制每页的记录数 | ||
const offset = (current - 1) * pageSize; // 计算要跳过的记录数 | ||
|
||
const total: any = await this.prisma.$queryRaw` | ||
SELECT COUNT(DISTINCT p.id) AS total | ||
FROM project_20240130 AS p | ||
LEFT JOIN coverage_20240130 AS c ON p.id = c.project_id | ||
LEFT JOIN summary_20240130 AS s ON c.sha = s.sha and c.report_id=s.report_id | ||
WHERE s.metric_type='statements' AND s.cov_type='all' AND bu IN (${Prisma.join(bu)}) AND (p.path_with_namespace ILIKE ${'%' + keyword + '%'} OR p.id ILIKE ${'%' + keyword + '%'}) | ||
GROUP BY p.id | ||
`.then((r: any) => r.length); | ||
|
||
const rows: any[] = await this.prisma.$queryRaw` | ||
SELECT p.id AS id, p.path_with_namespace,p.description,p.name, CAST(COUNT(DISTINCT c.sha) AS INT) AS report_times,p.bu, ROUND(MAX(100*covered::decimal / NULLIF(total, 0)),2) AS max_coverage, MAX(s.created_at) AS last_report_time | ||
FROM project_20240130 AS p | ||
LEFT JOIN coverage_20240130 AS c ON p.id = c.project_id | ||
LEFT JOIN summary_20240130 AS s ON c.sha = s.sha and c.report_id=s.report_id | ||
WHERE s.metric_type='statements' AND s.cov_type='all' AND bu IN (${Prisma.join(bu)}) AND (p.path_with_namespace ILIKE ${'%' + keyword + '%'} OR p.id ILIKE ${'%' + keyword + '%'}) | ||
GROUP BY p.id | ||
ORDER BY ${Prisma.sql([camelCaseToUnderscore(field) || 'last_report_time'])} ${Prisma.sql([{ ascend: 'ASC', descend: 'DESC' }[order] || 'DESC'])} | ||
LIMIT ${limit} OFFSET ${offset}; | ||
`; | ||
|
||
const summarys = await this.prisma.summary.findMany({ | ||
where: { | ||
// projectID: projects[i].id, | ||
covType: 'all', | ||
metricType: 'statements', | ||
sha: { | ||
in: [...new Set(coverages.map((item) => item.sha))], | ||
}, | ||
}, | ||
orderBy: { | ||
createdAt: 'desc', | ||
}, | ||
}); | ||
if (summarys.length > 0) { | ||
rows.push({ | ||
...projects[i], | ||
lastReportTime: summarys[0]?.createdAt, | ||
reportTimes: summarys.length, | ||
maxCoverage: Math.max( | ||
...summarys.map((item) => | ||
item.total === 0 ? 0 : percent(item.covered, item.total), | ||
), | ||
), | ||
}); | ||
} else { | ||
rows.push({ | ||
...projects[i], | ||
lastReportTime: new Date(), | ||
reportTimes: 0, | ||
maxCoverage: 0, | ||
}); | ||
} | ||
} | ||
return { | ||
data: rows, | ||
total, | ||
data: rows.map((item: any) => { | ||
return Object.keys(item).reduce((acc, key) => { | ||
acc[underscoreToCamelCase(key)] = item[key]; | ||
return acc; | ||
}, {}); | ||
}), | ||
total: total, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { ArgsType, Field, ID, InputType, Int } from '@nestjs/graphql'; | ||
|
||
@ArgsType() | ||
@InputType() | ||
export class PaginationArgs { | ||
@Field(() => Int, { | ||
description: '当前页码', | ||
}) | ||
current: number; | ||
|
||
@Field(() => Int, { | ||
description: '每页数量', | ||
}) | ||
pageSize: number; | ||
} | ||
|
||
@ArgsType() | ||
@InputType() | ||
export class SorterArgs { | ||
@Field({ | ||
description: '排序字段名称', | ||
}) | ||
field: string; | ||
|
||
@Field({ | ||
description: '升序或降序', | ||
}) | ||
order: string; | ||
} |
4 changes: 2 additions & 2 deletions
4
packages/canyon-platform/src/helpers/backend/gql/queries/GetProjects.graphql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters