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
},
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 }) => {
if (!item.bounty) {
return []
@ -951,7 +930,7 @@ export default {
FROM "ItemAct"
JOIN "Item" ON "ItemAct"."itemId" = "Item"."id"
WHERE "ItemAct"."userId" = ${item.userId}
AND "Item".path <@ text2ltree (${item.path})
AND "Item"."rootId" = ${item.id}
AND "Item"."userId" <> ${item.userId}
AND act IN ('TIP', 'FEE')
GROUP BY "Item"."id"
@ -990,13 +969,7 @@ export default {
if (!item.parentId) {
return null
}
return (await models.$queryRaw(`
${SELECT}
FROM "Item"
WHERE id = (
SELECT ltree2text(subltree(path, 0, 1))::integer
FROM "Item"
WHERE id = $1)`, Number(item.id)))[0]
return await models.item.findUnique({ where: { id: item.rootId } })
},
parent: async (item, args, { models }) => {
if (!item.parentId) {
@ -1160,7 +1133,8 @@ function nestComments (flat, parentId) {
// we have to do our own query because ltree is unsupported
export const SELECT =
`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"."subName", "Item".status, "Item"."uploadId", "Item"."pollCost",
"Item".msats, "Item".ncomments, "Item"."commentMsats", "Item"."lastCommentAt", "Item"."weightedVotes",

View File

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

View File

@ -101,7 +101,7 @@ function TopLevelItem ({ item, noReply, ...props }) {
{item.poll && <Poll item={item} />}
{item.bounty &&
<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'>
<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.bounty > 0 &&
<span>
<ActionTooltip notForm overlayText={`${abbrNum(item.bounty)} ${item.bountyPaid ? 'sats paid' : 'sats bounty'}`}>
<BountyIcon className={`${styles.bountyIcon} ${item.bountyPaid ? 'fill-success vertical-align-middle' : 'fill-grey vertical-align-middle'}`} height={16} width={16} />
<ActionTooltip notForm overlayText={`${abbrNum(item.bounty)} ${item.bountyPaidTo?.length ? 'sats paid' : 'sats bounty'}`}>
<BountyIcon className={`${styles.bountyIcon} ${item.bountyPaidTo?.length ? 'fill-success vertical-align-middle' : 'fill-grey vertical-align-middle'}`} height={16} width={16} />
</ActionTooltip>
</span>}
</a>

View File

@ -50,9 +50,6 @@ export default function PayBounty ({ children, item }) {
cache.modify({
id: `Item:${item.root.id}`,
fields: {
bountyPaid () {
return true
},
bountyPaidTo (existingPaidTo = []) {
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
}

View File

@ -21,7 +21,7 @@ export const ITEM_FIELDS = gql`
upvotes
boost
bounty
bountyPaid
bountyPaidTo
path
meSats
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])
parentId Int?
children Item[] @relation("ParentChildren")
root Item? @relation("RootDescendant", fields: [rootId], references: [id])
rootId Int?
descendants Item[] @relation("RootDescendant")
actions ItemAct[]
mentions Mention[]
path Unsupported("LTREE")?
@ -280,6 +283,7 @@ model Item {
@@index([freebie])
@@index([createdAt])
@@index([userId])
@@index([rootId])
@@index([parentId])
@@index([status])
@@index([maxBid])