mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 15:17:25 +01:00
Improve the performance of some frequently used API calls (#15251)
* use lean for getting task lists * Only load necessary user data for group-plans call Also don’t make a db request for groups if the user is in none * Only load necessary user fields for in app rewards * Optimize updateStore by not checking every item * Only load necessary user data for task scoring * improve performance of inbox request calls * merge fix * fix scoring task call * add quests to scoring call * fix showing official pinned items * also load achievements
This commit is contained in:
@@ -49,16 +49,6 @@ function populateGear (key, klass, type, index, item) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
each(GEAR_TYPES, type => {
|
|
||||||
const allGearTypes = CLASSES.concat(['base', 'special', 'mystery', 'armoire']);
|
|
||||||
each(allGearTypes, klass => {
|
|
||||||
each(gear[type][klass], (item, index) => {
|
|
||||||
const key = `${type}_${klass}_${index}`;
|
|
||||||
populateGear(key, klass, type, index, item);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function buildFlatList () {
|
function buildFlatList () {
|
||||||
/*
|
/*
|
||||||
The gear is exported as a tree (defined above), and a flat list
|
The gear is exported as a tree (defined above), and a flat list
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ function getTranslatedClassName (classType, language) {
|
|||||||
shops.checkMarketGearLocked = function checkMarketGearLocked (user, items) {
|
shops.checkMarketGearLocked = function checkMarketGearLocked (user, items) {
|
||||||
const result = filter(items, ['pinType', 'marketGear']);
|
const result = filter(items, ['pinType', 'marketGear']);
|
||||||
const officialPinnedItems = getOfficialPinnedItems(user);
|
const officialPinnedItems = getOfficialPinnedItems(user);
|
||||||
const availableGear = map(updateStore(user), item => getItemInfo(user, 'marketGear', item, officialPinnedItems).path);
|
const availableGear = map(updateStore(user, result.map(item => item.key)), item => getItemInfo(user, 'marketGear', item, officialPinnedItems).path);
|
||||||
const { pinnedSets } = seasonalShopConfig();
|
const { pinnedSets } = seasonalShopConfig();
|
||||||
for (const gear of result) {
|
for (const gear of result) {
|
||||||
if (gear.klass !== user.stats.class) {
|
if (gear.klass !== user.stats.class) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const sortOrder = reduce(content.gearTypes, (accumulator, val, key) => {
|
|||||||
return accumulator;
|
return accumulator;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
export default function updateStore (user) {
|
export default function updateStore (user, items) {
|
||||||
let changes = [];
|
let changes = [];
|
||||||
|
|
||||||
each(content.gearTypes, type => {
|
each(content.gearTypes, type => {
|
||||||
@@ -26,6 +26,7 @@ export default function updateStore (user) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
changes = changes.concat(filter(content.gear.flat, val => {
|
changes = changes.concat(filter(content.gear.flat, val => {
|
||||||
|
if (items && items.indexOf(val.key) === -1) return false;
|
||||||
if (['special', 'mystery', 'armoire'].indexOf(val.klass) !== -1 && !user.items.gear.owned[val.key] && (val.canOwn ? val.canOwn(user) : false)) {
|
if (['special', 'mystery', 'armoire'].indexOf(val.klass) !== -1 && !user.items.gear.owned[val.key] && (val.canOwn ? val.canOwn(user) : false)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1299,12 +1299,17 @@ api.removeGroupManager = {
|
|||||||
api.getGroupPlans = {
|
api.getGroupPlans = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: '/group-plans',
|
url: '/group-plans',
|
||||||
middlewares: [authWithHeaders()],
|
middlewares: [authWithHeaders({ userFieldsToInclude: ['guilds', 'party._id'] })],
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
|
|
||||||
const userGroups = user.getGroups();
|
const userGroups = user.getGroups();
|
||||||
|
|
||||||
|
if (userGroups.length === 0) {
|
||||||
|
res.respond(200, []);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const groups = await Group
|
const groups = await Group
|
||||||
.find({
|
.find({
|
||||||
_id: { $in: userGroups },
|
_id: { $in: userGroups },
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const api = {};
|
|||||||
api.getInboxMessages = {
|
api.getInboxMessages = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: '/inbox/messages',
|
url: '/inbox/messages',
|
||||||
middlewares: [authWithHeaders()],
|
middlewares: [authWithHeaders({ userFieldsToInclude: ['profile', 'contributor', 'backer', 'inbox'] })],
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const { page } = req.query;
|
const { page } = req.query;
|
||||||
|
|||||||
@@ -751,7 +751,7 @@ api.updateTask = {
|
|||||||
api.scoreTask = {
|
api.scoreTask = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/tasks/:taskId/score/:direction',
|
url: '/tasks/:taskId/score/:direction',
|
||||||
middlewares: [authWithHeaders()],
|
middlewares: [authWithHeaders({ userFieldsToInclude: ['stats', 'guilds', 'items.equipped', 'items.eggs', 'items.food', 'items.hatchingPotions', 'items.lastDrop', 'items.quests', 'achievements', 'tasksOrder', 'webhooks', 'party'] })],
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
// Parameters are validated in scoreTasks
|
// Parameters are validated in scoreTasks
|
||||||
|
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ api.getBuyList = {
|
|||||||
*/
|
*/
|
||||||
api.getInAppRewardsList = {
|
api.getInAppRewardsList = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
middlewares: [authWithHeaders()],
|
middlewares: [authWithHeaders({ userFieldsToInclude: ['items', 'pinnedItems', 'unpinnedItems', 'pinnedItemsOrder', 'stats.class', 'achievements'] })],
|
||||||
url: '/user/in-app-rewards',
|
url: '/user/in-app-rewards',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const list = common.inAppRewards(res.locals.user);
|
const list = common.inAppRewards(res.locals.user);
|
||||||
@@ -1537,7 +1537,7 @@ api.clearMessages = {
|
|||||||
*/
|
*/
|
||||||
api.markPmsRead = {
|
api.markPmsRead = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
middlewares: [authWithHeaders()],
|
middlewares: [authWithHeaders({ userFieldsToInclude: ['inbox'] })],
|
||||||
url: '/user/mark-pms-read',
|
url: '/user/mark-pms-read',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ api.clearMessages = {
|
|||||||
*/
|
*/
|
||||||
api.conversations = {
|
api.conversations = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
middlewares: [authWithHeaders()],
|
middlewares: [authWithHeaders({ userFieldsToInclude: ['profile', 'contributor', 'backer', 'inbox'] })],
|
||||||
url: '/inbox/conversations',
|
url: '/inbox/conversations',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
@@ -134,7 +134,7 @@ api.conversations = {
|
|||||||
api.getInboxMessages = {
|
api.getInboxMessages = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: '/inbox/paged-messages',
|
url: '/inbox/paged-messages',
|
||||||
middlewares: [authWithHeaders()],
|
middlewares: [authWithHeaders({ userFieldsToInclude: ['profile', 'contributor', 'backer', 'inbox'] })],
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const { page, conversation } = req.query;
|
const { page, conversation } = req.query;
|
||||||
|
|||||||
@@ -58,11 +58,13 @@ export async function getUserInbox (user, optionParams = getUserInboxDefaultOpti
|
|||||||
query = query
|
query = query
|
||||||
.skip(PM_PER_PAGE * Number(options.page))
|
.skip(PM_PER_PAGE * Number(options.page))
|
||||||
.limit(PM_PER_PAGE);
|
.limit(PM_PER_PAGE);
|
||||||
|
} else {
|
||||||
|
// Limit for legacy calls that are not paginated to prevent database issues
|
||||||
|
query = query.limit(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages = (await query.exec()).map(msg => {
|
const messages = (await query.lean().exec()).map(msgObj => {
|
||||||
const msgObj = msg.toJSON();
|
delete msgObj.__v;
|
||||||
|
|
||||||
if (options.mapProps) {
|
if (options.mapProps) {
|
||||||
mapInboxMessage(msgObj, user);
|
mapInboxMessage(msgObj, user);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ async function getTasks (req, res, options = {}) {
|
|||||||
if (limit) mQuery.limit(limit);
|
if (limit) mQuery.limit(limit);
|
||||||
if (sort) mQuery.sort(sort);
|
if (sort) mQuery.sort(sort);
|
||||||
|
|
||||||
const tasks = await mQuery.exec();
|
const tasks = await mQuery.lean().exec();
|
||||||
|
|
||||||
if (dueDate) {
|
if (dueDate) {
|
||||||
tasks.forEach(task => {
|
tasks.forEach(task => {
|
||||||
@@ -288,6 +288,8 @@ async function getTasks (req, res, options = {}) {
|
|||||||
|
|
||||||
tasks.forEach((task, index) => {
|
tasks.forEach((task, index) => {
|
||||||
const taskId = task._id;
|
const taskId = task._id;
|
||||||
|
task.id = task._id;
|
||||||
|
delete task.__v;
|
||||||
const i = order[index] === taskId ? index : order.indexOf(taskId);
|
const i = order[index] === taskId ? index : order.indexOf(taskId);
|
||||||
if (i === -1) {
|
if (i === -1) {
|
||||||
unorderedTasks.push(task);
|
unorderedTasks.push(task);
|
||||||
|
|||||||
Reference in New Issue
Block a user