Skip to content
This repository was archived by the owner on Aug 9, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ Thumbs.db
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

minio-data
minio-data
2 changes: 1 addition & 1 deletion src/routes/api/comment/+server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export const POST: RequestHandler = async ({
return json({ error: 'Invalid reposted lynt ID' }, { status: 400 });
}

let newId = (await db.insert(lynts).values(lyntValues).returning())[0].id || null;
let newId = (await db.insert(lynts).values(lyntValues).returning())[0].id || '';
let [newLynt] = await db
.select(lyntObj(userId))
.from(lynts)
Expand Down
26 changes: 5 additions & 21 deletions src/routes/api/comments/+server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { lynts, likes, users, followers } from '@/server/schema';
import { sql, desc, and, eq, not, exists } from 'drizzle-orm';

import { lyntObj } from '../util';
import { userRepliesQuery } from './userReplies';

export const GET: RequestHandler = async ({ url, cookies }) => {
const lyntId = url.searchParams.get('id');
Expand All @@ -29,26 +30,10 @@ export const GET: RequestHandler = async ({ url, cookies }) => {
const userId = jwtPayload.userId;

// First, get the comments that the user has replied to
const userReplies = await db
.select(lyntObj(userId))
.from(lynts)
.leftJoin(likes, eq(likes.lynt_id, lynts.id))
.leftJoin(users, eq(lynts.user_id, users.id))
.where(
and(
eq(lynts.parent, lyntId),
eq(lynts.reposted, false),
exists(
db
.select()
.from(lynts)
.where(and(eq(lynts.parent, lynts.id), eq(lynts.user_id, userId)))
)
)
)
.groupBy(lynts.id, users.id)
.orderBy(desc(lynts.created_at))
.execute();
const userReplies = await userRepliesQuery.execute({
user_id: userId,
lynt_id: lyntId
});

// Then, get the most liked comments
let mostLikedComments: {
Expand Down Expand Up @@ -90,7 +75,6 @@ export const GET: RequestHandler = async ({ url, cookies }) => {
.where(and(eq(lynts.parent, lyntId), eq(lynts.reposted, false), notInClause))
.groupBy(lynts.id, users.id)
.orderBy(desc(sql`count(distinct ${likes.user_id})`), desc(lynts.created_at))
// .limit(50 - userReplies.length)
.execute();
}
const comments = [...userReplies, ...mostLikedComments];
Expand Down
31 changes: 31 additions & 0 deletions src/routes/api/comments/userReplies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { db } from '@/server/db';
import { lynts, likes, users } from '@/server/schema';
import { eq, and, desc, sql, exists } from 'drizzle-orm';
import { lyntObj } from '../util';

// Prepared statement: user_replies
// Parameters:
const userId = sql.placeholder('user_id');
const lyntId = sql.placeholder('lynt_id');

// Query:
export const userRepliesQuery = db
.select(lyntObj())
.from(lynts)
.leftJoin(likes, eq(likes.lynt_id, lynts.id))
.leftJoin(users, eq(lynts.user_id, users.id))
.where(
and(
eq(lynts.parent, lyntId),
eq(lynts.reposted, false),
exists(
db
.select()
.from(lynts)
.where(and(eq(lynts.parent, lynts.id), eq(lynts.user_id, userId)))
)
)
)
.groupBy(lynts.id, users.id)
.orderBy(desc(lynts.created_at))
.prepare('user_replies');
47 changes: 27 additions & 20 deletions src/routes/api/feed/following.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
import { json } from '@sveltejs/kit';
import { db } from '@/server/db';
import { lynts, likes, users, followers, history } from '@/server/schema';
import { sql, desc, and, eq, not, exists, or, isNull } from 'drizzle-orm';
import { lyntObj } from '../util';

export async function followingFeed(userId: string) {
const feed = await db
.select(lyntObj(userId))
.from(lynts)
.leftJoin(likes, eq(likes.lynt_id, lynts.id))
.leftJoin(users, eq(lynts.user_id, users.id))
.where(
and(
or(isNull(lynts.parent), eq(lynts.reposted, true)),
exists(
db
.select()
.from(followers)
.where(and(eq(followers.user_id, lynts.user_id), eq(followers.follower_id, userId)))
)
// Prepared statement: following_feed
// Parameters:
const userId = sql.placeholder('user_id');

// Query:
const followingFeedQuery = db
.select(lyntObj())
.from(lynts)
.leftJoin(likes, eq(likes.lynt_id, lynts.id))
.leftJoin(users, eq(lynts.user_id, users.id))
.where(
and(
or(isNull(lynts.parent), eq(lynts.reposted, true)),
exists(
db
.select()
.from(followers)
.where(and(eq(followers.user_id, lynts.user_id), eq(followers.follower_id, userId)))
)
)
.groupBy(lynts.id, users.id)
.orderBy(desc(lynts.created_at))
.limit(100);
)
.groupBy(lynts.id, users.id)
.orderBy(desc(lynts.created_at))
.limit(100)
.prepare('following_feed');

return feed;
export async function followingFeed(userId: string) {
return await followingFeedQuery.execute({
user_id: userId
});
}
44 changes: 25 additions & 19 deletions src/routes/api/feed/handle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,32 @@ import { lynts, likes, users } from '@/server/schema';
import { sql, desc, and, eq, or, isNull } from 'drizzle-orm';
import { lyntObj } from '../util';

export async function handleFeed(handleUserId: string, userId: string) {
const totalLyntsResult = await db
.select({ count: sql<number>`count(*)` })
.from(lynts)
.where(eq(lynts.user_id, handleUserId));
// Prepared statement: handle_feed
// Parameters:
// - user_id from lyntObj
// - handle_user_id
const handleUserId = sql.placeholder('handle_user_id');

const feed = await db
.select(lyntObj(userId))
.from(lynts)
.leftJoin(likes, eq(likes.lynt_id, lynts.id))
.leftJoin(users, eq(lynts.user_id, users.id))
.where(
and(
eq(lynts.user_id, handleUserId),
or(and(eq(lynts.reposted, false), isNull(lynts.parent)), eq(lynts.reposted, true))
)
// Query:
const handleFeedQuery = db
.select(lyntObj())
.from(lynts)
.leftJoin(likes, eq(likes.lynt_id, lynts.id))
.leftJoin(users, eq(lynts.user_id, users.id))
.where(
and(
eq(lynts.user_id, handleUserId),
or(and(eq(lynts.reposted, false), isNull(lynts.parent)), eq(lynts.reposted, true))
)
.groupBy(lynts.id, users.id)
.orderBy(desc(lynts.created_at))
.limit(50);
)
.groupBy(lynts.id, users.id)
.orderBy(desc(lynts.created_at))
.limit(50)
.prepare('handle_feed');

return feed;
export async function handleFeed(handleUserId: string, userId: string) {
return await handleFeedQuery.execute({
handle_user_id: handleUserId,
user_id: userId
});
}
36 changes: 23 additions & 13 deletions src/routes/api/feed/liked.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import { db } from '@/server/db';
import { lynts, likes, users } from '@/server/schema';
import { desc, and, eq, exists, or, isNull } from 'drizzle-orm';
import { desc, and, eq, exists, or, isNull, sql } from 'drizzle-orm';
import { lyntObj } from '../util';

// Prepared statement: liked_feed
// Parameters:
const userId = sql.placeholder('user_id');

// Query:
const likedFeedQuery = db
.select({
...lyntObj(),
likedAt: likes.liked_at
})
.from(likes)
.innerJoin(lynts, eq(likes.lynt_id, lynts.id))
.innerJoin(users, eq(lynts.user_id, users.id))
.where(eq(likes.user_id, userId))
.orderBy(desc(likes.liked_at))
.limit(100)
.prepare('liked_feed');

export async function likedFeed(userId: string) {
const feed = await db
.select({
...lyntObj(userId),
likedAt: likes.liked_at
})
.from(likes)
.innerJoin(lynts, eq(likes.lynt_id, lynts.id))
.innerJoin(users, eq(lynts.user_id, users.id))
.where(eq(likes.user_id, userId))
.orderBy(desc(likes.liked_at))
.limit(100);
const feed = await likedFeedQuery.execute({
user_id: userId
});

return feed;
}
}
112 changes: 63 additions & 49 deletions src/routes/api/feed/main.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,80 @@
import { json } from '@sveltejs/kit';
import { db } from '@/server/db';
import { lynts, likes, users, followers, history } from '@/server/schema';
import { sql, desc, and, eq, exists, or, isNull, not, inArray, asc } from 'drizzle-orm';
import { lyntObj } from '../util';

export async function mainFeed(userId: string, limit = 20, excludePosts: string[] = []) {
const followedUsers = db
.select({ followedId: followers.follower_id })
.from(followers)
.where(eq(followers.user_id, userId));

const likeCounts = db
.select({
lyntId: likes.lynt_id,
likeCount: sql<number>`count(*)`.as('like_count')
})
.from(likes)
.groupBy(likes.lynt_id)
.as('like_counts');
// Prepared query: main_feed
// Parameters:
const userId = sql.placeholder('user_id');
const limit = sql.placeholder('limit');
const excludePosts = sql.placeholder('exclude_posts');

let whereConditions = and(
eq(users.banned, false),
or(isNull(lynts.parent), eq(lynts.reposted, true)),
sql`${lynts.created_at} > now() - interval '30 days'`
);
// Subqueries:
const followedUsers = db
.select({ followedId: followers.follower_id })
.from(followers)
.where(eq(followers.user_id, userId));

if (excludePosts.length > 0) {
whereConditions = and(whereConditions, not(inArray(lynts.id, excludePosts)));
}
const likeCounts = db
.select({
lyntId: likes.lynt_id,
likeCount: sql<number>`count(*)`.as('like_count')
})
.from(likes)
.groupBy(likes.lynt_id)
.as('like_counts');

const feed = await db
.select({
...lyntObj(userId),
isFollowed: inArray(lynts.user_id, followedUsers),
likeCount: sql<number>`coalesce(${likeCounts.likeCount}, 0)`,
isViewed: exists(
db
.select()
.from(history)
.where(and(eq(history.lynt_id, lynts.id), eq(history.user_id, userId)))
),
viewedAt: sql<Date>`(
// Query:
const feedQuery = db
.select({
...lyntObj(),
isFollowed: inArray(lynts.user_id, followedUsers),
likeCount: sql<number>`coalesce(${likeCounts.likeCount}, 0)`,
isViewed: exists(
db
.select()
.from(history)
.where(and(eq(history.lynt_id, lynts.id), eq(history.user_id, userId)))
),
viewedAt: sql<Date>`(
SELECT ${history.createdAt}
FROM ${history}
WHERE ${history.lynt_id} = ${lynts.id}
AND ${history.user_id} = ${userId}
)`
})
.from(lynts)
.leftJoin(users, eq(lynts.user_id, users.id))
.leftJoin(likeCounts, eq(lynts.id, likeCounts.lyntId))
.leftJoin(history, and(eq(history.lynt_id, lynts.id), eq(history.user_id, userId)))
.where(whereConditions)
.orderBy(
desc(sql`CASE WHEN ${history.id} IS NULL THEN 1 ELSE 0 END`),
desc(lynts.created_at),
desc(sql`CASE WHEN ${users.handle} = 'facedev' THEN 1 ELSE 0 END`),
desc(sql`CASE WHEN ${lynts.user_id} IN (${followedUsers}) THEN 1 ELSE 0 END`),
desc(sql`COALESCE(${likeCounts.likeCount}, 0)`),
desc(sql`CASE WHEN ${lynts.created_at} > now() - interval '24 hours' THEN 1 ELSE 0 END`)
})
.from(lynts)
.leftJoin(users, eq(lynts.user_id, users.id))
.leftJoin(likeCounts, eq(lynts.id, likeCounts.lyntId))
.leftJoin(history, and(eq(history.lynt_id, lynts.id), eq(history.user_id, userId)))
.where(
and(
eq(users.banned, false),
or(isNull(lynts.parent), eq(lynts.reposted, true)),
sql`${lynts.created_at} > now() - interval '30 days'`,

// If you try to use a placeholder and inArray, you will get a syntax error
// so we can use this hack
not(eq(lynts.id, sql`any(${excludePosts})`))
)
.limit(limit);
)
.orderBy(
desc(sql`CASE WHEN ${history.id} IS NULL THEN 1 ELSE 0 END`),
desc(lynts.created_at),
desc(sql`CASE WHEN ${users.handle} = 'facedev' THEN 1 ELSE 0 END`),
desc(sql`CASE WHEN ${lynts.user_id} IN (${followedUsers}) THEN 1 ELSE 0 END`),
desc(sql`COALESCE(${likeCounts.likeCount}, 0)`),
desc(sql`CASE WHEN ${lynts.created_at} > now() - interval '24 hours' THEN 1 ELSE 0 END`)
)
.limit(limit)
.prepare('main_feed');

export async function mainFeed(userId: string, limit = 20, excludePosts: string[] = []) {
const feed = await feedQuery.execute({
user_id: userId,
exclude_posts: excludePosts,
limit: limit
});

return feed;
}
Loading