mirror of
https://github.com/ershisan99/flashcards-api.git
synced 2025-12-17 12:33:22 +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 { Pagination } from '../../infrastructure/common/pagination/pagination.service'
|
||||||
import { SaveGradeDto } from '../auth/dto'
|
import { SaveGradeDto } from '../auth/dto'
|
||||||
|
import { GetMinMaxCardsDto } from '../auth/dto/min-max-cards.dto'
|
||||||
import { JwtAuthGuard } from '../auth/guards'
|
import { JwtAuthGuard } from '../auth/guards'
|
||||||
import { CreateCardDto, GetAllCardsInDeckDto } from '../cards/dto'
|
import { CreateCardDto, GetAllCardsInDeckDto } from '../cards/dto'
|
||||||
import { Card, CardWithGrade, PaginatedCardsWithGrade } from '../cards/entities/cards.entity'
|
import { Card, CardWithGrade, PaginatedCardsWithGrade } from '../cards/entities/cards.entity'
|
||||||
@@ -110,8 +111,13 @@ export class DecksController {
|
|||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Version('2')
|
@Version('2')
|
||||||
@Get('min-max-cards')
|
@Get('min-max-cards')
|
||||||
findMinMaxCards(): Promise<MinMaxCards> {
|
findMinMaxCards(@Query() query: GetMinMaxCardsDto, @Req() req): Promise<MinMaxCards> {
|
||||||
return this.commandBus.execute(new GetMinMaxCardsUseCaseCommand())
|
return this.commandBus.execute(
|
||||||
|
new GetMinMaxCardsUseCaseCommand({
|
||||||
|
...query,
|
||||||
|
userId: req.user.id,
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiConsumes('multipart/form-data')
|
@ApiConsumes('multipart/form-data')
|
||||||
|
|||||||
@@ -57,9 +57,61 @@ export class DecksRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async findMinMaxCards(): Promise<{ min: number; max: number }> {
|
async findMinMaxCards({
|
||||||
const result = await this.prisma
|
name,
|
||||||
.$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;`
|
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 {
|
return {
|
||||||
max: Number(result[0].maxCardsCount),
|
max: Number(result[0].maxCardsCount),
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export class GetAllDecksV1Handler implements ICommandHandler<GetAllDecksV1Comman
|
|||||||
|
|
||||||
async execute(command: GetAllDecksV1Command): Promise<PaginatedDecksWithMaxCardsCount> {
|
async execute(command: GetAllDecksV1Command): Promise<PaginatedDecksWithMaxCardsCount> {
|
||||||
const decks = await this.deckRepository.findAllDecks(command.params)
|
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 }
|
return { ...decks, maxCardsCount: minMax.max }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||||
|
|
||||||
|
import { ResendVerificationEmailDto } from '../../auth/dto'
|
||||||
import { MinMaxCards } from '../entities/min-max-cards.entity'
|
import { MinMaxCards } from '../entities/min-max-cards.entity'
|
||||||
import { DecksRepository } from '../infrastructure/decks.repository'
|
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||||
|
|
||||||
export class GetMinMaxCardsUseCaseCommand {
|
export class GetMinMaxCardsUseCaseCommand {
|
||||||
constructor() {}
|
constructor(public readonly args: ResendVerificationEmailDto) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@CommandHandler(GetMinMaxCardsUseCaseCommand)
|
@CommandHandler(GetMinMaxCardsUseCaseCommand)
|
||||||
export class GetMinMaxCardsUseCaseHandler implements ICommandHandler<GetMinMaxCardsUseCaseCommand> {
|
export class GetMinMaxCardsUseCaseHandler implements ICommandHandler<GetMinMaxCardsUseCaseCommand> {
|
||||||
constructor(private readonly deckRepository: DecksRepository) {}
|
constructor(private readonly deckRepository: DecksRepository) {}
|
||||||
|
|
||||||
async execute(): Promise<MinMaxCards> {
|
async execute(command: GetMinMaxCardsUseCaseCommand): Promise<MinMaxCards> {
|
||||||
return await this.deckRepository.findMinMaxCards()
|
return await this.deckRepository.findMinMaxCards(command.args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user