feat: improved queries, removed unnecessary imports

This commit is contained in:
Hackntosh 2024-10-25 00:11:08 +01:00
parent e1b244e92b
commit 8e7be25088
9 changed files with 123 additions and 110 deletions

View file

@ -65,6 +65,7 @@ This back-end uses the following stack:
- **NestJS** - **NestJS**
- **Fastify** - **Fastify**
- **Prisma** - **Prisma**
- **Kysely**
- **MinIO** - **MinIO**
- **PostgreSQL** - **PostgreSQL**
- **Redis** - **Redis**

View file

@ -5,18 +5,15 @@ import {
NotFoundException, NotFoundException,
UnauthorizedException, UnauthorizedException,
} from "@nestjs/common"; } from "@nestjs/common";
import { PrismaService } from "src/services/prisma/prisma.service";
import { S3Service } from "src/services/s3/s3.service"; import { S3Service } from "src/services/s3/s3.service";
import { CommentsRepository } from "./repository/comments.repository"; import { CommentsRepository } from "./repository/comments.repository";
import { KweeksRepository } from "./repository/kweeks.repository"; import { KweeksRepository } from "./repository/kweeks.repository";
import { selectCommentsWithReplies } from "./schemas/prisma_queries.schema";
@Injectable() @Injectable()
export class CommentsService { export class CommentsService {
constructor( constructor(
private readonly commentsRepository: CommentsRepository, private readonly commentsRepository: CommentsRepository,
private readonly kweeksRepository: KweeksRepository, private readonly kweeksRepository: KweeksRepository,
private readonly prisma: PrismaService,
private readonly s3: S3Service, private readonly s3: S3Service,
) {} ) {}
@ -33,12 +30,15 @@ export class CommentsService {
} }
// Verifies if the kweek_id is a kweek or a comment // Verifies if the kweek_id is a kweek or a comment
const parentComment = await this.commentsRepository.findOne(kweek_id); const parentComment = await this.commentsRepository.findOne(
kweek_id,
false,
);
let kweek = undefined; let kweek = undefined;
if (parentComment === undefined) { if (parentComment === undefined) {
kweek = await this.kweeksRepository.findOne(kweek_id); kweek = await this.kweeksRepository.findOne(kweek_id, false);
if (kweek === undefined) { if (kweek === undefined) {
throw new NotFoundException("Kweek/Comment not found"); throw new NotFoundException("Kweek/Comment not found");
@ -60,30 +60,21 @@ export class CommentsService {
await this.commentsRepository.addAttachments(id, attachments); await this.commentsRepository.addAttachments(id, attachments);
return await this.commentsRepository.findOne(id); return await this.commentsRepository.findOne(id, false);
} }
async info(comment_id: string) { async info(comment_id: string) {
const comment = await this.commentsRepository.findOne(comment_id); const comment = await this.commentsRepository.findOne(comment_id, false);
if (comment === undefined) { if (comment === undefined) {
throw new NotFoundException("Comment not found"); throw new NotFoundException("Comment not found");
} }
const likes = await this.commentsRepository.countLikes(comment.id); return comment;
const comments = await this.commentsRepository.countComments(comment.id);
return {
...comment,
count: {
likes,
comments,
},
};
} }
async update(comment_id: string, user_id: string, content: string) { async update(comment_id: string, user_id: string, content: string) {
const comment = await this.commentsRepository.findOne(comment_id); const comment = await this.commentsRepository.findOne(comment_id, true);
if (comment === undefined) { if (comment === undefined) {
throw new NotFoundException("Comment not found"); throw new NotFoundException("Comment not found");
@ -100,7 +91,7 @@ export class CommentsService {
} }
async delete(comment_id: string, user_id: string) { async delete(comment_id: string, user_id: string) {
const comment = await this.commentsRepository.findOne(comment_id); const comment = await this.commentsRepository.findOne(comment_id, true);
if (comment === undefined) { if (comment === undefined) {
throw new NotFoundException("Comment not found"); throw new NotFoundException("Comment not found");
@ -118,7 +109,7 @@ export class CommentsService {
} }
async like(comment_id: string, user_id: string) { async like(comment_id: string, user_id: string) {
const comment = await this.commentsRepository.findOne(comment_id); const comment = await this.commentsRepository.findOne(comment_id, true);
if (comment === undefined) { if (comment === undefined) {
throw new NotFoundException("Comment not found"); throw new NotFoundException("Comment not found");

View file

@ -1,5 +1,4 @@
import { Module } from "@nestjs/common"; import { Module } from "@nestjs/common";
import { PrismaService } from "src/services/prisma/prisma.service";
import { S3Service } from "src/services/s3/s3.service"; import { S3Service } from "src/services/s3/s3.service";
import { UsersRepository } from "src/users/repository/users.repository"; import { UsersRepository } from "src/users/repository/users.repository";
import { CommentsController } from "./comments.controller"; import { CommentsController } from "./comments.controller";
@ -12,7 +11,6 @@ import { KweeksRepository } from "./repository/kweeks.repository";
@Module({ @Module({
controllers: [KweeksController, CommentsController], controllers: [KweeksController, CommentsController],
providers: [ providers: [
PrismaService,
KweeksService, KweeksService,
S3Service, S3Service,
CommentsService, CommentsService,

View file

@ -31,30 +31,21 @@ export class KweeksService {
await this.kweekRepository.addAttachments(id, attachments); await this.kweekRepository.addAttachments(id, attachments);
return await this.kweekRepository.findOne(id); return await this.kweekRepository.findOne(id, false);
} }
async findOne(id: string) { async findOne(id: string) {
const post = await this.kweekRepository.findOne(id); const post = await this.kweekRepository.findOne(id, false);
if (post === undefined) { if (post === undefined) {
throw new NotFoundException("Post not found"); throw new NotFoundException("Post not found");
} }
const likes = await this.kweekRepository.countLikes(post.id); return post;
const comments = await this.kweekRepository.countComments(post.id);
return {
...post,
count: {
likes,
comments,
},
};
} }
async update(user_id: string, post_id: string, content: string) { async update(user_id: string, post_id: string, content: string) {
const post = await this.kweekRepository.findOne(post_id); const post = await this.kweekRepository.findOne(post_id, true);
if (post === undefined) { if (post === undefined) {
throw new NotFoundException("Post not found"); throw new NotFoundException("Post not found");
@ -71,7 +62,7 @@ export class KweeksService {
} }
async remove(user_id: string, id: string) { async remove(user_id: string, id: string) {
const post = await this.kweekRepository.findOne(id); const post = await this.kweekRepository.findOne(id, true);
if (post === undefined) { if (post === undefined) {
throw new NotFoundException("Post not found"); throw new NotFoundException("Post not found");
@ -89,7 +80,7 @@ export class KweeksService {
} }
async like(user_id: string, kweek_id: string) { async like(user_id: string, kweek_id: string) {
const kweek = await this.kweekRepository.findOne(kweek_id); const kweek = await this.kweekRepository.findOne(kweek_id, true);
if (kweek === undefined) { if (kweek === undefined) {
throw new NotFoundException("Post not found"); throw new NotFoundException("Post not found");

View file

@ -1,4 +1,9 @@
import { Injectable } from "@nestjs/common"; import { Injectable } from "@nestjs/common";
import {
jsonArrayFrom,
jsonBuildObject,
jsonObjectFrom,
} from "kysely/helpers/postgres";
import { Database } from "src/services/kysely/kysely.service"; import { Database } from "src/services/kysely/kysely.service";
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
@ -38,19 +43,57 @@ export class CommentsRepository {
.executeTakeFirst(); .executeTakeFirst();
} }
async findOne(id: string) { async findOne(id: string, withUserId: boolean) {
return await this.database return await this.database
.selectFrom("Comments") .selectFrom("Comments")
.select([ .select((eq) => [
"id", "id",
"content", "content",
"attachments", "attachments",
"createdAt", "createdAt",
"userId",
"updatedAt", "updatedAt",
jsonObjectFrom(
eq
.selectFrom("User")
.select(["displayName", "username", "profileImage"])
.whereRef("id", "=", "Comments.userId"),
).as("author"),
jsonBuildObject({
likes: eq
.selectFrom("CommentLike")
.whereRef("commentId", "=", "Comments.id")
.select(eq.fn.countAll<number>().as("likes")),
replies: eq
.selectFrom("Comments")
.where((qb) =>
qb("Comments.kweekId", "=", id).or("parentId", "=", id),
)
.select(eq.fn.countAll<number>().as("replies")),
}).as("count"),
jsonArrayFrom(
eq
.selectFrom("Comments")
.select((qb) => [
"id",
"content",
"attachments",
"createdAt",
"updatedAt",
jsonObjectFrom(
qb
.selectFrom("User")
.select(["displayName", "username", "profileImage"])
.whereRef("id", "=", "Comments.userId"),
).as("author"),
"kweekId", "kweekId",
"parentId", "parentId",
]) ])
.where("Comments.parentId", "=", id),
).as("replies"),
"kweekId",
"parentId",
])
.$if(withUserId, (qb) => qb.select("userId"))
.where("id", "=", id) .where("id", "=", id)
.executeTakeFirst(); .executeTakeFirst();
} }
@ -94,24 +137,4 @@ export class CommentsRepository {
.returningAll() .returningAll()
.executeTakeFirst(); .executeTakeFirst();
} }
async countLikes(id: string) {
const count = await this.database
.selectFrom("CommentLike")
.where("commentId", "=", id)
.select(this.database.fn.countAll<number>().as("count"))
.executeTakeFirstOrThrow();
return count.count ?? 0;
}
async countComments(id: string) {
const count = await this.database
.selectFrom("Comments")
.where((qb) => qb("kweekId", "=", id).or("parentId", "=", id))
.select(this.database.fn.countAll<number>().as("count"))
.executeTakeFirstOrThrow();
return count.count ?? 0;
}
} }

View file

@ -1,4 +1,10 @@
import { Injectable } from "@nestjs/common"; import { Injectable } from "@nestjs/common";
import { Expression } from "kysely";
import {
jsonArrayFrom,
jsonBuildObject,
jsonObjectFrom,
} from "kysely/helpers/postgres";
import { Database } from "src/services/kysely/kysely.service"; import { Database } from "src/services/kysely/kysely.service";
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
@ -31,10 +37,61 @@ export class KweeksRepository {
.executeTakeFirst(); .executeTakeFirst();
} }
async findOne(id: string) { async findOne(id: string, withUserId: boolean) {
return await this.database return await this.database
.selectFrom("Kweek") .selectFrom("Kweek")
.selectAll() .select((eq) => [
"id",
"content",
"attachments",
"createdAt",
"updatedAt",
jsonBuildObject({
likes: eq
.selectFrom("KweekLike")
.whereRef("kweekId", "=", "Kweek.id")
.select(eq.fn.countAll<number>().as("likes")),
comments: eq
.selectFrom("Comments")
.where("kweekId", "=", id)
.select(eq.fn.countAll<number>().as("comments")),
}).as("count"),
jsonObjectFrom(
eq
.selectFrom("User")
.select(["displayName", "username", "profileImage"])
.whereRef("id", "=", "authorId"),
).as("author"),
jsonArrayFrom(
eq
.selectFrom("Comments")
.select((qb) => [
"id",
"content",
"attachments",
"createdAt",
"updatedAt",
jsonBuildObject({
likes: qb
.selectFrom("CommentLike")
.whereRef("commentId", "=", "Comments.id")
.select(qb.fn.countAll<number>().as("likes")),
replies: qb
.selectFrom("Comments as Reply")
.whereRef("Reply.parentId", "=", "Comments.id")
.select(qb.fn.countAll<number>().as("replies")),
}).as("count"),
jsonObjectFrom(
qb
.selectFrom("User")
.select(["displayName", "username", "profileImage"])
.whereRef("id", "=", "Comments.userId"),
).as("author"),
])
.whereRef("Comments.kweekId", "=", "Kweek.id"),
).as("comments"),
])
.$if(withUserId, (qb) => qb.select("authorId"))
.where("id", "=", id) .where("id", "=", id)
.executeTakeFirst(); .executeTakeFirst();
} }
@ -78,22 +135,4 @@ export class KweeksRepository {
.returningAll() .returningAll()
.executeTakeFirst(); .executeTakeFirst();
} }
async countLikes(id: string) {
const count = await this.database
.selectFrom("KweekLike")
.where("kweekId", "=", id)
.select(this.database.fn.countAll<number>().as("count"))
.executeTakeFirstOrThrow();
return count.count ?? 0;
}
async countComments(id: string) {
const count = await this.database
.selectFrom("Comments")
.where("kweekId", "=", id)
.select(this.database.fn.countAll<number>().as("count"))
.executeTakeFirstOrThrow();
return count.count ?? 0;
}
} }

View file

@ -12,15 +12,11 @@ import { Configuration } from "./configuration";
/* /*
--- Present --- --- Present ---
TODO: Finish some routes.
-> Delete User service needs more protection.
TODO: Add `user` type to @nestjs/common ---> Request.
TODO: Add a authorization system. TODO: Add a authorization system.
TODO: Send e-mails to the user when something happens to his account. TODO: Send e-mails to the user when something happens to his account.
TODO: Create the chat system. TODO: Create the chat system.
-> Initialize the websocket system first. -> Initialize the websocket system first.
TODO: Create a TOS. TODO: Create a TOS.
TODO: Improve Kysely Queries.
TODO: Fix Docker Image. TODO: Fix Docker Image.
*/ */

View file

@ -1,8 +0,0 @@
import { Module } from "@nestjs/common";
import { PrismaService } from "./prisma.service";
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}

View file

@ -1,18 +0,0 @@
import { INestApplication, Injectable, OnModuleInit } from "@nestjs/common";
import { Prisma, PrismaClient } from "@prisma/client";
@Injectable()
export class PrismaService
extends PrismaClient<Prisma.PrismaClientOptions, "beforeExit">
implements OnModuleInit
{
async onModuleInit() {
await this.$connect();
}
async enableShutdownHooks(app: INestApplication) {
this.$on("beforeExit", async () => {
await app.close();
});
}
}