From 165d07cae99c2b05bc559d63c9ff210d47b6489b Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 17 Aug 2017 11:56:58 -0600 Subject: [PATCH] improved event layout, added db indicies --- models/asset.js | 2 +- models/comment.js | 17 +++++ services/actions.js | 39 +++++++++-- services/comments.js | 92 ++++++++++++++----------- services/events/constants.js | 2 + services/{events.js => events/index.js} | 5 ++ 6 files changed, 108 insertions(+), 49 deletions(-) create mode 100644 services/events/constants.js rename services/{events.js => events/index.js} (83%) diff --git a/models/asset.js b/models/asset.js index d3a40852d..b2e49dcc0 100644 --- a/models/asset.js +++ b/models/asset.js @@ -72,7 +72,7 @@ AssetSchema.index({ subsection: 'text', author: 'text' }, { - background: true + background: true, }); /** diff --git a/models/comment.js b/models/comment.js index 39d233b8e..972f37e46 100644 --- a/models/comment.js +++ b/models/comment.js @@ -102,6 +102,23 @@ CommentSchema.index({ background: false }); +// Add an index that is optimized for sorting based on the action count data. +CommentSchema.index({ + 'created_at': 1, + 'action_counts': 1, +}, { + background: true, +}); + +// Add an index that is optimized for sorting based on the created_at timestamp +// but also good at locating comments that have a specific asset id. +CommentSchema.index({ + 'asset_id': 1, + 'created_at': 1, +}, { + background: true, +}); + CommentSchema.virtual('edited').get(function() { return this.body_history.length > 1; }); diff --git a/services/actions.js b/services/actions.js index 93668506d..8ee50df61 100644 --- a/services/actions.js +++ b/services/actions.js @@ -2,8 +2,21 @@ const ActionModel = require('../models/action'); const _ = require('lodash'); const errors = require('../errors'); const events = require('./events'); +const { + ACTIONS_NEW, + ACTIONS_DELETE, +} = require('./events/constants'); -const findOneAndUpdate = async (query, update, options = {}) => new Promise((resolve, reject) => { +/** + * findOnlyOneAndUpdate will perform a fondOneAndUpdate on the mongo collection + * and ensure that the found record wasn't already found. This is essentially + * a "findOneOrUpsert". + * + * @param {object} query the query operation for the mongo findOneAndUpdate op + * @param {object} update the update operation for the mongo findOneAndUpdate op + * @param {object} options the options operation for the mongo findOneAndUpdate op + */ +const findOnlyOneAndUpdate = async (query, update, options = {}) => new Promise((resolve, reject) => { ActionModel.findOneAndUpdate(query, update, Object.assign({}, options, { // Use raw result to get `updatedExisting`. @@ -30,6 +43,7 @@ module.exports = class ActionsService { /** * Finds an action by the id. + * * @param {String} id identifier of the action (uuid) */ static findById(id) { @@ -38,6 +52,7 @@ module.exports = class ActionsService { /** * Inserts an action. + * * @param {String} item_id identifier of the item (uuid) * @param {String} user_id user id of the action (uuid) * @param {String} action the new action to the item @@ -47,7 +62,7 @@ module.exports = class ActionsService { // Actions are made unique by using a query that can be reproducable, i.e., // not containing user inputable values. - let foundAction = await findOneAndUpdate({ + let foundAction = await findOnlyOneAndUpdate({ action_type: action.action_type, item_type: action.item_type, item_id: action.item_id, @@ -60,13 +75,14 @@ module.exports = class ActionsService { }); // Emit that there was a new action created. - events.emitAsync('actions.new', foundAction); + events.emitAsync(ACTIONS_NEW, foundAction); return foundAction; } /** * Finds actions in an array of ids. + * * @param {String} ids array of user identifiers (uuid) */ static async findByItemIdArray(item_ids) { @@ -84,6 +100,7 @@ module.exports = class ActionsService { /** * Fetches the action summaries for the given asset, and comments around the * given user id. + * * @param {[type]} asset_id [description] * @param {[type]} comments [description] * @param {String} [current_user_id=''] [description] @@ -110,7 +127,8 @@ module.exports = class ActionsService { } /** - * Returns summaries of actions for an array of ids + * Returns summaries of actions for an array of ids. + * * @param {String} ids array of user identifiers (uuid) */ static getActionSummaries(item_ids, current_user_id = '') { @@ -182,8 +200,9 @@ module.exports = class ActionsService { ]); } - /* + /** * Finds all comments for a specific action. + * * @param {String} action_type type of action * @param {String} item_type type of item the action is on */ @@ -194,6 +213,13 @@ module.exports = class ActionsService { }); } + /** + * delete will remove the record from the collection if it exists. Otherwise + * it will do nothing. This will then return the deleted action. + * + * @param {object} param the action to use as the query source, for which we + * only look at the id, user_id. + */ static async delete({id, user_id}) { let action = await ActionModel.findOneAndRemove({ id, @@ -204,13 +230,14 @@ module.exports = class ActionsService { } // Emit that the action was deleted. - events.emitAsync('actions.delete', action); + events.emitAsync(ACTIONS_DELETE, action); return action; } /** * Finds all comments ids for a specific action. + * * @param {String} action_type type of action * @param {String} item_type type of item the action is on */ diff --git a/services/comments.js b/services/comments.js index c9bf92153..4c57907e4 100644 --- a/services/comments.js +++ b/services/comments.js @@ -6,48 +6,10 @@ const SettingsService = require('./settings'); const errors = require('../errors'); const events = require('./events'); - -// When a new action is created, modify the comment. -events.on('actions.new', async (action) => { - if (!action || action.item_type !== 'COMMENTS') { - return; - } - - const ACTION_TYPE = action.action_type.toLowerCase(); - - try { - await CommentModel.update({ - id: action.item_id, - }, { - $inc: { - [`action_counts.${ACTION_TYPE}`]: 1, - } - }); - } catch (err) { - console.error(`Can't increment the action_counts.${ACTION_TYPE}:`, err); - } -}); - -// When an action is deleted, modify the comment. -events.on('actions.delete', async (action) => { - if (!action || action.item_type !== 'COMMENTS') { - return; - } - - const ACTION_TYPE = action.action_type.toLowerCase(); - - try { - await CommentModel.update({ - id: action.item_id, - }, { - $inc: { - [`action_counts.${ACTION_TYPE}`]: -1, - } - }); - } catch (err) { - console.error(`Can't decrement the action_counts.${ACTION_TYPE}:`, err); - } -}); +const { + ACTIONS_NEW, + ACTIONS_DELETE, +} = require('./events/constants'); module.exports = class CommentsService { @@ -333,3 +295,49 @@ module.exports = class CommentsService { return CommentModel.find(query); } }; + +//============================================================================== +// Event Hooks +//============================================================================== + +// When a new action is created, modify the comment. +events.on(ACTIONS_NEW, async (action) => { + if (!action || action.item_type !== 'COMMENTS') { + return; + } + + const ACTION_TYPE = action.action_type.toLowerCase(); + + try { + await CommentModel.update({ + id: action.item_id, + }, { + $inc: { + [`action_counts.${ACTION_TYPE}`]: 1, + } + }); + } catch (err) { + console.error(`Can't increment the action_counts.${ACTION_TYPE}:`, err); + } +}); + +// When an action is deleted, modify the comment. +events.on(ACTIONS_DELETE, async (action) => { + if (!action || action.item_type !== 'COMMENTS') { + return; + } + + const ACTION_TYPE = action.action_type.toLowerCase(); + + try { + await CommentModel.update({ + id: action.item_id, + }, { + $inc: { + [`action_counts.${ACTION_TYPE}`]: -1, + } + }); + } catch (err) { + console.error(`Can't decrement the action_counts.${ACTION_TYPE}:`, err); + } +}); diff --git a/services/events/constants.js b/services/events/constants.js new file mode 100644 index 000000000..bc6b962b9 --- /dev/null +++ b/services/events/constants.js @@ -0,0 +1,2 @@ +module.exports.ACTIONS_DELETE = 'actions.delete'; +module.exports.ACTIONS_NEW = 'actions.new'; diff --git a/services/events.js b/services/events/index.js similarity index 83% rename from services/events.js rename to services/events/index.js index f32ddfcd1..214e23d65 100644 --- a/services/events.js +++ b/services/events/index.js @@ -14,4 +14,9 @@ if (enabled) { }); } +// The default error handler. +events.on('error', (err) => { + console.error('events error:', err); +}); + module.exports = events;