source: database/drizzle/queries/builds.ts@ ea09e98

main
Last change on this file since ea09e98 was ea09e98, checked in by Tome <gjorgievtome@…>, 7 months ago

Update and add

  • Property mode set to 100644
File size: 16.5 KB
RevLine 
[1ebac59]1import type { Database } from "../db";
[c4dd17d]2import {
3 buildComponentsTable,
4 buildsTable,
5 componentsTable,
6 favoriteBuildsTable,
7 ratingBuildsTable,
8 reviewsTable, usersTable
9} from "../schema";
[1ebac59]10import {eq, desc, and, sql, ilike} from "drizzle-orm";
[ea09e98]11import {inArray} from "drizzle-orm/sql/expressions/conditions";
[1ebac59]12
13export async function getPendingBuilds(db: Database) {
14 const pendingBuildsList = await db
15 .select({
16 id: buildsTable.id,
17 user_id: buildsTable.userId,
18 name: buildsTable.name,
19 created_at: buildsTable.createdAt,
20 total_price: buildsTable.totalPrice
21 })
22 .from(buildsTable)
23 .where(
24 eq(buildsTable.isApproved, false)
25 );
26
27 return pendingBuildsList;
28}
29
30export async function getUserBuilds(db: Database, userId: number) {
31 const userBuildsList = await db
32 .select({
33 id: buildsTable.id,
34 user_id: buildsTable.userId,
35 name: buildsTable.name,
36 created_at: buildsTable.createdAt,
37 total_price: buildsTable.totalPrice
38 })
39 .from(buildsTable)
40 .where(
41 eq(buildsTable.userId, userId)
42 );
43
44 return userBuildsList;
45}
46
47export async function setBuildApprovalStatus(db: Database, buildId: number, isApproved: boolean){
48 const result = await db
49 .update(buildsTable)
50 .set({
51 isApproved: isApproved
52 })
53 .where(
[ea09e98]54 and(
55 eq(buildsTable.id, buildId),
56 eq(buildsTable.isApproved, !isApproved)
57 )
58 )
59 .returning({
60 id: buildsTable.id
61 })
[1ebac59]62
[ea09e98]63 return result.length;
[1ebac59]64}
65
66export async function getFavoriteBuilds(db: Database, userId: number) {
67 const favoriteBuildsList = await db
68 .select({
69 id: buildsTable.id,
70 user_id: buildsTable.userId,
71 name: buildsTable.name,
72 created_at: buildsTable.createdAt,
73 total_price: buildsTable.totalPrice
74 })
75 .from(buildsTable)
76 .innerJoin(
77 favoriteBuildsTable,
78 eq(buildsTable.id, favoriteBuildsTable.buildId)
79 )
80 .where(
81 eq(favoriteBuildsTable.userId, userId)
82 );
83
84 return favoriteBuildsList;
85}
86
87export async function getApprovedBuilds(db: Database, limit?: number, q?: string) {
[c4dd17d]88 let queryConditions = [eq(buildsTable.isApproved, true)];
[1ebac59]89
90 if (q) {
91 queryConditions.push(
92 ilike(buildsTable.name, `%${q}%`)
93 );
94 }
95
96 const approvedBuildsList = await db
97 .select({
98 id: buildsTable.id,
99 user_id: buildsTable.userId,
100 name: buildsTable.name,
101 created_at: buildsTable.createdAt,
102 total_price: buildsTable.totalPrice
103 })
104 .from(buildsTable)
105 .where(
106 and (
107 ...queryConditions
108 )
109 )
110 .orderBy(
111 desc(buildsTable.totalPrice)
112 )
113 .limit(limit || 100); // 100 placeholder
114
115 return approvedBuildsList;
116}
117
118export async function getHighestRankedBuilds(db: Database, limit? : number) {
119 const highestRankedBuildsList = await db
120 .select({
121 id: buildsTable.id,
122 user_id: buildsTable.userId,
123 name: buildsTable.name,
124 created_at: buildsTable.createdAt,
[c4dd17d]125 total_price: buildsTable.totalPrice,
126 avgRating: sql<number>`AVG(${ratingBuildsTable.value}::float)`
[1ebac59]127 })
128 .from(buildsTable)
129 .innerJoin(
130 ratingBuildsTable,
131 eq(buildsTable.id, ratingBuildsTable.buildId)
132 )
133 .where(
134 eq(buildsTable.isApproved, true)
135 )
[c4dd17d]136 .groupBy(buildsTable.id)
[1ebac59]137 .orderBy(
138 desc(sql<number>`AVG(${ratingBuildsTable.value}::float)`)
139 )
140 .limit(limit || 100); // 100 placeholder
141
142 return highestRankedBuildsList;
143}
144
[c4dd17d]145export async function getBuildDetails(db: Database, buildId: number, userId?: number) {
146 return db.transaction(async (tx) => {
147 const [buildDetails] = await tx
148 .select({
149 id: buildsTable.id,
150 userId: buildsTable.userId,
151 name: buildsTable.name,
152 createdAt: buildsTable.createdAt,
153 description: buildsTable.description,
154 totalPrice: buildsTable.totalPrice,
155 isApproved: buildsTable.isApproved,
156 creator: usersTable.username
157 })
158 .from(buildsTable)
159 .innerJoin(
160 usersTable,
161 eq(buildsTable.userId, usersTable.id)
162 )
163 .where(
164 eq(buildsTable.id, buildId)
165 )
166 .limit(1);
167
168 if (!buildDetails) return null;
169
170 const components = await tx
171 .select({
172 componentId: buildComponentsTable.componentId,
173 component: componentsTable
174 })
175 .from(buildComponentsTable)
176 .innerJoin(
177 componentsTable,
178 eq(buildComponentsTable.componentId, componentsTable.id)
179 )
180 .where(
181 eq(buildComponentsTable.buildId, buildId)
182 );
[1ebac59]183
[c4dd17d]184 const reviews = await tx
185 .select({
186 username: usersTable.username,
187 content: reviewsTable.content,
188 createdAt: reviewsTable.createdAt
189 })
190 .from(reviewsTable)
191 .innerJoin(
192 usersTable,
193 eq(reviewsTable.userId, usersTable.id)
194 )
195 .where(
196 eq(reviewsTable.buildId, buildId)
197 )
198 .orderBy(
199 desc(reviewsTable.createdAt)
200 );
201
202 let [ratingStatistics] = await tx
203 .select({
204 averageRating: sql<number>`COALESCE(AVG(${ratingBuildsTable.value}::float), 0)`.as("averageRating"),
205 ratingCount: sql<number>`COUNT(${ratingBuildsTable.value})`.as("ratingCount")
206 })
207 .from(ratingBuildsTable)
208 .where(
209 eq(ratingBuildsTable.buildId, buildId)
210 )
211 .groupBy(ratingBuildsTable.buildId);
212
213 ratingStatistics = {
214 averageRating: Number(ratingStatistics?.averageRating ?? 0),
215 ratingCount: Number(ratingStatistics?.ratingCount ?? 0),
216 }
217
218 let userRating = null;
219 let isFavorite = false;
220 let userReview = null;
221
222 if(userId) {
223 const [rating] = await tx
224 .select()
225 .from(ratingBuildsTable)
226 .where(
227 and(
228 eq(ratingBuildsTable.buildId, buildId),
229 eq(ratingBuildsTable.userId, userId)
230 )
231 )
232 .limit(1);
233
234 userRating = rating?.value ? Number(rating.value) : null;
235
236 const [favorite] = await tx
237 .select()
238 .from(favoriteBuildsTable)
239 .where(
240 and(
241 eq(favoriteBuildsTable.buildId, buildId),
242 eq(favoriteBuildsTable.userId, userId)
243 )
244 )
245 .limit(1);
246
247 isFavorite = !!favorite;
248
249 const [review] = await tx
250 .select()
251 .from(reviewsTable)
252 .where(
253 and(
254 eq(reviewsTable.buildId, buildId),
255 eq(reviewsTable.userId, userId)
256 )
257 )
258 .limit(1);
259
260 userReview = review?.content;
261 }
262
263 return {
264 ...buildDetails,
265 components: components.map(c => c.component),
[ea09e98]266 reviews: reviews.map(r => ({
267 username: r.username,
268 content: r.content,
269 createdAt: r.createdAt
270 })),
[c4dd17d]271 ratingStatistics: ratingStatistics,
272 userRating,
273 userReview,
274 isFavorite
275 };
276 });
[1ebac59]277}
278
279export async function toggleFavoriteBuild(db: Database, userId: number, buildId: number) {
280 const existing = await db
281 .select()
282 .from(favoriteBuildsTable)
283 .where(
284 and(
285 eq(favoriteBuildsTable.userId, userId),
286 eq(favoriteBuildsTable.buildId, buildId)
287 )
288 )
289 .limit(1);
290
291 if (existing.length) {
292 await db
293 .delete(favoriteBuildsTable)
294 .where(
295 and(
296 eq(favoriteBuildsTable.userId, userId),
297 eq(favoriteBuildsTable.buildId, buildId)
298 )
299 );
300
301 return { favorite: false };
302 }
303
304 await db
305 .insert(favoriteBuildsTable)
306 .values({
307 userId,
308 buildId,
309 });
310
311 return { favorite: true };
312}
313
314export async function setBuildRating(db: Database, userId: number, buildId: number, value: number) {
[ea09e98]315 const [result] = await db
[1ebac59]316 .insert(ratingBuildsTable)
317 .values({
318 userId,
319 buildId,
[c4dd17d]320 value: value
[1ebac59]321 })
322 .onConflictDoUpdate({
323 target: [ratingBuildsTable.userId, ratingBuildsTable.buildId],
324 set: {
[c4dd17d]325 value: value,
[1ebac59]326 },
[ea09e98]327 })
328 .returning({
329 userId: ratingBuildsTable.userId,
330 buildId: ratingBuildsTable.buildId,
331 value: ratingBuildsTable.value
332 })
333
334 return result;
[1ebac59]335}
336
337export async function setBuildReview(db: Database, userId: number, buildId: number, content: string) {
[ea09e98]338 const [result] = await db
339 .insert(reviewsTable)
340 .values({
341 userId,
342 buildId,
343 content,
344 createdAt: new Date().toISOString().split('T')[0]
345 })
346 .onConflictDoUpdate({
347 target: [reviewsTable.userId, reviewsTable.buildId],
348 set: {
349 content: content,
350 createdAt: new Date().toISOString().split('T')[0]
351 },
352 })
353 .returning({
354 userId: reviewsTable.userId,
355 buildId: reviewsTable.buildId,
356 content: reviewsTable.content,
357 createdAt: reviewsTable.createdAt
358 })
359
360 return result;
361}
362
363export async function cloneBuild(db: Database, userId: number, buildId: number) {
364 return db.transaction(async (tx) => {
365 const [buildToClone] = await tx
366 .select({
367 id: buildsTable.id,
368 userId: buildsTable.userId,
369 name: buildsTable.name,
370 description: buildsTable.description,
371 totalPrice: buildsTable.totalPrice,
372 })
373 .from(buildsTable)
374 .where(
375 eq(buildsTable.id, buildId)
376 )
377 .limit(1);
378
379 if (!buildToClone) return null;
380
381 const [newBuild] = await tx
382 .insert(buildsTable)
383 .values({
384 userId: userId,
385 name: `${buildToClone.name} (copy)`,
386 createdAt: new Date().toISOString().split('T')[0],
387 description: buildToClone.description,
388 totalPrice: buildToClone.totalPrice,
389 isApproved: false
390 })
391 .returning({
392 id: buildsTable.id
393 });
394
395 const components = await tx
396 .select()
397 .from(buildComponentsTable)
398 .where(
399 eq(buildComponentsTable.buildId, buildId)
400 );
401
402 if(components.length) {
403 await tx
404 .insert(buildComponentsTable)
405 .values(
406 components.map(component => ({
407 buildId: newBuild.id,
408 componentId: component.componentId
409 }))
410 );
411 }
412
413 const [clonedBuild] = await tx
414 .select({
415 id: buildsTable.id,
416 userId: buildsTable.userId,
417 name: buildsTable.name,
418 createdAt: buildsTable.createdAt,
419 description: buildsTable.description,
420 totalPrice: buildsTable.totalPrice
421 })
422 .from(buildsTable)
423 .innerJoin(
424 usersTable,
425 eq(buildsTable.userId, usersTable.id)
426 )
427 .where(
428 eq(buildsTable.id, newBuild.id)
429 )
430 .limit(1);
431
432 const clonedComponents = await tx
433 .select({
434 componentId: buildComponentsTable.componentId,
435 component: componentsTable
436 })
437 .from(buildComponentsTable)
438 .innerJoin(
439 componentsTable,
440 eq(buildComponentsTable.componentId, componentsTable.id)
441 )
442 .where(
443 eq(buildComponentsTable.buildId, newBuild.id)
444 );
445
446 return {
447 ...clonedBuild,
448 components: clonedComponents.map(c => c.component)
449 };
450 });
451}
452
453export async function deleteBuild(db: Database, userId: number, buildId: number) {
454 const result = await db
455 .delete(buildsTable)
[1ebac59]456 .where(
457 and(
[ea09e98]458 eq(buildsTable.id, buildId),
459 eq(buildsTable.userId, userId)
[1ebac59]460 )
461 )
[ea09e98]462 .returning({
463 id: buildsTable.id
464 })
[1ebac59]465
[ea09e98]466 return result.length;
467}
468
469export async function addNewBuild(db: Database, userId: number, name: string, description: string, componentIds: number[]) {
470 return db.transaction(async (tx) => {
471 const components = await tx
472 .select({
473 price: componentsTable.price
[1ebac59]474 })
[ea09e98]475 .from(componentsTable)
[1ebac59]476 .where(
[ea09e98]477 inArray(componentsTable.id, componentIds)
[1ebac59]478 );
479
[ea09e98]480 const totalPrice = components.reduce((sum, c) => sum + Number(c.price), 0);
481
482 const [newBuild] = await tx
483 .insert(buildsTable)
484 .values({
485 userId: userId,
486 name: name,
487 createdAt: new Date().toISOString().split('T')[0],
488 description: description,
489 totalPrice: totalPrice.toFixed(2),
490 isApproved: false
491 })
492 .returning({
493 id: buildsTable.id
494 });
495
496 if(components.length) {
497 await tx.insert(buildComponentsTable)
498 .values(
499 componentIds.map(componentId => ({
500 buildId: newBuild.id,
501 componentId: componentId
502 }))
503 );
504 }
[1ebac59]505
[ea09e98]506 return newBuild.id;
507 });
[1ebac59]508}
509
[ea09e98]510export async function editBuild(db: Database, userId: number, buildId: number, name: string, description: string, componentIds: number[]) {
511 return db.transaction(async (tx) => {
512 const [build] = await tx
513 .select({
514 id: buildsTable.id,
515 userId: buildsTable.userId,
516 isApproved: buildsTable.isApproved
517 })
518 .from(buildsTable)
519 .where(
520 and(
521 eq(buildsTable.id, buildId),
522 eq(buildsTable.userId, userId)
523 )
524 )
525 .limit(1);
[1ebac59]526
[ea09e98]527 if (!build) return null;
528 if (build.isApproved) return null;
[1ebac59]529
[ea09e98]530 const components = await tx
531 .select({
532 id: componentsTable.id,
533 price: componentsTable.price
534 })
535 .from(componentsTable)
536 .where(
537 inArray(componentsTable.id, componentIds)
538 );
[1ebac59]539
[ea09e98]540 const totalPrice = components.reduce((sum, c) => sum + Number(c.price), 0);
[1ebac59]541
[ea09e98]542 await tx
543 .update(buildsTable)
544 .set({
545 name: name,
546 description: description,
547 totalPrice: totalPrice.toFixed(2),
548 })
549 .where(
550 and(
551 eq(buildsTable.id, buildId),
552 eq(buildsTable.userId, userId)
553 )
[1ebac59]554 );
555
[ea09e98]556 await tx
557 .delete(buildComponentsTable)
558 .where(
559 eq(buildComponentsTable.buildId, buildId)
560 );
561
562 if(components.length) {
563 await tx.insert(buildComponentsTable)
564 .values(
565 componentIds.map(componentId => ({
566 buildId: buildId,
567 componentId: componentId
568 }))
569 );
570 }
571
572 return build.id;
573 });
[1ebac59]574}
Note: See TracBrowser for help on using the repository browser.