ranking mostly

This commit is contained in:
keyan 2021-04-27 16:30:58 -05:00
parent c82c82bb7b
commit 84b69fc481
17 changed files with 159 additions and 55 deletions

View File

@ -8,7 +8,6 @@ import models from './models'
export default async function serverSideClient (req) {
const session = await getSession({ req })
console.log(session)
return new ApolloClient({
ssrMode: true,
// Instead of "createHttpLink" use SchemaLink here

View File

@ -6,14 +6,16 @@ export default {
return await models.$queryRaw(`
${SELECT}
FROM "Item"
WHERE "parentId" IS NULL`)
${LEFT_JOIN_SATS}
WHERE "parentId" IS NULL
${ORDER_BY_SATS}`)
},
recent: async (parent, args, { models }) => {
return await models.$queryRaw(`
${SELECT}
FROM "Item"
WHERE "parentId" IS NULL
ORDER BY created_at`)
ORDER BY created_at DESC`)
},
item: async (parent, { id }, { models }) => {
return (await models.$queryRaw(`
@ -82,6 +84,18 @@ export default {
throw new AuthenticationError('You must be logged in')
}
if (sats <= 0) {
throw new UserInputError('Sats must be positive', { argumentName: 'sats' })
}
// check if we've already voted for the item
const boosted = await models.vote.findFirst({
where: {
itemId: parseInt(id),
userId: me.id
}
})
const data = {
sats,
item: {
@ -93,7 +107,8 @@ export default {
connect: {
name: me.name
}
}
},
boost: !!boosted
}
await models.vote.create({ data })
@ -113,10 +128,17 @@ export default {
},
comments: async (item, args, { models }) => {
const flat = await models.$queryRaw(`
${SELECT}
FROM "Item"
WHERE path <@ (SELECT path FROM "Item" where id = ${item.id}) AND id != ${item.id}
ORDER BY "path"`)
WITH RECURSIVE base AS (
${SELECT}, ARRAY[row_number() OVER (${ORDER_BY_SATS}, "Item".path)] AS sort_path
FROM "Item"
${LEFT_JOIN_SATS}
WHERE "parentId" = ${item.id}
UNION ALL
${SELECT}, p.sort_path || row_number() OVER (${ORDER_BY_SATS}, "Item".path)
FROM base p
JOIN "Item" ON ltree2text(subpath("Item"."path", 0, -1)) = p."path"
${LEFT_JOIN_SATS})
SELECT * FROM base ORDER BY sort_path`)
return nestComments(flat, item.id)[0]
},
sats: async (item, args, { models }) => {
@ -125,7 +147,21 @@ export default {
sats: true
},
where: {
itemId: item.id
itemId: item.id,
boost: false
}
})
return sats
},
boost: async (item, args, { models }) => {
const { sum: { sats } } = await models.vote.aggregate({
sum: {
sats: true
},
where: {
itemId: item.id,
boost: true
}
})
@ -205,5 +241,14 @@ function nestComments (flat, parentId) {
// we have to do our own query because ltree is unsupported
const SELECT =
`SELECT id, created_at as "createdAt", updated_at as "updatedAt", title,
text, url, "userId", "parentId", ltree2text("path") AS "path"`
`SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
"Item".text, "Item".url, "Item"."userId", "Item"."parentId", ltree2text("Item"."path") AS "path"`
const LEFT_JOIN_SATS =
`LEFT JOIN (SELECT i.id, SUM("Vote".sats) as sats
FROM "Item" i
JOIN "Vote" ON i.id = "Vote"."itemId"
GROUP BY i.id) x ON "Item".id = x.id`
const ORDER_BY_SATS =
'ORDER BY (x.sats-1)/POWER(EXTRACT(EPOCH FROM ((NOW() AT TIME ZONE \'UTC\') - "Item".created_at))/3600+2, 1.5) DESC NULLS LAST'

View File

@ -3,7 +3,6 @@ export default {
me: async (parent, args, { models, me }) =>
me ? await models.user.findUnique({ where: { id: me.id } }) : null,
user: async (parent, { name }, { models }) => {
console.log(name)
return await models.user.findUnique({ where: { name } })
},
users: async (parent, args, { models }) =>
@ -17,7 +16,14 @@ export default {
ncomments: async (user, args, { models }) => {
return await models.item.count({ where: { userId: user.id, parentId: { not: null } } })
},
stacked: () => 0,
stacked: async (user, args, { models }) => {
const [{ sum }] = await models.$queryRaw`
SELECT sum("Vote".sats)
FROM "Item"
LEFT JOIN "Vote" on "Vote"."itemId" = "Item".id
WHERE "Item"."userId" = ${user.id}`
return sum
},
sats: () => 0
}
}

View File

@ -27,6 +27,7 @@ export default gql`
user: User!
depth: Int!
sats: Int!
boost: Int!
meSats: Int!
ncomments: Int!
comments: [Item!]!

View File

@ -60,6 +60,11 @@ export default function Comment ({ item, children, replyOpen, includeParent, cac
<span>{timeSince(new Date(item.createdAt))}</span>
<span> \ </span>
<span>{item.sats} sats</span>
{!!item.boost &&
<>
<span> \ </span>
<span>{item.boost} boost</span>
</>}
<span> \ </span>
<Link href={`/items/${item.id}`} passHref>
<a className='text-reset'>{item.ncomments} replies</a>

View File

@ -23,6 +23,11 @@ export default function Item ({ item, rank, children }) {
</div>
<div className={styles.other}>
<span>{item.sats} sats</span>
{!!item.boost &&
<>
<span> \ </span>
<span>{item.boost} boost</span>
</>}
<span> \ </span>
<Link href={`/items/${item.id}`} passHref>
<a className='text-reset'>{item.ncomments} comments</a>

View File

@ -31,6 +31,9 @@ export default function Reply ({ parentId, onSuccess }) {
fragmentName: 'CommentsRecursive'
})
return [newCommentRef, ...existingCommentRefs]
},
ncomments (existingNComments = 0) {
return existingNComments + 1
}
}
})

View File

@ -3,23 +3,21 @@ import UpArrow from '../svgs/lightning-arrow.svg'
import styles from './upvote.module.css'
import { gql, useMutation } from '@apollo/client'
export default function UpVote ({ itemId, meSats, className, item }) {
export default function UpVote ({ itemId, meSats, className }) {
const [vote] = useMutation(
gql`
mutation vote($id: ID!, $sats: Int!) {
vote(id: $id, sats: $sats)
}`, {
update (cache, { data: { vote } }) {
if (item) {
item.sats += vote
item.meSats += vote
return
}
cache.modify({
id: `Item:${itemId}`,
fields: {
sats (existingSats = 0) {
return existingSats + vote
return existingSats || vote
},
boost (existingBoost = 0) {
return meSats >= 1 ? existingBoost + vote : existingBoost
},
meSats (existingMeSats = 0) {
return existingMeSats + vote

View File

@ -10,6 +10,7 @@ export const COMMENT_FIELDS = gql`
name
}
sats
boost
meSats
ncomments
}

View File

@ -11,6 +11,7 @@ export const ITEM_FIELDS = gql`
name
}
sats
boost
meSats
ncomments
}`

View File

@ -7,12 +7,16 @@ import Comments, { CommentsSkeleton } from '../../components/comments'
import { COMMENTS } from '../../fragments/comments'
import { ITEM_FIELDS } from '../../fragments/items'
import { gql, useQuery } from '@apollo/client'
import { useRouter } from 'next/router'
export default function FullItem ({ item }) {
const router = useRouter()
const { id } = router.query
export async function getServerSideProps ({ params: { id } }) {
return {
props: {
id
}
}
}
export default function FullItem ({ id }) {
const query = gql`
${ITEM_FIELDS}
${COMMENTS}

View File

@ -17,7 +17,7 @@ export async function getServerSideProps ({ req, res, query: { callbackUrl, erro
Location: callbackUrl
})
res.end()
return
return { props: {} }
}
return {

View File

@ -1,26 +0,0 @@
-- DropIndex
DROP INDEX "item_gist_path_index";
-- CreateTable
CREATE TABLE "Vote" (
"id" SERIAL NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
"sats" INTEGER NOT NULL,
"itemId" INTEGER NOT NULL,
"userId" INTEGER NOT NULL,
PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "Vote.itemId_index" ON "Vote"("itemId");
-- CreateIndex
CREATE INDEX "Vote.userId_index" ON "Vote"("userId");
-- AddForeignKey
ALTER TABLE "Vote" ADD FOREIGN KEY ("itemId") REFERENCES "Item"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Vote" ADD FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -1,3 +0,0 @@
-- AlterTable
ALTER TABLE "Vote" ADD COLUMN "stimi" BOOLEAN NOT NULL DEFAULT false,
ALTER COLUMN "sats" SET DEFAULT 1;

View File

@ -20,6 +20,7 @@ CREATE TABLE "Message" (
PRIMARY KEY ("id")
);
-- Edit: create extension for path
CREATE EXTENSION ltree;
@ -63,6 +64,19 @@ CREATE TRIGGER path_tgr
BEFORE INSERT OR UPDATE ON "Item"
FOR EACH ROW EXECUTE PROCEDURE update_item_path();
-- CreateTable
CREATE TABLE "Vote" (
"id" SERIAL NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
"sats" INTEGER NOT NULL DEFAULT 1,
"boost" BOOLEAN NOT NULL DEFAULT false,
"itemId" INTEGER NOT NULL,
"userId" INTEGER NOT NULL,
PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "accounts" (
"id" SERIAL NOT NULL,
@ -117,6 +131,12 @@ CREATE INDEX "Item.userId_index" ON "Item"("userId");
-- CreateIndex
CREATE INDEX "Item.parentId_index" ON "Item"("parentId");
-- CreateIndex
CREATE INDEX "Vote.itemId_index" ON "Vote"("itemId");
-- CreateIndex
CREATE INDEX "Vote.userId_index" ON "Vote"("userId");
-- CreateIndex
CREATE UNIQUE INDEX "accounts.compound_id_unique" ON "accounts"("compound_id");
@ -146,3 +166,9 @@ ALTER TABLE "Item" ADD FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE
-- AddForeignKey
ALTER TABLE "Item" ADD FOREIGN KEY ("parentId") REFERENCES "Item"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Vote" ADD FOREIGN KEY ("itemId") REFERENCES "Item"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Vote" ADD FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;

39
prisma/scatch.sql Normal file
View File

@ -0,0 +1,39 @@
WITH RECURSIVE base AS (
SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
"Item".text, "Item".url, "Item"."userId", "Item"."parentId", ltree2text("Item"."path") AS "path",
ARRAY[row_number() OVER (ORDER BY (x.sats-1)/POWER(EXTRACT(EPOCH FROM ((NOW() AT TIME ZONE 'UTC') - "Item".created_at))/3600+2, 1.5) DESC NULLS LAST, "Item"."path")] AS sort_path
FROM "Item"
LEFT JOIN (SELECT i.id, SUM("Vote".sats) as sats
FROM "Item" i
JOIN "Vote" ON i.id = "Vote"."itemId"
WHERE i."parentId" IS NULL
GROUP BY i.id) x ON "Item".id = x.id
WHERE "parentId" IS NULL
UNION ALL
SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
"Item".text, "Item".url, "Item"."userId", "Item"."parentId", ltree2text("Item"."path") AS "path",
p.sort_path || row_number() OVER (ORDER BY (x.sats-1)/POWER(EXTRACT(EPOCH FROM ((NOW() AT TIME ZONE 'UTC') - "Item".created_at))/3600+2, 1.5) DESC NULLS LAST, "Item"."path")
FROM base p
JOIN "Item" ON ltree2text(subpath("Item"."path", 0, -1)) = p."path"
LEFT JOIN (SELECT i.id, SUM("Vote".sats) as sats
FROM "Item" i
JOIN "Vote" ON i.id = "Vote"."itemId"
WHERE i."parentId" IS NULL
GROUP BY i.id) x ON "Item".id = x.id
)
select * from base order by sort_path;
WITH RECURSIVE base AS (
SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
"Item".text, "Item".url, "Item"."userId", "Item"."parentId", ltree2text("Item"."path") AS "path",
ARRAY[row_number() OVER (ORDER BY "Item".created_at, "Item"."path")] AS sort_path
FROM "Item"
WHERE "parentId" IS NULL
UNION ALL
SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
"Item".text, "Item".url, "Item"."userId", "Item"."parentId", ltree2text("Item"."path") AS "path",
p.sort_path || row_number() OVER (ORDER BY "Item".created_at, "Item"."path")
FROM base p
JOIN "Item" ON ltree2text(subpath("Item"."path", 0, -1)) = p."path"
)
select * from base order by sort_path;

View File

@ -56,7 +56,7 @@ model Vote {
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
sats Int @default(1)
stimi Boolean @default(false)
boost Boolean @default(false)
item Item @relation(fields: [itemId], references: [id])
itemId Int
user User @relation(fields: [userId], references: [id])