Skip to content

Commit 8015f49

Browse files
committed
PEER-219: Add question history dynamic query builder for attempted qns
Signed-off-by: SeeuSim <[email protected]>
1 parent 44708bc commit 8015f49

File tree

4 files changed

+54
-28
lines changed

4 files changed

+54
-28
lines changed

backend/matching/src/services/collab.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { collabServiceClient, routes } from './_hosts';
33
export async function createRoom(
44
userId1: string,
55
userId2: string,
6-
questionId: string
6+
questionId: string,
7+
attemptCounts: number
78
): Promise<string> {
89
const response = await collabServiceClient.get<{ roomName: string }>(
910
routes.COLLAB_SERVICE.GET_ROOM.path,

backend/matching/src/services/get-match-items.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,12 @@ export async function getMatchItems(
3333
return undefined;
3434
}
3535

36-
const roomId = await createRoom(userId1, userId2, question.id.toString());
36+
const roomId = await createRoom(
37+
userId1,
38+
userId2,
39+
question.id.toString(),
40+
question.attemptCount
41+
);
3742

3843
logger.info('Successfully got match items');
3944
return {

backend/matching/src/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export interface IQuestion {
5858
// description: string;
5959
// difficulty: string;
6060
// topic: string[];
61+
attemptCount: number;
6162
}
6263

6364
export interface IGetRandomQuestionPayload {

backend/question/src/services/get/get-random-question.ts

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
eq,
66
getTableColumns,
77
inArray,
8+
InferSelectModel,
89
isNull,
910
or,
1011
sql,
@@ -86,8 +87,17 @@ type Params = {
8687
difficulty?: string;
8788
};
8889

90+
type IGetRandomQuestionResponse = InferSelectModel<typeof QUESTIONS_TABLE> & {
91+
attemptCount: number;
92+
};
93+
8994
// Fetch an unattempted question or fallback to the least attempted one
90-
export const getRandomQuestion = async ({ userId1, userId2, topics, difficulty }: Params) => {
95+
export const getRandomQuestion = async ({
96+
userId1,
97+
userId2,
98+
topics,
99+
difficulty,
100+
}: Params): Promise<IGetRandomQuestionResponse | null> => {
91101
// If an attempt contains either user's ID
92102
const ids = [userId1, userId2];
93103
const userIdClause = [
@@ -128,14 +138,15 @@ export const getRandomQuestion = async ({ userId1, userId2, topics, difficulty }
128138
}
129139

130140
for (const filterClause of filterCombinations) {
131-
// Check if questions exist with current filters
132-
const questionExists = await db
133-
.select({ count: sql<number>`count(*)` })
141+
// Check if AT LEAST 1 question exists with current filters
142+
const questionCounts = await db
143+
.select({ id: QUESTIONS_TABLE.id })
134144
.from(QUESTIONS_TABLE)
135145
.where(and(...filterClause))
136-
.then((result) => Number(result[0].count) > 0);
146+
.limit(1);
137147

138-
if (!questionExists) {
148+
// No questions exist with the filter.
149+
if (!questionCounts || !questionCounts.length) {
139150
continue;
140151
}
141152

@@ -149,28 +160,33 @@ export const getRandomQuestion = async ({ userId1, userId2, topics, difficulty }
149160
.limit(1);
150161

151162
if (bothUnattempted && bothUnattempted.length > 0) {
152-
return bothUnattempted[0].question;
163+
return { ...bothUnattempted[0].question, attemptCount: 0 };
153164
}
154165

155166
// If no unattempted question, try least attempted
156-
const attempts = db.$with('at').as(
157-
db
158-
.select({
159-
...getTableColumns(QUESTIONS_TABLE),
160-
user1Count:
161-
sql`SUM(CASE WHEN ${QUESTION_ATTEMPTS_TABLE.userId1} = ${userId1}::uuid OR ${QUESTION_ATTEMPTS_TABLE.userId2} = ${userId1}::uuid THEN 1 END)`.as(
162-
'user1_attempts'
163-
),
164-
user2Count:
165-
sql`SUM(CASE WHEN ${QUESTION_ATTEMPTS_TABLE.userId1} = ${userId2}::uuid OR ${QUESTION_ATTEMPTS_TABLE.userId2} = ${userId2}::uuid THEN 1 END)`.as(
166-
'user2_attempts'
167-
),
168-
})
169-
.from(QUESTIONS_TABLE)
170-
.innerJoin(QUESTION_ATTEMPTS_TABLE, and(...joinClause))
171-
.where(and(...filterClause))
172-
.groupBy(QUESTIONS_TABLE.id)
173-
);
167+
let nestedQuery = db
168+
.select({
169+
...getTableColumns(QUESTIONS_TABLE),
170+
user1Count:
171+
sql`SUM(CASE WHEN ${QUESTION_ATTEMPTS_TABLE.userId1} = ${userId1}::uuid OR ${QUESTION_ATTEMPTS_TABLE.userId2} = ${userId1}::uuid THEN 1 END)`.as(
172+
'user1_attempts'
173+
),
174+
user2Count:
175+
sql`SUM(CASE WHEN ${QUESTION_ATTEMPTS_TABLE.userId1} = ${userId2}::uuid OR ${QUESTION_ATTEMPTS_TABLE.userId2} = ${userId2}::uuid THEN 1 END)`.as(
176+
'user2_attempts'
177+
),
178+
})
179+
.from(QUESTIONS_TABLE)
180+
.innerJoin(QUESTION_ATTEMPTS_TABLE, and(...joinClause))
181+
.$dynamic();
182+
183+
if (filterClause.length) {
184+
nestedQuery = nestedQuery.where(and(...filterClause));
185+
}
186+
187+
nestedQuery = nestedQuery.groupBy(QUESTIONS_TABLE.id);
188+
189+
const attempts = db.$with('at').as(nestedQuery);
174190

175191
const result = await db
176192
.with(attempts)
@@ -180,7 +196,10 @@ export const getRandomQuestion = async ({ userId1, userId2, topics, difficulty }
180196
.limit(1);
181197

182198
if (result && result.length > 0) {
183-
return { ...result[0], user1Count: undefined, user2Count: undefined };
199+
const { user1Count, user2Count, ...details } = result[0];
200+
const attemptCount =
201+
(user1Count ? (user1Count as number) : 0) + (user2Count ? (user2Count as number) : 0);
202+
return { ...details, attemptCount };
184203
}
185204
}
186205

0 commit comments

Comments
 (0)