improve bounty performance

This commit is contained in:
keyan 2023-01-26 13:09:57 -06:00
parent e13e37744e
commit 5306b11157
9 changed files with 49 additions and 39 deletions

View File

@ -920,27 +920,6 @@ export default {
return (msats && msatsToSats(msats)) || 0 return (msats && msatsToSats(msats)) || 0
}, },
bountyPaid: async (item, args, { models }) => {
if (!item.bounty) {
return null
}
// if there's a child where the OP paid the amount, but it isn't the OP's own comment
const paid = await models.$queryRaw`
-- Sum up the sats and if they are greater than or equal to item.bounty than return true, else return false
SELECT "Item"."id"
FROM "ItemAct"
JOIN "Item" ON "ItemAct"."itemId" = "Item"."id"
WHERE "ItemAct"."userId" = ${item.userId}
AND "Item".path <@ text2ltree (${item.path})
AND "Item"."userId" <> ${item.userId}
AND act IN ('TIP', 'FEE')
GROUP BY "Item"."id"
HAVING coalesce(sum("ItemAct"."msats"), 0) >= ${item.bounty * 1000}
`
return paid.length > 0
},
bountyPaidTo: async (item, args, { models }) => { bountyPaidTo: async (item, args, { models }) => {
if (!item.bounty) { if (!item.bounty) {
return [] return []
@ -951,7 +930,7 @@ export default {
FROM "ItemAct" FROM "ItemAct"
JOIN "Item" ON "ItemAct"."itemId" = "Item"."id" JOIN "Item" ON "ItemAct"."itemId" = "Item"."id"
WHERE "ItemAct"."userId" = ${item.userId} WHERE "ItemAct"."userId" = ${item.userId}
AND "Item".path <@ text2ltree (${item.path}) AND "Item"."rootId" = ${item.id}
AND "Item"."userId" <> ${item.userId} AND "Item"."userId" <> ${item.userId}
AND act IN ('TIP', 'FEE') AND act IN ('TIP', 'FEE')
GROUP BY "Item"."id" GROUP BY "Item"."id"
@ -990,13 +969,7 @@ export default {
if (!item.parentId) { if (!item.parentId) {
return null return null
} }
return (await models.$queryRaw(` return await models.item.findUnique({ where: { id: item.rootId } })
${SELECT}
FROM "Item"
WHERE id = (
SELECT ltree2text(subltree(path, 0, 1))::integer
FROM "Item"
WHERE id = $1)`, Number(item.id)))[0]
}, },
parent: async (item, args, { models }) => { parent: async (item, args, { models }) => {
if (!item.parentId) { if (!item.parentId) {
@ -1160,7 +1133,8 @@ function nestComments (flat, parentId) {
// we have to do our own query because ltree is unsupported // we have to do our own query because ltree is unsupported
export const SELECT = export const SELECT =
`SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title, `SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
"Item".text, "Item".url, "Item"."bounty", "Item"."userId", "Item"."fwdUserId", "Item"."parentId", "Item"."pinId", "Item"."maxBid", "Item".text, "Item".url, "Item"."bounty", "Item"."userId", "Item"."fwdUserId", "Item"."parentId",
"Item"."pinId", "Item"."maxBid", "Item"."rootId",
"Item".company, "Item".location, "Item".remote, "Item"."deletedAt", "Item".company, "Item".location, "Item".remote, "Item"."deletedAt",
"Item"."subName", "Item".status, "Item"."uploadId", "Item"."pollCost", "Item"."subName", "Item".status, "Item"."uploadId", "Item"."pollCost",
"Item".msats, "Item".ncomments, "Item"."commentMsats", "Item"."lastCommentAt", "Item"."weightedVotes", "Item".msats, "Item".ncomments, "Item"."commentMsats", "Item"."lastCommentAt", "Item"."weightedVotes",

View File

@ -90,7 +90,6 @@ export default gql`
mine: Boolean! mine: Boolean!
boost: Int! boost: Int!
bounty: Int bounty: Int
bountyPaid: Boolean
bountyPaidTo: [Int]! bountyPaidTo: [Int]!
sats: Int! sats: Int!
commentSats: Int! commentSats: Int!

View File

@ -101,7 +101,7 @@ function TopLevelItem ({ item, noReply, ...props }) {
{item.poll && <Poll item={item} />} {item.poll && <Poll item={item} />}
{item.bounty && {item.bounty &&
<div className='font-weight-bold mt-2 mb-3'> <div className='font-weight-bold mt-2 mb-3'>
{item.bountyPaid {item.bountyPaidTo?.length
? ( ? (
<div className='px-3 py-1 d-inline-block bg-grey-medium rounded text-success'> <div className='px-3 py-1 d-inline-block bg-grey-medium rounded text-success'>
<Check className='fill-success' /> {item.bounty} sats paid <Check className='fill-success' /> {item.bounty} sats paid

View File

@ -78,8 +78,8 @@ export default function Item ({ item, rank, showFwdUser, toc, children }) {
{item.pollCost && <span> <PollIcon className='fill-grey vertical-align-baseline' height={14} width={14} /></span>} {item.pollCost && <span> <PollIcon className='fill-grey vertical-align-baseline' height={14} width={14} /></span>}
{item.bounty > 0 && {item.bounty > 0 &&
<span> <span>
<ActionTooltip notForm overlayText={`${abbrNum(item.bounty)} ${item.bountyPaid ? 'sats paid' : 'sats bounty'}`}> <ActionTooltip notForm overlayText={`${abbrNum(item.bounty)} ${item.bountyPaidTo?.length ? 'sats paid' : 'sats bounty'}`}>
<BountyIcon className={`${styles.bountyIcon} ${item.bountyPaid ? 'fill-success vertical-align-middle' : 'fill-grey vertical-align-middle'}`} height={16} width={16} /> <BountyIcon className={`${styles.bountyIcon} ${item.bountyPaidTo?.length ? 'fill-success vertical-align-middle' : 'fill-grey vertical-align-middle'}`} height={16} width={16} />
</ActionTooltip> </ActionTooltip>
</span>} </span>}
</a> </a>

View File

@ -50,9 +50,6 @@ export default function PayBounty ({ children, item }) {
cache.modify({ cache.modify({
id: `Item:${item.root.id}`, id: `Item:${item.root.id}`,
fields: { fields: {
bountyPaid () {
return true
},
bountyPaidTo (existingPaidTo = []) { bountyPaidTo (existingPaidTo = []) {
return [...existingPaidTo, Number(item.id)] return [...existingPaidTo, Number(item.id)]
} }
@ -84,7 +81,7 @@ export default function PayBounty ({ children, item }) {
} }
} }
if (!me || item.root.user.name !== me.name || item.mine || item.root.bountyPaid) { if (item.mine || item.root.user.name !== me.name) {
return null return null
} }

View File

@ -21,7 +21,7 @@ export const ITEM_FIELDS = gql`
upvotes upvotes
boost boost
bounty bounty
bountyPaid bountyPaidTo
path path
meSats meSats
meDontLike meDontLike

View File

@ -0,0 +1,8 @@
-- AlterTable
ALTER TABLE "Item" ADD COLUMN "rootId" INTEGER;
-- CreateIndex
CREATE INDEX "Item.rootId_index" ON "Item"("rootId");
-- AddForeignKey
ALTER TABLE "Item" ADD FOREIGN KEY ("rootId") REFERENCES "Item"("id") ON DELETE SET NULL ON UPDATE CASCADE;

View File

@ -0,0 +1,28 @@
-- Store root ids of all existing items
UPDATE "Item"
SET "rootId" = ltree2text(subltree(path, 0, 1))::integer
WHERE "parentId" IS NOT NULL;
CREATE OR REPLACE FUNCTION update_item_path() RETURNS TRIGGER AS $$
DECLARE
npath ltree;
root_id INTEGER;
BEGIN
IF NEW."parentId" IS NULL THEN
SELECT NEW.id::text::ltree INTO npath;
NEW."path" = npath;
ELSEIF TG_OP = 'INSERT' OR OLD."parentId" IS NULL OR OLD."parentId" != NEW."parentId" THEN
SELECT "path" || NEW.id::text, ltree2text(subltree("path", 0, 1))::integer
FROM "Item"
WHERE id = NEW."parentId"
INTO npath, root_id;
IF npath IS NULL THEN
RAISE EXCEPTION 'Invalid parent_id %', NEW."parentId";
END IF;
NEW."path" = npath;
NEW."rootId" = root_id;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

View File

@ -222,6 +222,9 @@ model Item {
parent Item? @relation("ParentChildren", fields: [parentId], references: [id]) parent Item? @relation("ParentChildren", fields: [parentId], references: [id])
parentId Int? parentId Int?
children Item[] @relation("ParentChildren") children Item[] @relation("ParentChildren")
root Item? @relation("RootDescendant", fields: [rootId], references: [id])
rootId Int?
descendants Item[] @relation("RootDescendant")
actions ItemAct[] actions ItemAct[]
mentions Mention[] mentions Mention[]
path Unsupported("LTREE")? path Unsupported("LTREE")?
@ -280,6 +283,7 @@ model Item {
@@index([freebie]) @@index([freebie])
@@index([createdAt]) @@index([createdAt])
@@index([userId]) @@index([userId])
@@index([rootId])
@@index([parentId]) @@index([parentId])
@@index([status]) @@index([status])
@@index([maxBid]) @@index([maxBid])