Changes between Initial Version and Version 1 of Transactions


Ignore:
Timestamp:
01/29/26 02:00:09 (11 days ago)
Author:
233051
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Transactions

    v1 v1  
     1== Transactions ==
     2
     3=== Transaction for getting build details ===
     4
     5{{{
     6db.transaction(async (tx) => {
     7        const [buildDetails] = await tx
     8            .select({
     9                id: buildsTable.id,
     10                userId: buildsTable.userId,
     11                name: buildsTable.name,
     12                createdAt: buildsTable.createdAt,
     13                description: buildsTable.description,
     14                totalPrice: buildsTable.totalPrice,
     15                isApproved: buildsTable.isApproved,
     16                creator: usersTable.username
     17            })
     18            .from(buildsTable)
     19            .innerJoin(
     20                usersTable,
     21                eq(buildsTable.userId, usersTable.id)
     22            )
     23            .where(
     24                eq(buildsTable.id, buildId)
     25            )
     26            .limit(1);
     27
     28        if (!buildDetails) return null;
     29
     30        const components = await tx
     31            .select({
     32                componentId: buildComponentsTable.componentId,
     33                component: componentsTable,
     34                quantity: buildComponentsTable.numComponents
     35            })
     36            .from(buildComponentsTable)
     37            .innerJoin(
     38                componentsTable,
     39                eq(buildComponentsTable.componentId, componentsTable.id)
     40            )
     41            .where(
     42                eq(buildComponentsTable.buildId, buildId)
     43            );
     44
     45        const reviews = await tx
     46            .select({
     47                username: usersTable.username,
     48                content: reviewsTable.content,
     49                createdAt: reviewsTable.createdAt
     50            })
     51            .from(reviewsTable)
     52            .innerJoin(
     53                usersTable,
     54                eq(reviewsTable.userId, usersTable.id)
     55            )
     56            .where(
     57                eq(reviewsTable.buildId, buildId)
     58            )
     59            .orderBy(
     60                desc(reviewsTable.createdAt)
     61            );
     62
     63        let [ratingStatistics] = await tx
     64            .select({
     65                averageRating: sql<number>`COALESCE(AVG(${ratingBuildsTable.value}::float), 0)`.as("averageRating"),
     66                ratingCount: sql<number>`COUNT(${ratingBuildsTable.value})`.as("ratingCount")
     67            })
     68            .from(ratingBuildsTable)
     69            .where(
     70                eq(ratingBuildsTable.buildId, buildId)
     71            )
     72            .groupBy(ratingBuildsTable.buildId);
     73
     74        ratingStatistics = {
     75            averageRating: Number(ratingStatistics?.averageRating ?? 0),
     76            ratingCount: Number(ratingStatistics?.ratingCount ?? 0),
     77        }
     78
     79        let userRating = null;
     80        let isFavorite = false;
     81        let userReview = null;
     82
     83        if(userId) {
     84            const [rating] = await tx
     85                .select()
     86                .from(ratingBuildsTable)
     87                .where(
     88                    and(
     89                        eq(ratingBuildsTable.buildId, buildId),
     90                        eq(ratingBuildsTable.userId, userId)
     91                    )
     92                )
     93                .limit(1);
     94
     95            userRating = rating?.value ? Number(rating.value) : null;
     96
     97            const [favorite] = await tx
     98                .select()
     99                .from(favoriteBuildsTable)
     100                .where(
     101                    and(
     102                        eq(favoriteBuildsTable.buildId, buildId),
     103                        eq(favoriteBuildsTable.userId, userId)
     104                    )
     105                )
     106                .limit(1);
     107
     108            isFavorite = !!favorite;
     109
     110            const [review] = await tx
     111                .select()
     112                .from(reviewsTable)
     113                .where(
     114                    and(
     115                        eq(reviewsTable.buildId, buildId),
     116                        eq(reviewsTable.userId, userId)
     117                    )
     118                )
     119                .limit(1);
     120
     121            userReview = review?.content;
     122        }
     123
     124        return {
     125            ...buildDetails,
     126            components: components.map(c => ({
     127                    ...c.component,
     128                    quantity: c.quantity
     129            })),
     130            reviews: reviews.map(r => ({
     131                username: r.username,
     132                content: r.content,
     133                createdAt: r.createdAt
     134            })),
     135            ratingStatistics: ratingStatistics,
     136            userRating,
     137            userReview,
     138            isFavorite
     139        };
     140    });
     141}}}
     142
     143=== Transaction for cloning existing build ===
     144
     145{{{
     146db.transaction(async (tx) => {
     147        const [buildToClone] = await tx
     148            .select()
     149            .from(buildsTable)
     150            .where(
     151                eq(buildsTable.id, buildId)
     152            )
     153            .limit(1);
     154
     155        if (!buildToClone) return null;
     156
     157        const [newBuild] = await tx
     158            .insert(buildsTable)
     159            .values({
     160                userId: userId,
     161                name: `${buildToClone.name} (copy)`,
     162                createdAt: new Date().toISOString().split('T')[0],
     163                description: buildToClone.description,
     164                totalPrice: buildToClone.totalPrice,
     165                isApproved: false
     166            })
     167            .returning({
     168                id: buildsTable.id
     169            });
     170
     171        if(!newBuild) return null;
     172
     173        const existing = await tx
     174            .select({
     175                componentId: buildComponentsTable.componentId,
     176                numComponents: buildComponentsTable.numComponents
     177            })
     178            .from(buildComponentsTable)
     179            .where(eq(buildComponentsTable.buildId, buildId));
     180
     181        if (existing.length > 0) {
     182            await tx.insert(buildComponentsTable).values(
     183                existing.map((r) => ({
     184                    buildId: newBuild.id,
     185                    componentId: r.componentId,
     186                    numComponents: r.numComponents
     187                })),
     188            );
     189        }
     190
     191        return newBuild.id;
     192    });
     193}}}
     194
     195=== Transaction for getting current build state ===
     196
     197{{{
     198db.transaction(async (tx) => {
     199        const [build] = await tx
     200            .select({
     201                id: buildsTable.id,
     202                userId: buildsTable.userId,
     203                isApproved: buildsTable.isApproved,
     204                name: buildsTable.name,
     205                description: buildsTable.description,
     206                totalPrice: buildsTable.totalPrice,
     207            })
     208            .from(buildsTable)
     209            .where(
     210                and(
     211                    eq(buildsTable.id, buildId),
     212                    eq(buildsTable.userId, userId)
     213                )
     214            )
     215            .limit(1);
     216
     217        if (!build || build.isApproved) return null;
     218
     219        const components = await tx
     220            .select({
     221                componentId: buildComponentsTable.componentId,
     222                quantity: buildComponentsTable.numComponents
     223            })
     224            .from(buildComponentsTable)
     225            .where(
     226                eq(buildComponentsTable.buildId, buildId)
     227            );
     228
     229        return {
     230            build,
     231            components: components.map(c => ({
     232                id: c.componentId,
     233                quantity: c.quantity
     234            }))
     235        };
     236    });
     237}}}
     238
     239=== Transaction for adding a new component to the database ===
     240
     241{{{
     242db.transaction(async (tx) => {
     243        const [newComponent] = await tx
     244            .insert(componentsTable)
     245            .values({
     246                name: name,
     247                brand: brand,
     248                price: price.toFixed(2),
     249                imgUrl: imgUrl,
     250                type: type
     251            })
     252            .returning({
     253                id: componentsTable.id
     254            });
     255
     256        const componentId = newComponent.id;
     257
     258        const config = typeConfigMap[type as ComponentType];
     259
     260        await tx
     261            .insert(config.table)
     262            .values({
     263                componentId: componentId,
     264                ...specificData
     265            });
     266
     267        if (type === 'case') {
     268            if (specificData.storageFormFactors) {
     269                await tx.insert(caseStorageFormFactorsTable).values(
     270                    specificData.storageFormFactors.map((sf: any) => ({
     271                        caseId: componentId,
     272                        formFactor: sf.formFactor,
     273                        numSlots: sf.numSlots,
     274                    }))
     275                );
     276            }
     277            if (specificData.psFormFactors) {
     278                await tx.insert(casePsFormFactorsTable).values(
     279                    specificData.psFormFactors.map((pf: any) => ({
     280                        caseId: componentId,
     281                        formFactor: pf.formFactor,
     282                    }))
     283                );
     284            }
     285            if (specificData.moboFormFactors) {
     286                await tx.insert(caseMoboFormFactorsTable).values(
     287                    specificData.moboFormFactors.map((mf: any) => ({
     288                        caseId: componentId,
     289                        formFactor: mf.formFactor,
     290                    }))
     291                );
     292            }
     293        }
     294
     295        if (type === 'cooler' && specificData.cpuSockets) {
     296            await tx.insert(coolerCPUSocketsTable).values(
     297                specificData.cpuSockets.map((socket: any) => ({ coolerId: componentId, socket }))
     298            );
     299        }
     300
     301        return componentId;
     302    });
     303}}}
     304
     305=== Transaction for adding a component to a build ===
     306
     307{{{
     308db.transaction(async (tx) => {
     309        const [build] = await tx
     310            .select()
     311            .from(buildsTable)
     312            .where(
     313                and(
     314                    eq(buildsTable.id, buildId),
     315                    eq(buildsTable.userId, userId)
     316                )
     317            )
     318            .limit(1);
     319
     320        if(!build || build.isApproved) return null;
     321
     322        await tx
     323            .insert(buildComponentsTable)
     324            .values({
     325                buildId,
     326                componentId,
     327                numComponents: 1
     328            })
     329            .onConflictDoUpdate({
     330                target: [buildComponentsTable.buildId, buildComponentsTable.componentId],
     331                set: {
     332                    numComponents: sql`${buildComponentsTable.numComponents} + 1`
     333                }
     334            });
     335
     336        const buildComponents = await tx
     337            .select({
     338                price:  componentsTable.price,
     339                quantity: buildComponentsTable.numComponents
     340            })
     341            .from(buildComponentsTable)
     342            .innerJoin(
     343                componentsTable,
     344                eq(buildComponentsTable.componentId, componentsTable.id)
     345            )
     346            .where(
     347                eq(buildComponentsTable.buildId, buildId)
     348            );
     349
     350        // const totalPrice = buildComponents.reduce((sum, c) =>
     351        //     sum + (Number(c.price) * c.quantity), 0
     352        // );
     353
     354        // await tx
     355        //     .update(buildsTable)
     356        //     .set({
     357        //         totalPrice: totalPrice.toFixed(2)
     358        //     })
     359        //     .where(
     360        //         eq(buildsTable.id, buildId)
     361        //     );
     362
     363        return buildId;
     364    })
     365}}}
     366
     367=== Transaction for removing a component from a build ===
     368
     369{{{
     370db.transaction(async (tx) => {
     371        const [build] = await tx
     372            .select()
     373            .from(buildsTable)
     374            .where(
     375                and(
     376                    eq(buildsTable.id, buildId),
     377                    eq(buildsTable.userId, userId)
     378                )
     379            )
     380            .limit(1);
     381
     382        if(!build || build.isApproved) return null;
     383
     384        const [existing] = await tx
     385            .select({
     386                quantity: buildComponentsTable.numComponents
     387            })
     388            .from(buildComponentsTable)
     389            .where(
     390                and(
     391                    eq(buildComponentsTable.buildId, buildId),
     392                    eq(buildComponentsTable.componentId, componentId)
     393                )
     394            )
     395            .limit(1);
     396
     397        if (!existing) return null;
     398
     399        if (existing.quantity > 1) {
     400            await tx
     401                .update(buildComponentsTable)
     402                .set({
     403                    numComponents: sql`${buildComponentsTable.numComponents} - 1`
     404                })
     405                .where(
     406                    and(
     407                        eq(buildComponentsTable.buildId, buildId),
     408                        eq(buildComponentsTable.componentId, componentId)
     409                    )
     410                );
     411        } else {
     412            await tx
     413                .delete(buildComponentsTable)
     414                .where(
     415                    and(
     416                        eq(buildComponentsTable.buildId, buildId),
     417                        eq(buildComponentsTable.componentId, componentId)
     418                    )
     419                );
     420        }
     421
     422        const buildComponents = await tx
     423            .select({
     424                price:  componentsTable.price,
     425                quantity: buildComponentsTable.numComponents
     426            })
     427            .from(buildComponentsTable)
     428            .innerJoin(
     429                componentsTable,
     430                eq(buildComponentsTable.componentId, componentsTable.id)
     431            )
     432            .where(
     433                eq(buildComponentsTable.buildId, buildId)
     434            );
     435
     436        // const totalPrice = buildComponents.reduce((sum, c) =>
     437        //     sum + (Number(c.price) * c.quantity), 0
     438        // );
     439
     440        // await tx
     441        //     .update(buildsTable)
     442        //     .set({
     443        //         totalPrice: totalPrice.toFixed(2)
     444        //     })
     445        //     .where(
     446        //         eq(buildsTable.id, buildId)
     447        //     );
     448
     449        return componentId;
     450    })
     451}}}