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