mirror of
https://github.com/ershisan99/flashcards-api.git
synced 2025-12-16 20:59:26 +00:00
feat: filter min max cards by authorId, name, favoritedBy
This commit is contained in:
29
src/modules/auth/dto/min-max-cards.dto.ts
Normal file
29
src/modules/auth/dto/min-max-cards.dto.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { ApiHideProperty } from '@nestjs/swagger'
|
||||
|
||||
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
|
||||
import { IsOptionalOrEmptyString } from '../../../infrastructure/decorators'
|
||||
import { IsUUIDOrCaller } from '../../../infrastructure/decorators/is-uuid-or-caller'
|
||||
|
||||
@ApiSchema({ name: 'MinMaxCardsRequestArgs' })
|
||||
export class GetMinMaxCardsDto {
|
||||
/** Search by deck name */
|
||||
@IsOptionalOrEmptyString()
|
||||
name?: string
|
||||
|
||||
/** Filter by deck authorId
|
||||
* If ~caller is passed, it will be replaced with the current user's id
|
||||
*/
|
||||
@IsOptionalOrEmptyString()
|
||||
@IsUUIDOrCaller()
|
||||
authorId?: string
|
||||
|
||||
/** Decks favorited by user
|
||||
* If ~caller is passed, it will be replaced with the current user's id
|
||||
* */
|
||||
@IsOptionalOrEmptyString()
|
||||
@IsUUIDOrCaller()
|
||||
favoritedBy?: string
|
||||
|
||||
@ApiHideProperty()
|
||||
userId?: string
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
|
||||
import { Pagination } from '../../infrastructure/common/pagination/pagination.service'
|
||||
import { SaveGradeDto } from '../auth/dto'
|
||||
import { GetMinMaxCardsDto } from '../auth/dto/min-max-cards.dto'
|
||||
import { JwtAuthGuard } from '../auth/guards'
|
||||
import { CreateCardDto, GetAllCardsInDeckDto } from '../cards/dto'
|
||||
import { Card, CardWithGrade, PaginatedCardsWithGrade } from '../cards/entities/cards.entity'
|
||||
@@ -110,8 +111,13 @@ export class DecksController {
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Version('2')
|
||||
@Get('min-max-cards')
|
||||
findMinMaxCards(): Promise<MinMaxCards> {
|
||||
return this.commandBus.execute(new GetMinMaxCardsUseCaseCommand())
|
||||
findMinMaxCards(@Query() query: GetMinMaxCardsDto, @Req() req): Promise<MinMaxCards> {
|
||||
return this.commandBus.execute(
|
||||
new GetMinMaxCardsUseCaseCommand({
|
||||
...query,
|
||||
userId: req.user.id,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
@ApiConsumes('multipart/form-data')
|
||||
|
||||
@@ -57,9 +57,61 @@ export class DecksRepository {
|
||||
}
|
||||
}
|
||||
|
||||
async findMinMaxCards(): Promise<{ min: number; max: number }> {
|
||||
const result = await this.prisma
|
||||
.$queryRaw`SELECT MAX(card_count) as "maxCardsCount", MIN(card_count) as "minCardsCount" FROM (SELECT deck.id, COUNT(card.id) as card_count FROM flashcards.deck LEFT JOIN "flashcards"."card" ON deck.id = card."deckId" GROUP BY deck.id) AS card_counts;`
|
||||
async findMinMaxCards({
|
||||
name,
|
||||
authorId,
|
||||
favoritedBy,
|
||||
userId,
|
||||
}: {
|
||||
name?: string
|
||||
authorId?: string
|
||||
favoritedBy?: string
|
||||
userId?: string
|
||||
}): Promise<{ min: number; max: number }> {
|
||||
const conditions = []
|
||||
|
||||
if (name) conditions.push(`deck.name ILIKE ('%' || ? || '%')`)
|
||||
if (authorId) conditions.push(`deck."userId" = ?`)
|
||||
if (userId)
|
||||
conditions.push(
|
||||
`(deck."isPrivate" = FALSE OR (deck."isPrivate" = TRUE AND deck."userId" = ?))`
|
||||
)
|
||||
if (favoritedBy) conditions.push(`fd."userId" = ?`)
|
||||
const deckQueryParams = [
|
||||
userId,
|
||||
...(name ? [name] : []),
|
||||
...(authorId ? [authorId] : []),
|
||||
...(userId ? [userId] : []),
|
||||
...(favoritedBy ? [favoritedBy] : []),
|
||||
]
|
||||
|
||||
console.log(conditions, deckQueryParams)
|
||||
const query = `
|
||||
SELECT MAX(card_count) as "maxCardsCount",
|
||||
MIN(card_count) as "minCardsCount" FROM (SELECT deck.id, COUNT(card.id) as card_count
|
||||
FROM flashcards.deck
|
||||
LEFT JOIN flashcards."favoriteDeck" AS fd ON deck."id" = fd."deckId" AND fd."userId" = $1
|
||||
LEFT JOIN
|
||||
"flashcards"."card" ON deck.id = card."deckId"
|
||||
${
|
||||
conditions.length
|
||||
? `WHERE ${conditions
|
||||
.map(
|
||||
(condition, index) =>
|
||||
`${condition.replace(
|
||||
'?',
|
||||
`$${index + 2}`
|
||||
)}`
|
||||
)
|
||||
.join(' AND ')}`
|
||||
: ''
|
||||
}
|
||||
GROUP BY deck.id
|
||||
) AS card_counts;
|
||||
`
|
||||
|
||||
console.log(query)
|
||||
const result = await this.prisma.$queryRawUnsafe(query, ...deckQueryParams)
|
||||
|
||||
return {
|
||||
max: Number(result[0].maxCardsCount),
|
||||
|
||||
@@ -14,7 +14,7 @@ export class GetAllDecksV1Handler implements ICommandHandler<GetAllDecksV1Comman
|
||||
|
||||
async execute(command: GetAllDecksV1Command): Promise<PaginatedDecksWithMaxCardsCount> {
|
||||
const decks = await this.deckRepository.findAllDecks(command.params)
|
||||
const minMax = await this.deckRepository.findMinMaxCards()
|
||||
const minMax = await this.deckRepository.findMinMaxCards({})
|
||||
|
||||
return { ...decks, maxCardsCount: minMax.max }
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
|
||||
import { ResendVerificationEmailDto } from '../../auth/dto'
|
||||
import { MinMaxCards } from '../entities/min-max-cards.entity'
|
||||
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||
|
||||
export class GetMinMaxCardsUseCaseCommand {
|
||||
constructor() {}
|
||||
constructor(public readonly args: ResendVerificationEmailDto) {}
|
||||
}
|
||||
|
||||
@CommandHandler(GetMinMaxCardsUseCaseCommand)
|
||||
export class GetMinMaxCardsUseCaseHandler implements ICommandHandler<GetMinMaxCardsUseCaseCommand> {
|
||||
constructor(private readonly deckRepository: DecksRepository) {}
|
||||
|
||||
async execute(): Promise<MinMaxCards> {
|
||||
return await this.deckRepository.findMinMaxCards()
|
||||
async execute(command: GetMinMaxCardsUseCaseCommand): Promise<MinMaxCards> {
|
||||
return await this.deckRepository.findMinMaxCards(command.args)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user