Skip to content

Commit

Permalink
cache queries to speedup play
Browse files Browse the repository at this point in the history
  • Loading branch information
geoffrey-wu committed Dec 12, 2022
1 parent 34fee46 commit 40e0526
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 70 deletions.
85 changes: 52 additions & 33 deletions server/Room.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ class Room {
this.tossup = {};
this.wordIndex = 0;

this.randomQuestionCache = [];
this.setCache = [];

this.query = {
difficulties: [4, 5],
packetNumbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
questionType: 'tossup',
setName: '2022 PACE NSC',
categories: [],
subcategories: []
subcategories: [],
reverse: true // used for `database.getSet`
};

this.settings = {
Expand Down Expand Up @@ -150,7 +154,12 @@ class Room {
}

if (type === 'difficulties') {
this.adjustQuery(userId, 'difficulties', message.value);
this.sendSocketMessage({
type: 'difficulties',
username: this.players[userId].username,
value: message.value
});
this.adjustQuery(['difficulties'], [message.value]);
}

if (type === 'give-answer') {
Expand All @@ -168,8 +177,7 @@ class Room {
}

if (type === 'packet-number') {
this.query.packetNumbers = message.value;
this.questionNumber = 0;
this.adjustQuery(['packetNumbers'], [message.value]);
this.sendSocketMessage({
type: 'packet-number',
username: this.players[userId].username,
Expand All @@ -191,13 +199,13 @@ class Room {
}

if (type === 'set-name') {
this.query.setName = message.value;
this.questionNumber = 0;
this.sendSocketMessage({
type: 'set-name',
username: this.players[userId].username,
value: this.query.setName
value: message.value
});

this.adjustQuery(['setName'], [message.value]);
}

if (type === 'toggle-rebuzz') {
Expand All @@ -210,15 +218,14 @@ class Room {
}

if (type === 'toggle-select-by-set-name') {
this.settings.selectBySetName = message.selectBySetName;
this.query.setName = message.setName;
this.questionNumber = 0;
this.sendSocketMessage({
type: 'toggle-select-by-set-name',
selectBySetName: this.settings.selectBySetName,
selectBySetName: message.selectBySetName,
setName: this.query.setName,
username: this.players[userId].username
});
this.settings.selectBySetName = message.selectBySetName;
this.adjustQuery(['setName'], [message.setName]);
}

if (type === 'toggle-visibility') {
Expand All @@ -231,27 +238,39 @@ class Room {
}

if (type === 'update-categories') {
this.query.categories = message.categories;
this.query.subcategories = message.subcategories;
this.sendSocketMessage({
type: 'update-categories',
categories: this.query.categories,
subcategories: this.query.subcategories,
categories: message.categories,
subcategories: message.subcategories,
username: this.players[userId].username
});
this.adjustQuery(['categories', 'subcategories'], [message.categories, message.subcategories]);
}
}

adjustQuery(userId, setting, value) {
if (Object.prototype.hasOwnProperty.call(this.query, setting)) {
this.query[setting] = value;
adjustQuery(settings, values) {
if (settings.length !== values.length) {
return;
}

this.sendSocketMessage({
type: setting,
username: this.players[userId].username,
value: value
});
for (let i = 0; i < settings.length; i++) {
const setting = settings[i];
const value = values[i];
if (Object.prototype.hasOwnProperty.call(this.query, setting)) {
this.query[setting] = value;
}
}

if (this.settings.selectBySetName) {
this.questionNumber = 0;
database.getSet(this.query).then(set => {
this.setCache = set;
});
} else {
database.getRandomQuestions(this.query).then(tossups => {
this.randomQuestionCache = tossups;
});
}
}

async advanceQuestion() {
Expand All @@ -261,25 +280,25 @@ class Room {
this.paused = false;

if (this.settings.selectBySetName) {
this.tossup = await database.getNextQuestion(
this.query.setName,
this.query.packetNumbers,
this.questionNumber,
this.query.categories,
this.query.subcategories
);
if (Object.keys(this.tossup).length === 0) {
if (this.setCache.length === 0) {
this.setCache = await database.getSet(this.query);
}

if (this.setCache.length === 0) {
this.sendSocketMessage({
type: 'end-of-set'
});
return false;
} else {
this.tossup = this.setCache.pop();
this.questionNumber = this.tossup.questionNumber;
this.query.packetNumbers = this.query.packetNumbers.filter(packetNumber => packetNumber >= this.tossup.packetNumber);
}
} else {
this.tossup = await database.getRandomQuestions(this.query);
this.tossup = this.tossup[0];
if (this.randomQuestionCache.length === 0) {
this.randomQuestionCache = await database.getRandomQuestions(this.query);
}
this.tossup = this.randomQuestionCache.pop();
if (Object.keys(this.tossup).length === 0) {
this.sendSocketMessage({
type: 'no-questions-found'
Expand Down
63 changes: 26 additions & 37 deletions server/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ function escapeRegExp(string) {
* Gets the next question with a question number greater than `currentQuestionNumber` that satisfies the given conditions.
* @param {String} setName - the name of the set (e.g. "2021 ACF Fall").
* @param {Array<Number>} packetNumbers - an array of packet numbers to search. Each packet number is 1-indexed.
* @param {Number} currentQuestionNumber - current question number. **Starts at 1.**
* @param {Array<String>} validCategories
* @param {Array<String>} validSubcategories
* @param {Array<String>} categories
* @param {Array<String>} subcategories
* @param {'tossup' | 'bonus'} type - Type of question you want to get. Default: `'tossup'`.
* @param {Boolean} alwaysUseUnformattedAnswer - whether to always use the unformatted answer. Default: `false`
* @param {Boolean} reverse - whether to reverse the order of the questions. Useful for functions that pop at the end of the array, Default: `false`
* @returns {Promise<JSON>}
*/
async function getNextQuestion(setName, packetNumbers, currentQuestionNumber, validCategories, validSubcategories, type = 'tossup', alwaysUseUnformattedAnswer = false) {
async function getSet({ setName, packetNumbers, categories, subcategories, type = 'tossup', alwaysUseUnformattedAnswer = false, reverse = false }) {
if (setName === '') {
return 0;
}
Expand All @@ -57,43 +57,32 @@ async function getNextQuestion(setName, packetNumbers, currentQuestionNumber, va
return 0;
}

if (validCategories.length === 0) validCategories = CATEGORIES;
if (validSubcategories.length === 0) validSubcategories = SUBCATEGORIES_FLATTENED;

const question = await questions.findOne({
$or: [
{
setName: setName,
category: { $in: validCategories },
subcategory: { $in: validSubcategories },
packetNumber: packetNumbers[0],
questionNumber: { $gt: currentQuestionNumber },
type: type
},
{
setName: setName,
category: { $in: validCategories },
subcategory: { $in: validSubcategories },
packetNumber: { $in: packetNumbers.slice(1) },
type: type
},
]
if (categories.length === 0) categories = CATEGORIES;
if (subcategories.length === 0) subcategories = SUBCATEGORIES_FLATTENED;

const questionArray = await questions.find({
setName: setName,
category: { $in: categories },
subcategory: { $in: subcategories },
packetNumber: { $in: packetNumbers },
type: type
}, {
sort: { packetNumber: 1, questionNumber: 1 }
}).catch(error => {
console.log('[DATABASE] ERROR:', error);
return {};
});
sort: { packetNumber: reverse ? -1 : 1, questionNumber: reverse ? -1 : 1 }
}).toArray();

if (!question) {
if (!questionArray) {
return {};
}

if (!alwaysUseUnformattedAnswer && Object.prototype.hasOwnProperty.call(question, 'formatted_answer')) {
question.answer = question.formatted_answer;
if (!alwaysUseUnformattedAnswer && type === 'tossup') {
for (let i = 0; i < questionArray.length; i++) {
if (questionArray[i].formatted_answer) {
questionArray[i].answer = questionArray[i].formatted_answer;
}
}
}

return question || {};
return questionArray || [];
}


Expand Down Expand Up @@ -301,10 +290,10 @@ function getRandomName() {
* @param {Array<Number>} difficulties - an array of allowed difficulty levels (1-10). Pass a 0-length array to select any difficulty.
* @param {Array<String>} categories - an array of allowed categories. Pass a 0-length array to select any category.
* @param {Array<String>} subcategories - an array of allowed subcategories. Pass a 0-length array to select any subcategory.
* @param {Number} number - how many random tossups to return
* @param {Number} number - how many random tossups to return. Default: 20.
* @returns {Promise<Array<JSON>>}
*/
async function getRandomQuestions({ questionType = 'tossup', difficulties = DIFFICULTIES, categories = CATEGORIES, subcategories = SUBCATEGORIES_FLATTENED, number = 1 }) {
async function getRandomQuestions({ questionType = 'tossup', difficulties = DIFFICULTIES, categories = CATEGORIES, subcategories = SUBCATEGORIES_FLATTENED, number = 20 }) {
if (difficulties.length === 0) difficulties = DIFFICULTIES;
if (categories.length === 0) categories = CATEGORIES;
if (subcategories.length === 0) subcategories = SUBCATEGORIES_FLATTENED;
Expand Down Expand Up @@ -354,4 +343,4 @@ async function reportQuestion(_id, reason, description) {
}


module.exports = { DEFAULT_QUERY_RETURN_LENGTH, getNextQuestion, getNumPackets, getPacket, getQuery, getRandomQuestions, getSetList, getRandomName, reportQuestion };
module.exports = { DEFAULT_QUERY_RETURN_LENGTH, getSet, getNumPackets, getPacket, getQuery, getRandomQuestions, getSetList, getRandomName, reportQuestion };

0 comments on commit 40e0526

Please sign in to comment.