You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Hydro/hydro/model/document.js

367 lines
9.9 KiB
JavaScript

const assert = require('assert');
const { ObjectID } = require('bson');
const db = require('../service/db');
const coll = db.collection('document');
const collStatus = db.collection('document.status');
const TYPE_DOMAIN_ROLE = 0;
const TYPE_PROBLEM = 10;
const TYPE_PROBLEM_SOLUTION = 11;
const TYPE_PROBLEM_LIST = 12;
const TYPE_DISCUSSION_NODE = 20;
const TYPE_DISCUSSION = 21;
const TYPE_DISCUSSION_REPLY = 22;
const TYPE_CONTEST = 30;
const TYPE_TRAINING = 40;
const TYPE_FILE = 50;
const TYPE_HOMEWORK = 60;
async function add(
domainId, content, owner, docType, docId = null,
parentType = null, parentId = null, args = {},
) {
const _id = new ObjectID();
const doc = {
content,
owner,
domainId,
docType,
docId: docId || _id,
...args,
};
if (parentType || parentId) {
assert(parentType && parentId);
doc.parentType = parentType;
doc.parentId = parentId;
}
const res = await coll.insertOne(doc);
return docId || res.insertedId;
}
function get(domainId, docType, docId) {
return coll.findOne({ domainId, docType, docId });
}
async function set(domainId, docType, docId, args) {
const res = await coll.findOneAndUpdate(
{ domainId, docType, docId },
{ $set: { args } },
{ returnOriginal: false },
);
return res.value;
}
function deleteOne(domainId, docType, docId) {
// TODO(twd2): delete status?
return coll.deleteOne({ domainId, docType, docId });
}
function deleteMulti(domainId, docType, args = {}) {
return coll.deleteMany({ ...args, domainId, docType });
}
function deleteMultiStatus(domainId, docType, args = {}) {
return collStatus.deleteMany({ ...args, domainId, docType });
}
function getMulti(domainId, docType, args = {}) {
return coll.find({ ...args, docType, domainId });
}
async function inc(domainId, docType, docId, key, value) {
const res = await coll.findOneAndUpdate(
{ domainId, docType, docId },
{ $inc: { [key]: value } },
{ returnOriginal: false },
);
return res.value;
}
async function incAndSet(domainId, docType, docId, key, value, args) {
const res = await coll.findOneAndUpdate(
{ domainId, docType, docId },
{ $inc: { [key]: value }, $set: args },
{ returnOriginal: false },
);
return res.value;
}
function count(domainId, docType, query) {
return coll.find({ ...query, docType, domainId }).count();
}
async function push(domainId, docType, docId, key, content, owner, args) {
const _id = new ObjectID();
const doc = await coll.findOneAndUpdate(
{ domainId, docType, docId },
{
$push: {
[key]: {
...args, content, owner, _id,
},
},
},
{ returnOriginal: false },
);
return [doc.value, _id];
}
async function pull(domainId, docType, docId, setKey, contents) {
const res = await coll.findOneAndUpdate(
{ domainId, docType, docId },
{ $pull: { [setKey]: { $in: contents } } },
{ returnOriginal: false },
);
return res.value;
}
async function deleteSub(domainId, docType, docId, key, subId) {
const res = await coll.findOneAndUpdate(
{ domainId, docType, docId },
{ $pull: { [key]: { _id: subId } } },
{ returnOriginal: false },
);
return res.value;
}
async function getSub(domainId, docType, docId, key, subId) {
const doc = await coll.findOne({
domainId,
docType,
docId,
[key]: { $elemMatch: { _id: subId } },
});
if (!doc) return [null, null];
for (const sdoc of doc[key] || []) {
if (sdoc._id === subId) {
return [doc, sdoc];
}
}
return [doc, null];
}
async function setSub(domainId, docType, docId, key, subId, args) {
const $set = {};
for (const k in args) {
$set[`${key}.$.${k}`] = args[k];
}
const res = await coll.findOneAndUpdate(
{
domainId,
docType,
docId,
[key]: { $elemMatch: { _id: subId } },
},
{ $set },
{ returnOriginal: false },
);
return res.value;
}
async function addToSet(domainId, docType, docId, setKey, content) {
const res = await coll.findOneAndUpdate(
{ domainId, docType, docId },
{ $addToSet: { [setKey]: content } },
{ returnOriginal: false },
);
return res.value;
}
function getStatus(domainId, docType, docId, uid) {
return collStatus.findOne({
domainId, docType, docId, uid,
});
}
function getMultiStatus(domainId, docType, args) {
return collStatus.find({ ...args, docType, domainId });
}
async function setStatus(domainId, docType, docId, uid, args) {
const res = await collStatus.findOneAndUpdate(
{
domainId, docType, docId, uid,
},
{ $set: args },
{ upsert: true, returnOriginal: false },
);
return res.value;
}
async function setIfNotStatus(domainId, docType, docId, uid, key, value, ifNot, args) {
const res = await collStatus.findOneAndUpdate(
{
domainId, docType, docId, uid, key: { $not: { $eq: ifNot } },
},
{ $set: { [key]: value, args } },
{ upsert: true, returnOriginal: false },
);
return res.value;
}
async function cappedIncStatus(
domainId, docType, docId, uid,
key, value, minValue = -1, maxValue = 1,
) {
assert(value !== 0);
const $not = value > 0 ? { $gte: maxValue } : { $lte: minValue };
const res = await collStatus.findOneAndUpdate(
{
domainId, docType, docId, uid, [key]: { $not },
},
{ $inc: { [key]: value } },
{ upsert: true, returnOriginal: false },
);
return res.value;
}
async function incStatus(domainId, docType, docId, uid, key, value) {
const res = await collStatus.findOneAndUpdate(
{
domainId, docType, docId, uid,
},
{ $inc: { [key]: value } },
{ upsert: true, returnOriginal: false },
);
return res.value;
}
async function revPushStatus(domainId, docType, docId, uid, key, value) {
const res = await collStatus.findOneAndUpdate(
{
domainId, docType, docId, uid,
},
{ $push: { [key]: value }, $inc: { rev: 1 } },
{ upsert: true, returnOriginal: false },
);
return res.value;
}
async function revInitStatus(domainId, docType, docId, uid) {
const res = await collStatus.findOneAndUpdate(
{
domainId, docType, docId, uid,
},
{ $inc: { rev: 1 } },
{ upsert: true, returnOriginal: false },
);
return res.value;
}
async function revSetStatus(domainId, docType, docId, uid, rev, args, returnDoc = true) {
const filter = {
domainId, docType, docId, uid, rev,
};
const update = { $set: args, $inc: { rev: 1 } };
if (returnDoc) {
const res = await coll.findOneAndUpdate(filter, update, { returnOriginal: false });
return res.value;
}
const res = await coll.updateOne(filter, update);
return res;
}
async function ensureIndexes() {
await coll.createIndex({ domainId: 1, docType: 1, docId: 1 }, { unique: true });
await coll.createIndex({
domainId: 1, docType: 1, owner: 1, docId: -1,
});
// For problem
await coll.createIndex({
domainId: 1, docType: 1, category: 1, docId: 1,
}, { sparse: true });
await coll.createIndex({
domainId: 1, docType: 1, hidden: 1, category: 1, docId: 1,
}, { sparse: true });
await coll.createIndex({
domainId: 1, docType: 1, tag: 1, docId: 1,
}, { sparse: true });
await coll.createIndex({
domainId: 1, docType: 1, hidden: 1, tag: 1, docId: 1,
}, { sparse: true });
// For problem solution
await coll.createIndex({
domainId: 1, docType: 1, parentType: 1, parentId: 1, vote: -1, docId: -1,
}, { sparse: true });
// For discussion
await coll.createIndex({
domainId: 1, docType: 1, updateAt: -1, docId: -1,
}, { sparse: true });
await coll.createIndex({
domainId: 1, docType: 1, parentType: 1, parentId: 1, updateAt: -1, docId: -1,
}, { sparse: true });
// Hidden doc
await coll.createIndex({
domainId: 1, docType: 1, hidden: 1, docId: -1,
}, { sparse: true });
// For contest
await coll.createIndex({ domainId: 1, docType: 1, pids: 1 }, { sparse: true });
await coll.createIndex({
domainId: 1, docType: 1, rule: 1, docId: -1,
}, { sparse: true });
// For training
await coll.createIndex({ domainId: 1, docType: 1, 'dag.pids': 1 }, { sparse: true });
await collStatus.createIndex({
domainId: 1, docType: 1, uid: 1, docId: 1,
}, { unique: true });
// For rp system
await collStatus.createIndex({
domainId: 1, docType: 1, docId: 1, status: 1, rid: 1, rp: 1,
}, { sparse: true });
// For contest rule OI
await collStatus.createIndex({
domainId: 1, docType: 1, docId: 1, score: -1,
}, { sparse: true });
// For contest rule ACM
await collStatus.createIndex({
domainId: 1, docType: 1, docId: 1, accept: -1, time: 1,
}, { sparse: true });
// For training
await collStatus.createIndex({
domainId: 1, docType: 1, uid: 1, enroll: 1, docId: 1,
}, { sparse: true });
}
global.Hydro.model.document = module.exports = {
add,
addToSet,
cappedIncStatus,
count,
deleteMulti,
deleteMultiStatus,
deleteOne,
deleteSub,
ensureIndexes,
get,
getMulti,
getMultiStatus,
getStatus,
getSub,
inc,
incAndSet,
incStatus,
pull,
push,
revInitStatus,
revPushStatus,
revSetStatus,
set,
setIfNotStatus,
setStatus,
setSub,
TYPE_DOMAIN_ROLE,
TYPE_CONTEST,
TYPE_DISCUSSION,
TYPE_DISCUSSION_NODE,
TYPE_DISCUSSION_REPLY,
TYPE_HOMEWORK,
TYPE_PROBLEM,
TYPE_PROBLEM_LIST,
TYPE_PROBLEM_SOLUTION,
TYPE_FILE,
TYPE_TRAINING,
};