Merge pull request #887 from hydro-dev/remove-union

This commit is contained in:
undefined authored and GitHub committed 2024-10-23 14:18:28 +01:00
commit 965b18d8a6
12 files changed
+88 -108

No files matched your search

+3 -4
View File
@@ -142,10 +142,9 @@ export class ProblemMainHandler extends Handler {
// eslint-disable-next-line prefer-const
let [pdocs, ppcount, pcount] = this.queryContext.fail
? [[], 0, 0]
: await problem.list(
domainId, query, sort.length ? 1 : page, limit,
quick ? ['title', 'pid', 'domainId', 'docId'] : undefined,
this.user._id,
: await this.paginate(
problem.getMulti(domainId, query, quick ? ['title', 'pid', 'domainId', 'docId'] : undefined).sort({ sort: 1, docId: 1 }),
sort.length ? 1 : page, limit,
);
if (total) {
pcount = total;
+8 -11
View File
@@ -358,25 +358,22 @@ class UserDetailHandler extends Handler {
async get(domainId: string, uid: number) {
if (uid === 0) throw new UserNotFoundError(0);
const isSelfProfile = this.user._id === uid;
const [udoc, sdoc, union] = await Promise.all([
const [udoc, sdoc] = await Promise.all([
user.getById(domainId, uid),
token.getMostRecentSessionByUid(uid, ['createAt', 'updateAt']),
domain.get(domainId),
]);
if (!udoc) throw new UserNotFoundError(uid);
const pdocs: ProblemDoc[] = [];
const acInfo: Record<string, number> = {};
const canViewHidden = this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN) || this.user._id;
if (this.user.hasPerm(PERM.PERM_VIEW_PROBLEM)) {
await Promise.all([domainId, ...(union?.union || [])].map(async (did) => {
const psdocs = await problem.getMultiStatus(did, { uid, status: STATUS.STATUS_ACCEPTED }).toArray();
pdocs.push(...Object.values(
await problem.getList(
did, psdocs.map((i) => i.docId), canViewHidden,
false, problem.PROJECTION_LIST, true,
),
));
}));
const psdocs = await problem.getMultiStatus(domainId, { uid, status: STATUS.STATUS_ACCEPTED }).toArray();
pdocs.push(...Object.values(
await problem.getList(
domainId, psdocs.map((i) => i.docId), canViewHidden,
false, problem.PROJECTION_LIST, true,
),
));
}
for (const pdoc of pdocs) {
for (const tag of pdoc.tag) {
+18 -30
View File
@@ -16,6 +16,7 @@ import type {
} from '../interface';
import { parseConfig } from '../lib/testdataConfig';
import * as bus from '../service/bus';
import db from '../service/db';
import {
ArrayKeys, MaybeArray, NumberKeys, Projection,
} from '../typeutils';
@@ -27,14 +28,15 @@ import RecordModel from './record';
import SolutionModel from './solution';
import storage from './storage';
import * as SystemModel from './system';
import user from './user';
export interface ProblemDoc extends Document { }
export type Field = keyof ProblemDoc;
const logger = new Logger('problem');
function sortable(source: string) {
return source.replace(/(\d+)/g, (str) => (str.length >= 6 ? str : ('0'.repeat(6 - str.length) + str)));
function sortable(source: string, namespaces: Record<string, string>) {
const [namespace, pid] = source.includes('-') ? source.split('-') : ['default', source];
return ((namespaces ? `${namespaces[namespace]}-` : '') + pid)
.replace(/(\d+)/g, (str) => (str.length >= 6 ? str : ('0'.repeat(6 - str.length) + str)));
}
function findOverrideContent(dir: string, base: string) {
@@ -149,8 +151,9 @@ export class ProblemModel {
content: string, owner: number, tag: string[] = [],
meta: ProblemCreateOptions = {},
) {
const ddoc = await DomainModel.get(domainId);
const args: Partial<ProblemDoc> = {
title, tag, hidden: meta.hidden || false, nSubmit: 0, nAccept: 0, sort: sortable(pid || `P${docId}`),
title, tag, hidden: meta.hidden || false, nSubmit: 0, nAccept: 0, sort: sortable(pid || `P${docId}`, ddoc?.namespaces),
};
if (pid) args.pid = pid;
if (meta.difficulty) args.difficulty = meta.difficulty;
@@ -171,9 +174,10 @@ export class ProblemModel {
rawConfig = false,
): Promise<ProblemDoc | null> {
if (Number.isSafeInteger(+pid)) pid = +pid;
const ddoc = await DomainModel.get(domainId);
const res = typeof pid === 'number'
? await document.get(domainId, document.TYPE_PROBLEM, pid, projection)
: (await document.getMulti(domainId, document.TYPE_PROBLEM, { sort: sortable(pid), pid })
: (await document.getMulti(domainId, document.TYPE_PROBLEM, { sort: sortable(pid, ddoc?.namespaces), pid })
.project(buildProjection(projection)).limit(1).toArray())[0];
if (!res) return null;
try {
@@ -188,33 +192,16 @@ export class ProblemModel {
return document.getMulti(domainId, document.TYPE_PROBLEM, query, projection).sort({ sort: 1 });
}
/** @deprecated */
static async list(
domainId: string, query: Filter<ProblemDoc>,
page: number, pageSize: number,
projection = ProblemModel.PROJECTION_LIST, uid?: number,
projection = ProblemModel.PROJECTION_LIST,
): Promise<[ProblemDoc[], number, number]> {
const union = await DomainModel.get(domainId);
const domainIds = [domainId, ...(union.union || [])];
let count = 0;
const pdocs = [];
for (const id of domainIds) {
// TODO enhance performance
if (typeof uid === 'number') {
// eslint-disable-next-line no-await-in-loop
const udoc = await user.getById(id, uid);
if (!udoc.hasPerm(PERM.PERM_VIEW_PROBLEM)) continue;
}
// eslint-disable-next-line no-await-in-loop
const ccount = await document.count(id, document.TYPE_PROBLEM, query);
if (pdocs.length < pageSize && (page - 1) * pageSize - count <= ccount) {
// eslint-disable-next-line no-await-in-loop
pdocs.push(...await document.getMulti(id, document.TYPE_PROBLEM, query, projection)
.sort({ sort: 1, docId: 1 })
.skip(Math.max((page - 1) * pageSize - count, 0)).limit(pageSize - pdocs.length).toArray());
}
count += ccount;
}
return [pdocs, Math.ceil(count / pageSize), count];
return await db.paginate(
document.getMulti(domainId, document.TYPE_PROBLEM, query, projection).sort({ sort: 1, docId: 1 }),
page, pageSize,
);
}
static getStatus(domainId: string, docId: number, uid: number) {
@@ -227,11 +214,12 @@ export class ProblemModel {
static async edit(domainId: string, _id: number, $set: Partial<ProblemDoc>): Promise<ProblemDoc> {
const delpid = $set.pid === '';
const ddoc = await DomainModel.get(domainId);
if (delpid) {
delete $set.pid;
$set.sort = sortable(`P${_id}`);
$set.sort = sortable(`P${_id}`, ddoc.namespaces);
} else if ($set.pid) {
$set.sort = sortable($set.pid);
$set.sort = sortable($set.pid, ddoc.namespaces);
}
await bus.parallel('problem/before-edit', $set);
const result = await document.set(domainId, document.TYPE_PROBLEM, _id, $set, delpid ? { pid: '' } : undefined);
+13
View File
@@ -123,6 +123,19 @@ export class StorageModel {
return await storage.signDownloadLink(res.value?.link || res.value?._id || target, filename, noExpire, useAlternativeEndpointFor);
}
static async move(src: string, dst: string) {
const res = await StorageModel.coll.findOneAndUpdate(
{ path: src, autoDelete: null },
{ $set: { path: dst } },
);
return !!res.value;
}
static async exists(path: string) {
const value = await StorageModel.coll.findOne({ path, autoDelete: null });
return !!value;
}
static async copy(src: string, dst: string) {
const { value } = await StorageModel.coll.findOneAndUpdate(
{ path: src, autoDelete: null },
+1 -3
View File
@@ -136,14 +136,12 @@ export async function calcLevel(domainId: string, report: Function) {
}
async function runInDomain(domainId: string, report: Function) {
const info = await domain.get(domainId);
const domainIds = [domainId, ...(info.union || [])];
const results: Record<keyof typeof RpTypes, ND> = {};
const udict = Counter();
await db.collection('domain.user').updateMany({ domainId }, { $set: { rpInfo: {} } });
for (const type in RpTypes) {
results[type] = new Proxy({}, { get: (self, key) => self[key] || RpTypes[type].base });
await RpTypes[type].run(domainIds, results[type], report);
await RpTypes[type].run([domainId], results[type], report);
const bulk = db.collection('domain.user').initializeUnorderedBulkOp();
for (const uid in results[type]) {
const udoc = await UserModel.getById(domainId, +uid);
+3 -2
View File
@@ -32,6 +32,7 @@ declare module '@hydrooj/framework' {
domain: DomainDoc;
paginate<T>(cursor: FindCursor<T>, page: number, key: string): Promise<[docs: T[], numPages: number, count: number]>;
paginate<T>(cursor: FindCursor<T>, page: number, limit: number): Promise<[docs: T[], numPages: number, count: number]>;
progress(message: string, params: any[]): void;
limitRate(op: string, periodSecs: number, maxOperations: number, defaultKey?: string): Promise<void>;
renderTitle(str: string): string;
@@ -237,8 +238,8 @@ export async function apply(ctx: Context) {
: str.toString().translate(...this.context.acceptsLanguages(), system.get('server.language'));
return res;
},
paginate<T>(cursor: FindCursor<T>, page: number, key: string) {
return db.paginate(cursor, page, this.ctx.setting.get(`pagination.${key}`) || 20);
paginate<T>(cursor: FindCursor<T>, page: number, key: string | number) {
return db.paginate(cursor, page, typeof key === 'number' ? key : (this.ctx.setting.get(`pagination.${key}`) || 20));
},
checkPerm(...args: bigint[]) {
if (!this.user.hasPerm(...args)) {