De-dupe item-driven notifications (#457)
* De-dupe item-driven notifications Update how we query for item-driven notifications to de-dupe them for: 1. mentions 2. replies 3. thread subscriptions 4. user subscriptions users should recieve only 1 notification for any given item created by another user, following the above priority order this is accomplished by querying for replies to current user, replies on subscribed threads, items from subscribed users, and mentions (if enabled for the current user), sorting those results by item id and priority order within item id based on notification type, then selecting the highest priority notification from each item id these results are then union all'ed with all other notification types, unchanged. * remove extra union hashing --------- Co-authored-by: keyan <keyan.kousha+huumn@gmail.com>
This commit is contained in:
parent
1872d6a7ad
commit
906571324a
|
@ -71,17 +71,23 @@ export default {
|
||||||
|
|
||||||
const queries = []
|
const queries = []
|
||||||
|
|
||||||
queries.push(
|
const itemDrivenQueries = []
|
||||||
`(SELECT DISTINCT "Item".id::TEXT, "Item".created_at AS "sortTime", NULL::BIGINT as "earnedSats",
|
|
||||||
|
// Replies
|
||||||
|
itemDrivenQueries.push(
|
||||||
|
`SELECT DISTINCT "Item".id::TEXT, "Item".created_at AS "sortTime", NULL::BIGINT as "earnedSats",
|
||||||
'Reply' AS type
|
'Reply' AS type
|
||||||
FROM "Item"
|
FROM "Item"
|
||||||
JOIN "Item" p ON ${meFull.noteAllDescendants ? '"Item".path <@ p.path' : '"Item"."parentId" = p.id'}
|
JOIN "Item" p ON ${meFull.noteAllDescendants ? '"Item".path <@ p.path' : '"Item"."parentId" = p.id'}
|
||||||
WHERE p."userId" = $1 AND "Item"."userId" <> $1 AND "Item".created_at <= $2
|
WHERE p."userId" = $1 AND "Item"."userId" <> $1 AND "Item".created_at <= $2
|
||||||
${await filterClause(me, models)}
|
${await filterClause(me, models)}
|
||||||
ORDER BY "sortTime" DESC
|
ORDER BY "sortTime" DESC
|
||||||
LIMIT ${LIMIT}+$3)
|
LIMIT ${LIMIT}+$3`
|
||||||
UNION DISTINCT
|
)
|
||||||
(SELECT DISTINCT "Item".id::TEXT, "Item".created_at AS "sortTime", NULL::BIGINT as "earnedSats",
|
|
||||||
|
// Thread subscriptions
|
||||||
|
itemDrivenQueries.push(
|
||||||
|
`SELECT DISTINCT "Item".id::TEXT, "Item".created_at AS "sortTime", NULL::BIGINT as "earnedSats",
|
||||||
'Reply' AS type
|
'Reply' AS type
|
||||||
FROM "ThreadSubscription"
|
FROM "ThreadSubscription"
|
||||||
JOIN "Item" p ON "ThreadSubscription"."itemId" = p.id
|
JOIN "Item" p ON "ThreadSubscription"."itemId" = p.id
|
||||||
|
@ -89,14 +95,16 @@ export default {
|
||||||
WHERE
|
WHERE
|
||||||
"ThreadSubscription"."userId" = $1
|
"ThreadSubscription"."userId" = $1
|
||||||
AND "Item"."userId" <> $1 AND "Item".created_at <= $2
|
AND "Item"."userId" <> $1 AND "Item".created_at <= $2
|
||||||
|
-- Only show items that have been created since subscribing to the thread
|
||||||
|
AND "Item".created_at >= "ThreadSubscription".created_at
|
||||||
${await filterClause(me, models)}
|
${await filterClause(me, models)}
|
||||||
ORDER BY "sortTime" DESC
|
ORDER BY "sortTime" DESC
|
||||||
LIMIT ${LIMIT}+$3)`
|
LIMIT ${LIMIT}+$3`
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
queries.push(
|
// User subscriptions
|
||||||
`(SELECT DISTINCT "Item".id::TEXT, "Item".created_at AS "sortTime", NULL::BIGINT as "earnedSats",
|
itemDrivenQueries.push(
|
||||||
|
`SELECT DISTINCT "Item".id::TEXT, "Item".created_at AS "sortTime", NULL::BIGINT as "earnedSats",
|
||||||
'FollowActivity' AS type
|
'FollowActivity' AS type
|
||||||
FROM "Item"
|
FROM "Item"
|
||||||
JOIN "UserSubscription" ON "Item"."userId" = "UserSubscription"."followeeId"
|
JOIN "UserSubscription" ON "Item"."userId" = "UserSubscription"."followeeId"
|
||||||
|
@ -106,25 +114,40 @@ export default {
|
||||||
AND "Item".created_at >= "UserSubscription".created_at
|
AND "Item".created_at >= "UserSubscription".created_at
|
||||||
${await filterClause(me, models)}
|
${await filterClause(me, models)}
|
||||||
ORDER BY "sortTime" DESC
|
ORDER BY "sortTime" DESC
|
||||||
LIMIT ${LIMIT}+$3)`
|
LIMIT ${LIMIT}+$3`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// mentions
|
||||||
if (meFull.noteMentions) {
|
if (meFull.noteMentions) {
|
||||||
queries.push(
|
itemDrivenQueries.push(
|
||||||
`(SELECT "Item".id::TEXT, "Mention".created_at AS "sortTime", NULL as "earnedSats",
|
`SELECT "Item".id::TEXT, "Mention".created_at AS "sortTime", NULL as "earnedSats",
|
||||||
'Mention' AS type
|
'Mention' AS type
|
||||||
FROM "Mention"
|
FROM "Mention"
|
||||||
JOIN "Item" ON "Mention"."itemId" = "Item".id
|
JOIN "Item" ON "Mention"."itemId" = "Item".id
|
||||||
LEFT JOIN "Item" p ON "Item"."parentId" = p.id
|
|
||||||
WHERE "Mention"."userId" = $1
|
WHERE "Mention"."userId" = $1
|
||||||
AND "Mention".created_at <= $2
|
AND "Mention".created_at <= $2
|
||||||
AND "Item"."userId" <> $1
|
AND "Item"."userId" <> $1
|
||||||
AND (p."userId" IS NULL OR p."userId" <> $1)
|
|
||||||
${await filterClause(me, models)}
|
${await filterClause(me, models)}
|
||||||
ORDER BY "sortTime" DESC
|
ORDER BY "sortTime" DESC
|
||||||
LIMIT ${LIMIT}+$3)`
|
LIMIT ${LIMIT}+$3`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
// Inner union to de-dupe item-driven notifications
|
||||||
|
queries.push(
|
||||||
|
// Only record per item ID
|
||||||
|
`(SELECT DISTINCT ON (id) *
|
||||||
|
FROM (
|
||||||
|
SELECT *
|
||||||
|
FROM (
|
||||||
|
${itemDrivenQueries.map(q => `(${q})`).join(' UNION ALL ')}
|
||||||
|
) as inner_union
|
||||||
|
ORDER BY id ASC, CASE
|
||||||
|
WHEN type = 'Mention' THEN 1
|
||||||
|
WHEN type = 'Reply' THEN 2
|
||||||
|
WHEN type = 'FollowActivity' THEN 3
|
||||||
|
END ASC
|
||||||
|
) as ordered_unioned)`
|
||||||
|
)
|
||||||
|
|
||||||
queries.push(
|
queries.push(
|
||||||
`(SELECT "Item".id::text, "Item"."statusUpdatedAt" AS "sortTime", NULL as "earnedSats",
|
`(SELECT "Item".id::text, "Item"."statusUpdatedAt" AS "sortTime", NULL as "earnedSats",
|
||||||
|
|
Loading…
Reference in New Issue