use keyset pagination for notifications (#899)

This commit is contained in:
Keyan 2024-03-06 13:53:13 -06:00 committed by GitHub
parent 8d49c034c6
commit 48aef15a07
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 48 additions and 31 deletions

View File

@ -1,5 +1,5 @@
import { GraphQLError } from 'graphql'
import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
import { decodeCursor, LIMIT, nextNoteCursorEncoded } from '../../lib/cursor'
import { getItem, filterClause, whereClause, muteClause } from './item'
import { getInvoice } from './wallet'
import { pushSubscriptionSchema, ssValidate } from '../../lib/validate'
@ -84,10 +84,11 @@ export default {
'"ThreadSubscription"."userId" = $1',
'"Item"."userId" <> $1',
'"Item".created_at >= "ThreadSubscription".created_at',
'"Item".created_at < $2',
'"Item"."parentId" IS NOT NULL'
)}
ORDER BY "sortTime" DESC
LIMIT ${LIMIT}+$3`
LIMIT ${LIMIT}`
)
// User subscriptions
@ -97,6 +98,7 @@ export default {
FROM "Item"
JOIN "UserSubscription" ON "Item"."userId" = "UserSubscription"."followeeId"
${whereClause(
'"Item".created_at < $2',
'"UserSubscription"."followerId" = $1',
`(
("Item"."parentId" IS NULL AND "UserSubscription"."postsSubscribedAt" IS NOT NULL AND "Item".created_at >= "UserSubscription"."postsSubscribedAt")
@ -104,7 +106,7 @@ export default {
)`
)}
ORDER BY "sortTime" DESC
LIMIT ${LIMIT}+$3`
LIMIT ${LIMIT}`
)
// Territory subscriptions
@ -113,13 +115,14 @@ export default {
FROM "Item"
JOIN "SubSubscription" ON "Item"."subName" = "SubSubscription"."subName"
${whereClause(
'"Item".created_at < $2',
'"SubSubscription"."userId" = $1',
'"Item"."userId" <> $1',
'"Item"."parentId" IS NULL',
'"Item".created_at >= "SubSubscription".created_at'
)}
ORDER BY "sortTime" DESC
LIMIT ${LIMIT}+$3`
LIMIT ${LIMIT}`
)
// mentions
@ -129,11 +132,12 @@ export default {
FROM "Mention"
JOIN "Item" ON "Mention"."itemId" = "Item".id
${whereClause(
'"Item".created_at < $2',
'"Mention"."userId" = $1',
'"Item"."userId" <> $1'
)}
ORDER BY "sortTime" DESC
LIMIT ${LIMIT}+$3`
LIMIT ${LIMIT}`
)
}
// Inner union to de-dupe item-driven notifications
@ -145,7 +149,7 @@ export default {
${itemDrivenQueries.map(q => `(${q})`).join(' UNION ALL ')}
) as "Item"
${whereClause(
'"Item".created_at <= $2',
'"Item".created_at < $2',
await filterClause(me, models),
muteClause(me))}
ORDER BY id ASC, CASE
@ -163,9 +167,9 @@ export default {
FROM "Item"
WHERE "Item"."userId" = $1
AND "maxBid" IS NOT NULL
AND "statusUpdatedAt" <= $2 AND "statusUpdatedAt" <> created_at
AND "statusUpdatedAt" < $2 AND "statusUpdatedAt" <> created_at
ORDER BY "sortTime" DESC
LIMIT ${LIMIT}+$3)`
LIMIT ${LIMIT})`
)
// territory transfers
@ -186,12 +190,12 @@ export default {
FROM "Item"
JOIN "ItemAct" ON "ItemAct"."itemId" = "Item".id
WHERE "ItemAct"."userId" <> $1
AND "ItemAct".created_at <= $2
AND "ItemAct".created_at < $2
AND "ItemAct".act IN ('TIP', 'FEE')
AND "Item"."userId" = $1
GROUP BY "Item".id
ORDER BY "sortTime" DESC
LIMIT ${LIMIT}+$3)`
LIMIT ${LIMIT})`
)
}
@ -204,11 +208,11 @@ export default {
JOIN "ItemForward" ON "ItemForward"."itemId" = "Item".id AND "ItemForward"."userId" = $1
WHERE "ItemAct"."userId" <> $1
AND "Item"."userId" <> $1
AND "ItemAct".created_at <= $2
AND "ItemAct".created_at < $2
AND "ItemAct".act IN ('TIP')
GROUP BY "Item".id
ORDER BY "sortTime" DESC
LIMIT ${LIMIT}+$3)`
LIMIT ${LIMIT})`
)
}
@ -220,9 +224,9 @@ export default {
WHERE "Invoice"."userId" = $1
AND "confirmedAt" IS NOT NULL
AND "isHeld" IS NULL
AND created_at <= $2
AND created_at < $2
ORDER BY "sortTime" DESC
LIMIT ${LIMIT}+$3)`
LIMIT ${LIMIT})`
)
}
@ -232,10 +236,10 @@ export default {
'Invitification' AS type
FROM users JOIN "Invite" on users."inviteId" = "Invite".id
WHERE "Invite"."userId" = $1
AND users.created_at <= $2
AND users.created_at < $2
GROUP BY "Invite".id
ORDER BY "sortTime" DESC
LIMIT ${LIMIT}+$3)`
LIMIT ${LIMIT})`
)
queries.push(
`(SELECT users.id::text, users.created_at AS "sortTime", NULL as "earnedSats",
@ -243,37 +247,44 @@ export default {
FROM users
WHERE "users"."referrerId" = $1
AND "inviteId" IS NULL
AND users.created_at <= $2
LIMIT ${LIMIT}+$3)`
AND users.created_at < $2
ORDER BY "sortTime" DESC
LIMIT ${LIMIT})`
)
}
if (meFull.noteEarning) {
queries.push(
`SELECT min(id)::text, created_at AS "sortTime", FLOOR(sum(msats) / 1000) as "earnedSats",
`(SELECT min(id)::text, created_at AS "sortTime", FLOOR(sum(msats) / 1000) as "earnedSats",
'Earn' AS type
FROM "Earn"
WHERE "userId" = $1
AND created_at <= $2
GROUP BY "userId", created_at`
AND created_at < $2
GROUP BY "userId", created_at
ORDER BY "sortTime" DESC
LIMIT ${LIMIT})`
)
queries.push(
`SELECT min(id)::text, created_at AS "sortTime", FLOOR(sum(msats) / 1000) as "earnedSats",
`(SELECT min(id)::text, created_at AS "sortTime", FLOOR(sum(msats) / 1000) as "earnedSats",
'Revenue' AS type
FROM "SubAct"
WHERE "userId" = $1
AND type = 'REVENUE'
AND created_at <= $2
GROUP BY "userId", "subName", created_at`
AND created_at < $2
GROUP BY "userId", "subName", created_at
ORDER BY "sortTime" DESC
LIMIT ${LIMIT})`
)
}
if (meFull.noteCowboyHat) {
queries.push(
`SELECT id::text, updated_at AS "sortTime", 0 as "earnedSats", 'Streak' AS type
`(SELECT id::text, updated_at AS "sortTime", 0 as "earnedSats", 'Streak' AS type
FROM "Streak"
WHERE "userId" = $1
AND updated_at <= $2`
AND updated_at < $2
ORDER BY "sortTime" DESC
LIMIT ${LIMIT})`
)
}
@ -283,9 +294,9 @@ export default {
FROM "Sub"
WHERE "Sub"."userId" = $1
AND "status" <> 'ACTIVE'
AND "statusUpdatedAt" <= $2
AND "statusUpdatedAt" < $2
ORDER BY "sortTime" DESC
LIMIT ${LIMIT}+$3)`
LIMIT ${LIMIT})`
)
// we do all this crazy subquery stuff to make 'reward' islands
@ -306,8 +317,7 @@ export default {
) sub
GROUP BY type, island
ORDER BY "sortTime" DESC
OFFSET $3
LIMIT ${LIMIT}`, me.id, decodedCursor.time, decodedCursor.offset)
LIMIT ${LIMIT}`, me.id, decodedCursor.time)
if (decodedCursor.offset === 0) {
await models.user.update({ where: { id: me.id }, data: { checkedNotesAt: new Date() } })
@ -315,7 +325,7 @@ export default {
return {
lastChecked: meFull.checkedNotesAt,
cursor: notifications.length === LIMIT ? nextCursorEncoded(decodedCursor) : null,
cursor: notifications.length === LIMIT ? nextNoteCursorEncoded(decodedCursor, notifications) : null,
notifications
}
}

View File

@ -14,3 +14,10 @@ export function nextCursorEncoded (cursor, limit = LIMIT) {
cursor.offset += limit
return Buffer.from(JSON.stringify(cursor)).toString('base64')
}
export function nextNoteCursorEncoded (cursor, notifications = [], limit = LIMIT) {
// what we are looking for this oldest sort time for every table we are looking at
cursor.time = new Date(notifications.slice(-1).pop()?.sortTime ?? cursor.time)
cursor.offset += limit
return Buffer.from(JSON.stringify(cursor)).toString('base64')
}