import { Brackets } from "typeorm"; import { Followings, Notes } from "../../../models/index.js"; import { Cache } from "../../../misc/cache.js"; import { apiLogger } from "../logger.js"; export const cache = new Cache("homeTlQueryData", 60 * 60 * 24); const cutoff = 250; // 250 posts in the last 7 days, constant determined by comparing benchmarks for cutoff values between 100 and 2500 const logger = apiLogger.createSubLogger("heuristics"); export async function generateFollowingQuery(q, me) { const followingQuery = Followings.createQueryBuilder("following").select("following.followeeId").where("following.followerId = :meId"); const heuristic = await cache.fetch(me.id, async ()=>{ let curr = new Date(); let prev = new Date(); prev.setDate(prev.getDate() - 7); return Notes.createQueryBuilder('note').where(`note.createdAt > :prev`, { prev }).andWhere(`note.createdAt < :curr`, { curr }).andWhere(new Brackets((qb)=>{ qb.where(`note.userId IN (${followingQuery.getQuery()})`); qb.orWhere(`note.userId = :meId`, { meId: me.id }); })).getCount().then((res)=>{ logger.info(`Calculating heuristics for user ${me.id} took ${new Date().getTime() - curr.getTime()}ms`); return res; }); }); const shouldUseUnion = heuristic < cutoff; q.andWhere(new Brackets((qb)=>{ if (shouldUseUnion) { qb.where(`note.userId = ANY(array(${followingQuery.getQuery()} UNION ALL VALUES (:meId)))`); } else { qb.where(`note.userId = :meId`); qb.orWhere(`note.userId IN (${followingQuery.getQuery()})`); } })); q.setParameters({ meId: me.id }); }