Skip to content

Commit

Permalink
Merge pull request #683 from bounswe/feature/639/BE/recommendation-en…
Browse files Browse the repository at this point in the history
…dpoints

Feature/639/be/recommendation endpoints
  • Loading branch information
omersafakbebek authored Dec 12, 2023
2 parents bafd6ae + 2c143f9 commit 5b05624
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 0 deletions.
9 changes: 9 additions & 0 deletions ludos/backend/src/controllers/game.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,4 +263,13 @@ export class GameController {
);
return HttpStatus.OK;
}


@ApiOperation({ summary: 'Get Related Games Endpoint' })
@ApiNotFoundResponse({ description: 'Game is not found!' })
@Get(':gameId/related')
public async getRelatedGames(@Param('gameId') gameId: string) {
const relatedGames = await this.gameService.getRelatedGames(gameId);
return relatedGames;
}
}
10 changes: 10 additions & 0 deletions ludos/backend/src/controllers/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,14 @@ export class UserController {
public async getUserById(@Param('userId') userId: string) {
return await this.userService.getUserInfo(userId);
}

@ApiBearerAuth()
@UseGuards(AuthGuard)
@ApiOperation({ summary: 'Get Suggested Games For User Endpoint' })
@ApiNotFoundResponse({ description: 'User is not found!' })
@Get('/suggested')
public async getSuggestedGames(@Req() req: AuthorizedRequest) {
const suggestedGames = await this.userService.getSuggestedGames(req.user.id);
return suggestedGames;
}
}
18 changes: 18 additions & 0 deletions ludos/backend/src/repositories/game.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,22 @@ export class GameRepository extends Repository<Game> {
public getAllRelationsAsList() {
return this.metadata.relations.map((relation) => relation.propertyName);
}


public async getRelatedGames(gameId: string, tags: string[]): Promise<Game[]> {

const tagArray = tags.map(tag => `'${tag}'`).join(',');
const query = `
SELECT *,
(SELECT COUNT(*) FROM UNNEST(games.tags) tag WHERE tag = ANY(ARRAY[${tagArray}])) AS match_count
FROM games
WHERE games.id::text != $1
ORDER BY match_count DESC
LIMIT 10
`;

const relatedGames = await this.query(query, [gameId]);

return relatedGames;
}
}
33 changes: 33 additions & 0 deletions ludos/backend/src/repositories/user.repository.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Injectable } from '@nestjs/common';
import { User } from '../entities/user.entity';
import { Game } from '../entities/game.entity';
import { DataSource, Repository } from 'typeorm';
import { IPaginationMeta, Pagination, paginate } from 'nestjs-typeorm-paginate';

Expand Down Expand Up @@ -69,4 +70,36 @@ export class UserRepository extends Repository<User> {
});
return paginationResult;
}

public async getSuggestedGames (userId: string): Promise<Game[]> {

const query = `
WITH user_followed_games AS (
SELECT
ARRAY_AGG(DISTINCT gameId::text) AS gameIds,
ARRAY_AGG(DISTINCT tag) AS tags
FROM
(
SELECT guf."gamesId" AS gameId, unnest(g.tags) AS tag
FROM game_user_follows guf
JOIN games g ON guf."gamesId" = g.id
WHERE guf."usersId" = $1
) subquery
)
SELECT *,
(SELECT COUNT(*)
FROM unnest(g.tags) AS tag
WHERE tag = ANY((SELECT unnest(tags) FROM user_followed_games))) AS match_count
FROM games g
WHERE
g.id::text NOT IN (SELECT unnest(gameIds) FROM user_followed_games)
ORDER BY match_count DESC
LIMIT 10
`;

const suggestedGames = await this.query(query, [userId]);

return suggestedGames;
}

}
25 changes: 25 additions & 0 deletions ludos/backend/src/services/game.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { GameRepository } from '../repositories/game.repository';
import { RatingRepository } from '../repositories/rating.repository';
import { UserRepository } from '../repositories/user.repository';
import { CompletionDurationRepository } from '../repositories/completion-duration.repository';
import { GameGetResponseDto } from '../dtos/game/response/get.response';

@Injectable()
export class GameService {
Expand Down Expand Up @@ -190,4 +191,28 @@ export class GameService {
throw new NotFoundException('Completion Duration Not Found!');
}
}

public async getRelatedGames(gameId: string): Promise<GameGetResponseDto[]> {

const game = await this.gameRepository.findGameById(gameId);
if (!game) {
throw new NotFoundException('Game not found');
}

const relatedGames = await this.gameRepository.getRelatedGames(gameId, game.tags);

const relatedGamesResponse: GameGetResponseDto[] = relatedGames.map((relatedGame : Game) => ({
id: relatedGame.id,
title: relatedGame.title,
coverLink : relatedGame.coverLink,
gameBio : relatedGame.gameBio,
releaseDate : relatedGame.releaseDate,
developer : relatedGame.developer,
userCompletionDuration : relatedGame.userCompletionDuration,
averageCompletionDuration : relatedGame.averageCompletionDuration
}));

return relatedGamesResponse;
}

}
21 changes: 21 additions & 0 deletions ludos/backend/src/services/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import { Payload } from '../interfaces/user/payload.interface';
import { ResetPasswordRepository } from '../repositories/reset-password.repository';
import { UserRepository } from '../repositories/user.repository';
import { GetUserInfoResponseDto } from '../dtos/user/response/get-user-info-response.dto';
import { GameGetResponseDto } from '../dtos/game/response/get.response';
import { Game } from '../entities/game.entity';

@Injectable()
export class UserService {
Expand Down Expand Up @@ -208,4 +210,23 @@ export class UserService {
delete response.password;
return response;
}


public async getSuggestedGames(userId: string): Promise<GameGetResponseDto[]> {

const suggestedGames = await this.userRepository.getSuggestedGames(userId);

const suggestedGamesResponse: GameGetResponseDto[] = suggestedGames.map((relatedGame : Game) => ({
id: relatedGame.id,
title: relatedGame.title,
coverLink : relatedGame.coverLink,
gameBio : relatedGame.gameBio,
releaseDate : relatedGame.releaseDate,
developer : relatedGame.developer,
userCompletionDuration : relatedGame.userCompletionDuration,
averageCompletionDuration : relatedGame.averageCompletionDuration
}));

return suggestedGamesResponse;
}
}

0 comments on commit 5b05624

Please sign in to comment.