I'm trying to retrieve the test from a user that has the highest WPM
and accuracy
with a time of 60 seconds
. I've managed to get the test with the highest WPM and accuracy. If there are two tests with the same wpm and accuracy then the createdAt
date is used to pick a test, so the test with the older createdAt date is picked. The problem is that if there are two tests with the same WPM and accuracy, and there's a third test with a higher WPM, the query fails to return any test.
Here is the query builder:
const qb = ctx.em
.createQueryBuilder(Test, 'test')
.innerJoin(
(subQuery) =>
subQuery
.select('MAX(t.wpm)', 'max_wpm')
.addSelect('t.creatorId', 'creatorId')
.addSelect('MAX(t.accuracy)', 'max_accuracy')
.from(Test, 't')
.where('t.time = :time', { time: '60' })
.groupBy('t.creatorId'),
'max_tests',
'max_tests.max_wpm = test.wpm AND max_tests."creatorId" = test."creatorId" AND max_tests.max_accuracy = test.accuracy'
)
.leftJoinAndSelect('test.creator', 'creator')
.where('test.time = :time', { time: '60' })
.andWhere((qb) => {
const subQuery = qb
.subQuery()
.select('MIN(t2.createdAt)')
.from(Test, 't2')
.where('t2.creatorId = test.creatorId')
.andWhere('t2.wpm = test.wpm')
.andWhere('t2.accuracy = test.accuracy')
.getQuery();
return `test."createdAt" = (${subQuery})`;
})
.orderBy('test.wpm', 'DESC')
.addOrderBy('test.accuracy', 'DESC')
And here are three example tests:
[
{
"id": 1,
"wpm": 96,
"accuracy": 96.7,
"time": "60",
"createdAt": "2023-05-09T11:26:42.003917Z",
"creatorId": 1
},
{
"id": 2,
"wpm": 96,
"accuracy": 96.7,
"time": "60",
"createdAt": "2023-05-09T12:58:48.956275Z",
"creatorId": 1
},
{
"id": 3,
"wpm": 97,
"accuracy": 97,
"time": "60",
"createdAt": "2023-05-09T13:18:21.991219Z",
"creatorId": 1
}
]
The expected result is to get test #3, but my current code fails to return anything.
How can I modify my query?
Database: postgreSQL orm: typeorm
I figured out that the issue was that I was comparing both wpm and accuracy at the same time, so if two tests have the same wpm and accuracy and if the third test has a higher wpm but lower accuracy then the query fails which was what happened in this case. So I modified the queryBuilder to first check for highest wpm and if there's two tests with the same wpm then check for accuracy and if there is a duplication then check for createdAt date.
Here is the final qb:
const qb = ctx.em
.createQueryBuilder(Test, 'test')
.innerJoin(
(subQuery) =>
subQuery
.select('MAX(t.wpm)', 'max_wpm')
.addSelect('t.creatorId', 'creatorId')
.from(Test, 't')
.where('t.time = :time', { time: '60' })
.groupBy('t.creatorId'),
'max_tests',
'max_tests.max_wpm = test.wpm AND max_tests."creatorId" = test."creatorId"'
)
.where('test.time = :time', { time: '60' })
.andWhere((qb) => {
const subQuery = qb
.subQuery()
.select('MAX(t2.accuracy)')
.from(Test, 't2')
.where('t2.time = :time')
.andWhere('t2.wpm = test.wpm')
.andWhere('t2.creatorId = test.creatorId')
.setParameter('time', '60')
.getQuery();
return `test.accuracy = (${subQuery})`;
})
.andWhere((qb) => {
const subQuery = qb
.subQuery()
.select('MIN(t3.createdAt)')
.from(Test, 't3')
.where('t3.creatorId = test.creatorId')
.andWhere('t3.wpm = test.wpm')
.andWhere('t3.accuracy = test.accuracy')
.setParameter('time', '60')
.getQuery();
return `test."createdAt" = (${subQuery})`;
})
.leftJoinAndSelect('test.creator', 'creator')
.orderBy('test.wpm', 'DESC')
.addOrderBy('test.accuracy', 'DESC')
.addOrderBy('test.createdAt', 'ASC');