mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
4 files changed
+27
-34
No files matched your search
@@ -40,16 +40,19 @@ global.Hydro.lib.problemSearch = async (domainId, q, opts) => {
|
|||||||
});
|
});
|
||||||
let hits = res.hits.hits.map((i) => i._id);
|
let hits = res.hits.hits.map((i) => i._id);
|
||||||
if (!opts.skip) {
|
if (!opts.skip) {
|
||||||
const pdoc = await ProblemModel.get(domainId, +q || q, ProblemModel.PROJECTION_LIST);
|
let pdoc = await ProblemModel.get(domainId, +q || q, ProblemModel.PROJECTION_LIST);
|
||||||
if (pdoc) {
|
if (pdoc) {
|
||||||
hits = hits.filter((i) => i !== `${pdoc.domainId}/${pdoc.docId}`);
|
hits = hits.filter((i) => i !== `${pdoc.domainId}/${pdoc.docId}`);
|
||||||
hits.unshift(`${pdoc.domainId}/${pdoc.docId}`);
|
hits.unshift(`${pdoc.domainId}/${pdoc.docId}`);
|
||||||
|
} else if (/^P\d+$/.test(q)) {
|
||||||
|
pdoc = await ProblemModel.get(domainId, +q.substring(1), ProblemModel.PROJECTION_LIST);
|
||||||
|
if (pdoc) hits.unshift(`${pdoc.domainId}/${pdoc.docId}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
countRelation: typeof res.hits.total === 'number' ? 'eq' : res.hits.total.relation,
|
countRelation: typeof res.hits.total === 'number' ? 'eq' : res.hits.total.relation,
|
||||||
total: typeof res.hits.total === 'number' ? res.hits.total : res.hits.total.value,
|
total: typeof res.hits.total === 'number' ? res.hits.total : res.hits.total.value,
|
||||||
hits,
|
hits: Array.from(new Set(hits)),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import AdmZip from 'adm-zip';
|
import AdmZip from 'adm-zip';
|
||||||
import { readFile, statSync } from 'fs-extra';
|
import { readFile, statSync } from 'fs-extra';
|
||||||
import {
|
import {
|
||||||
escapeRegExp, flattenDeep, intersection, pick, uniqBy,
|
escapeRegExp, flattenDeep, intersection, pick,
|
||||||
} from 'lodash';
|
} from 'lodash';
|
||||||
import { Filter, ObjectId } from 'mongodb';
|
import { Filter, ObjectId } from 'mongodb';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
@@ -34,7 +34,6 @@ import user from '../model/user';
|
|||||||
import {
|
import {
|
||||||
Handler, param, post, query, route, Types,
|
Handler, param, post, query, route, Types,
|
||||||
} from '../service/server';
|
} from '../service/server';
|
||||||
import { buildProjection } from '../utils';
|
|
||||||
import { ContestDetailBaseHandler } from './contest';
|
import { ContestDetailBaseHandler } from './contest';
|
||||||
|
|
||||||
export const parseCategory = (value: string) => value.replace(/,/g, ',').split(',').map((e) => e.trim());
|
export const parseCategory = (value: string) => value.replace(/,/g, ',').split(',').map((e) => e.trim());
|
||||||
@@ -53,16 +52,21 @@ function buildQuery(udoc: User) {
|
|||||||
|
|
||||||
const defaultSearch = async (domainId: string, q: string, options?: ProblemSearchOptions) => {
|
const defaultSearch = async (domainId: string, q: string, options?: ProblemSearchOptions) => {
|
||||||
const escaped = escapeRegExp(q.toLowerCase());
|
const escaped = escapeRegExp(q.toLowerCase());
|
||||||
|
const projection: (keyof ProblemDoc)[] = ['domainId', 'docId', 'pid'];
|
||||||
const $regex = new RegExp(q.length >= 2 ? escaped : `\\A${escaped}`, 'gmi');
|
const $regex = new RegExp(q.length >= 2 ? escaped : `\\A${escaped}`, 'gmi');
|
||||||
const filter = { $or: [{ pid: { $regex } }, { title: { $regex } }, { tag: q }] };
|
const filter = { $or: [{ pid: { $regex } }, { title: { $regex } }, { tag: q }] };
|
||||||
const pdocs = await problem.getMulti(domainId, filter, ['domainId', 'docId', 'pid'])
|
const pdocs = await problem.getMulti(domainId, filter, projection)
|
||||||
.skip(options.skip || 0).limit(options.limit || system.get('pagination.problem')).toArray();
|
.skip(options.skip || 0).limit(options.limit || system.get('pagination.problem')).toArray();
|
||||||
if (!options.skip) {
|
if (!options.skip) {
|
||||||
const pdoc = await problem.get(domainId, +q || q, ['domainId', 'docId', 'pid', 'title']);
|
let pdoc = await problem.get(domainId, Number.isSafeInteger(+q) ? +q : q, projection);
|
||||||
if (pdoc) pdocs.unshift(pdoc);
|
if (pdoc) pdocs.unshift(pdoc);
|
||||||
|
else if (/^P\d+$/.test(q)) {
|
||||||
|
pdoc = await problem.get(domainId, +q.substring(1), projection);
|
||||||
|
if (pdoc) pdocs.unshift(pdoc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
hits: pdocs.map((i) => `${i.domainId}/${i.docId}`),
|
hits: Array.from(new Set(pdocs.map((i) => `${i.domainId}/${i.docId}`))),
|
||||||
total: Math.max(pdocs.length, await problem.count(domainId, filter)),
|
total: Math.max(pdocs.length, await problem.count(domainId, filter)),
|
||||||
countRelation: 'eq',
|
countRelation: 'eq',
|
||||||
};
|
};
|
||||||
@@ -95,7 +99,8 @@ export class ProblemMainHandler extends Handler {
|
|||||||
@param('q', Types.Content, true)
|
@param('q', Types.Content, true)
|
||||||
@param('limit', Types.PositiveInt, true)
|
@param('limit', Types.PositiveInt, true)
|
||||||
@param('pjax', Types.Boolean)
|
@param('pjax', Types.Boolean)
|
||||||
async get(domainId: string, page = 1, q = '', limit: number, pjax = false) {
|
@param('quick', Types.Boolean)
|
||||||
|
async get(domainId: string, page = 1, q = '', limit: number, pjax = false, quick = false) {
|
||||||
this.response.template = 'problem_main.html';
|
this.response.template = 'problem_main.html';
|
||||||
if (!limit || limit > this.ctx.setting.get('pagination.problem') || page > 1) limit = this.ctx.setting.get('pagination.problem');
|
if (!limit || limit > this.ctx.setting.get('pagination.problem') || page > 1) limit = this.ctx.setting.get('pagination.problem');
|
||||||
this.queryContext.query = buildQuery(this.user);
|
this.queryContext.query = buildQuery(this.user);
|
||||||
@@ -137,7 +142,11 @@ export class ProblemMainHandler extends Handler {
|
|||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
let [pdocs, ppcount, pcount] = this.queryContext.fail
|
let [pdocs, ppcount, pcount] = this.queryContext.fail
|
||||||
? [[], 0, 0]
|
? [[], 0, 0]
|
||||||
: await problem.list(domainId, query, sort.length ? 1 : page, limit, undefined, this.user._id);
|
: await problem.list(
|
||||||
|
domainId, query, sort.length ? 1 : page, limit,
|
||||||
|
quick ? ['title', 'pid', 'domainId', 'docId'] : undefined,
|
||||||
|
this.user._id,
|
||||||
|
);
|
||||||
if (total) {
|
if (total) {
|
||||||
pcount = total;
|
pcount = total;
|
||||||
ppcount = Math.ceil(total / limit);
|
ppcount = Math.ceil(total / limit);
|
||||||
@@ -995,28 +1004,6 @@ export class ProblemCreateHandler extends Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProblemPrefixListHandler extends Handler {
|
|
||||||
@param('prefix', Types.Name)
|
|
||||||
async get(domainId: string, prefix: string) {
|
|
||||||
const projection = ['domainId', 'docId', 'pid', 'title', 'hidden'] as const;
|
|
||||||
const [pdocs, pdoc, apdoc] = await Promise.all([
|
|
||||||
problem.getPrefixList(domainId, prefix),
|
|
||||||
problem.get(domainId, Number.isSafeInteger(+prefix) ? +prefix : prefix, projection),
|
|
||||||
/^P\d+$/.test(prefix) ? problem.get(domainId, +prefix.substring(1), projection) : Promise.resolve(null),
|
|
||||||
]);
|
|
||||||
if (apdoc) pdocs.unshift(apdoc);
|
|
||||||
if (pdoc) pdocs.unshift(pdoc);
|
|
||||||
if (pdocs.length < 20) {
|
|
||||||
const search = global.Hydro.lib.problemSearch || defaultSearch;
|
|
||||||
const result = await search(domainId, prefix, { limit: 20 - pdocs.length });
|
|
||||||
const docs = await problem.getMulti(domainId, { docId: { $in: result.hits.map((i) => +i.split('/')[1]) } })
|
|
||||||
.project<ProblemDoc>(buildProjection(projection)).toArray();
|
|
||||||
pdocs.push(...docs);
|
|
||||||
}
|
|
||||||
this.response.body = uniqBy(pdocs, 'docId');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function apply(ctx: Context) {
|
export async function apply(ctx: Context) {
|
||||||
ctx.Route('problem_main', '/p', ProblemMainHandler, PERM.PERM_VIEW_PROBLEM);
|
ctx.Route('problem_main', '/p', ProblemMainHandler, PERM.PERM_VIEW_PROBLEM);
|
||||||
ctx.Route('problem_random', '/problem/random', ProblemRandomHandler, PERM.PERM_VIEW_PROBLEM);
|
ctx.Route('problem_random', '/problem/random', ProblemRandomHandler, PERM.PERM_VIEW_PROBLEM);
|
||||||
@@ -1033,7 +1020,6 @@ export async function apply(ctx: Context) {
|
|||||||
ctx.Route('problem_solution_reply_raw', '/p/:pid/solution/:psid/:psrid/raw', ProblemSolutionRawHandler, PERM.PERM_VIEW_PROBLEM);
|
ctx.Route('problem_solution_reply_raw', '/p/:pid/solution/:psid/:psrid/raw', ProblemSolutionRawHandler, PERM.PERM_VIEW_PROBLEM);
|
||||||
ctx.Route('problem_statistics', '/p/:pid/stat', ProblemStatisticsHandler, PERM.PERM_VIEW_PROBLEM);
|
ctx.Route('problem_statistics', '/p/:pid/stat', ProblemStatisticsHandler, PERM.PERM_VIEW_PROBLEM);
|
||||||
ctx.Route('problem_create', '/problem/create', ProblemCreateHandler, PERM.PERM_CREATE_PROBLEM);
|
ctx.Route('problem_create', '/problem/create', ProblemCreateHandler, PERM.PERM_CREATE_PROBLEM);
|
||||||
ctx.Route('problem_prefix_list', '/problem/list', ProblemPrefixListHandler, PERM.PERM_VIEW_PROBLEM);
|
|
||||||
ctx.inject(['api'], ({ api }) => {
|
ctx.inject(['api'], ({ api }) => {
|
||||||
api.value('FileInfo', [
|
api.value('FileInfo', [
|
||||||
['_id', 'String!'],
|
['_id', 'String!'],
|
||||||
|
|||||||
@@ -162,7 +162,8 @@ export class ProblemModel {
|
|||||||
if (Number.isSafeInteger(+pid)) pid = +pid;
|
if (Number.isSafeInteger(+pid)) pid = +pid;
|
||||||
const res = typeof pid === 'number'
|
const res = typeof pid === 'number'
|
||||||
? await document.get(domainId, document.TYPE_PROBLEM, pid, projection)
|
? await document.get(domainId, document.TYPE_PROBLEM, pid, projection)
|
||||||
: (await document.getMulti(domainId, document.TYPE_PROBLEM, { sort: sortable(pid), pid }).toArray())[0];
|
: (await document.getMulti(domainId, document.TYPE_PROBLEM, { sort: sortable(pid), pid })
|
||||||
|
.project(buildProjection(projection)).limit(1).toArray())[0];
|
||||||
if (!res) return null;
|
if (!res) return null;
|
||||||
try {
|
try {
|
||||||
if (!rawConfig) res.config = await parseConfig(res.config);
|
if (!rawConfig) res.config = await parseConfig(res.config);
|
||||||
|
|||||||
+4
-1
@@ -8,7 +8,10 @@ const ProblemSelectAutoComplete = forwardRef<AutoCompleteHandle<ProblemDoc>, Aut
|
|||||||
<AutoComplete<ProblemDoc>
|
<AutoComplete<ProblemDoc>
|
||||||
ref={ref as any}
|
ref={ref as any}
|
||||||
cacheKey={`problem-${UiContext.domainId}`}
|
cacheKey={`problem-${UiContext.domainId}`}
|
||||||
queryItems={(query) => request.get(`/d/${UiContext.domainId}/problem/list`, { prefix: query })}
|
queryItems={async (query) => {
|
||||||
|
const { pdocs } = await request.get(`/d/${UiContext.domainId}/p`, { q: query, quick: true });
|
||||||
|
return pdocs;
|
||||||
|
}}
|
||||||
fetchItems={(ids) => api(gql`
|
fetchItems={(ids) => api(gql`
|
||||||
problems(ids: ${ids.map((i) => +i)}) {
|
problems(ids: ${ids.map((i) => +i)}) {
|
||||||
docId
|
docId
|
||||||
|
|||||||
Reference in new issue
Block a user