get public reactions from scylla

This commit is contained in:
Namekuji 2023-08-27 13:51:47 -04:00
parent f399a230f3
commit a4b5ab42bf
No known key found for this signature in database
GPG key ID: 1D62332C07FBA532
5 changed files with 69 additions and 13 deletions

View file

@ -151,10 +151,10 @@ export const scyllaQueries = {
("id", "noteId", "userId", "reaction", "emoji", "createdAt")
VALUES (?, ?, ?, ?, ?, ?)`,
select: {
byNoteId: `SELECT * FROM reaction_by_id WHERE "noteId" IN ?`,
byUserId: `SELECT * FROM reaction_by_user_id WHERE "userId" IN ?`,
byNoteId: `SELECT * FROM reaction_by_id WHERE "noteId" = ?`,
byUserId: `SELECT * FROM reaction_by_user_id WHERE "userId" = ?`,
byNoteAndUser: `SELECT * FROM reaction WHERE "noteId" IN ? AND "userId" IN ?`,
byId: `SELECT * FROM reaction WHERE "id" IN ?`,
byId: `SELECT * FROM reaction WHERE "id" = ?`,
},
delete: `DELETE FROM reaction WHERE "noteId" = ? AND "userId" = ?`,
},

View file

@ -262,7 +262,8 @@ export type FeedType =
| "user"
| "channel"
| "notification"
| "list";
| "list"
| "reaction";
export function parseScyllaReaction(row: types.Row): ScyllaNoteReaction {
return {
@ -310,6 +311,9 @@ export function preparePaginationQuery(
case "notification":
queryParts.push(prepared.notification.select.byTargetId);
break;
case "reaction":
queryParts.push(prepared.reaction.select.byUserId);
break;
default:
queryParts.push(prepared.note.select.byDate);
}
@ -359,17 +363,19 @@ export async function execPaginationQuery(
},
filter?: {
note?: (_: ScyllaNote[]) => Promise<ScyllaNote[]>;
reaction?: (_: ScyllaNoteReaction[]) => Promise<ScyllaNoteReaction[]>;
notification?: (_: ScyllaNotification[]) => ScyllaNotification[];
},
userId?: User["id"],
maxPartitions = config.scylla?.sparseTimelineDays ?? 14,
): Promise<ScyllaNote[] | ScyllaNotification[]> {
): Promise<ScyllaNote[] | ScyllaNotification[] | ScyllaNoteReaction[]> {
if (!scyllaClient) return [];
switch (kind) {
case "home":
case "user":
case "notification":
case "reaction":
if (!userId) throw new Error(`Feed ${kind} needs userId`);
break;
case "renotes":
@ -387,7 +393,7 @@ export async function execPaginationQuery(
let { query, untilDate, sinceDate } = preparePaginationQuery(kind, ps);
let scannedPartitions = 0;
const found: (ScyllaNote | ScyllaNotification)[] = [];
const found: ScyllaNote[] | ScyllaNotification[] | ScyllaNoteReaction[] = [];
const queryLimit = config.scylla?.queryLimit ?? 1000;
let foundLimit = ps.limit;
if (kind === "list" && ps.userIds) {
@ -399,7 +405,7 @@ export async function execPaginationQuery(
const params: (Date | string | string[] | number)[] = [];
if (kind === "home" && userId) {
params.push(userId, untilDate, untilDate);
} else if (kind === "user" && userId) {
} else if (["user", "reaction"].includes(kind) && userId) {
params.push(userId, untilDate);
} else if (kind === "renotes" && ps.noteId) {
params.push(ps.noteId, untilDate);
@ -429,15 +435,19 @@ export async function execPaginationQuery(
if (result.rowLength > 0) {
if (kind === "notification") {
const notifications = result.rows.map(parseScyllaNotification);
found.push(
(found as ScyllaNotification[]).push(
...(filter?.notification
? filter.notification(notifications)
: notifications),
);
untilDate = notifications[notifications.length - 1].createdAt;
} else if (kind === "reaction") {
const reactions = result.rows.map(parseScyllaReaction);
(found as ScyllaNoteReaction[]).push(...(filter?.reaction ? await filter.reaction(reactions) : reactions));
untilDate = reactions[reactions.length - 1].createdAt;
} else {
const notes = result.rows.map(parseScyllaNote);
found.push(...(filter?.note ? await filter.note(notes) : notes));
(found as ScyllaNote[]).push(...(filter?.note ? await filter.note(notes) : notes));
untilDate = notes[notes.length - 1].createdAt;
}
}
@ -454,6 +464,8 @@ export async function execPaginationQuery(
if (kind === "notification") {
return found as ScyllaNotification[];
} else if (kind === "reaction") {
return found as ScyllaNoteReaction[];
}
return found as ScyllaNote[];

View file

@ -39,9 +39,9 @@ export const NoteReactionRepository = db.getRepository(NoteReaction).extend({
async packMany(
src: NoteReaction[],
me?: { id: User["id"] } | null | undefined,
me?: { id: User["id"] } | null,
options?: {
withNote: booleam;
withNote: boolean;
},
): Promise<Packed<"NoteReaction">[]> {
const reactions = await Promise.allSettled(

View file

@ -73,7 +73,7 @@ export default define(meta, paramDef, async (ps, user) => {
let reactions: NoteReaction[] = [];
if (scyllaClient) {
const scyllaQuery = [prepared.reaction.select.byNoteId]
const params: (string | string[] | number)[] = [[ps.noteId]];
const params: (string | string[] | number)[] = [ps.noteId];
if (ps.type) {
scyllaQuery.push(`AND "reaction" = ?`);
params.push(query.reaction as string)

View file

@ -3,6 +3,15 @@ import define from "../../define.js";
import { makePaginationQuery } from "../../common/make-pagination-query.js";
import { generateVisibilityQuery } from "../../common/generate-visibility-query.js";
import { ApiError } from "../../error.js";
import {
type ScyllaNoteReaction,
execPaginationQuery,
filterVisibility,
parseScyllaNote,
prepared,
scyllaClient,
} from "@/db/scylla.js";
import { LocalFollowingsCache } from "@/misc/cache.js";
export const meta = {
tags: ["users", "reactions"],
@ -49,10 +58,45 @@ export const paramDef = {
export default define(meta, paramDef, async (ps, me) => {
const profile = await UserProfiles.findOneByOrFail({ userId: ps.userId });
if (me.id !== ps.userId && !profile.publicReactions) {
if (me?.id !== ps.userId && !profile.publicReactions) {
throw new ApiError(meta.errors.reactionsNotPublic);
}
if (scyllaClient) {
let followingUserIds: string[] = [];
if (me) {
followingUserIds = await LocalFollowingsCache.init(me.id).then((cache) =>
cache.getAll(),
);
}
const filter = async (reactions: ScyllaNoteReaction[]) => {
if (!scyllaClient) return reactions;
let noteIds = reactions.map(({ noteId }) => noteId);
if (me) {
const notes = await scyllaClient
.execute(prepared.note.select.byIds, [noteIds], { prepare: true })
.then((result) => result.rows.map(parseScyllaNote));
const filteredNoteIds = await filterVisibility(
notes,
me,
followingUserIds,
).then((notes) => notes.map(({ id }) => id));
noteIds = noteIds.filter((id) => filteredNoteIds.includes(id));
}
return reactions.filter((reaction) => noteIds.includes(reaction.noteId));
};
const foundReactions = (await execPaginationQuery(
"reaction",
ps,
{ reaction: filter },
ps.userId,
)) as ScyllaNoteReaction[];
return await NoteReactions.packMany(foundReactions, me, { withNote: true });
}
const query = makePaginationQuery(
NoteReactions.createQueryBuilder("reaction"),
ps.sinceId,