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

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

remove deprecated query and telefunc

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