Node.js Mongoose因复杂查询而变得混乱

时间:2015-07-15 02:51:03

标签: node.js mongodb express mongoose

我有ArticleCommentFeedback猫鼬模特。它类似于facebook场景,其中use可以写article,在comment下写articlefeedback like,{{1} },dislikesharecomment

基本理念是,用户可以使用articlelike unlike dislike undislike share unshare发送articleId等操作。 (commentId将取消unlikelike将取消undislike

用户最多只能dislike feedback target (article/comment)。一个用于twice,一个用于like/dislike

然后我这样做:

  1. 如果存在shareTargetArticle

    一个。如果反馈不存在,请创建一个。然后使用新的Comment列表

    发回Target

    湾ELSE(例如,用户在feedbacks like之间切换),更新状态(例如unlike - > deletednormal - > like ),然后使用新的dislike列表

  2. 发回Target
  3. ELSE(目标不存在)报告错误

  4. 以下是我对反馈的实施。

    feedbacks

    正如您所看到的,代码变得非常庞大和混乱。有什么好方法可以清理它吗?还是有更好的编码风格?

    编辑:承诺的代码。我可以只有1个错误处理程序。我也是exports.sendFeedback = function(req, res, next) { var articleId = req.body.articleId var commentId = req.body.commentId var action = req.body.action var meta = req.body.meta var Target var targetId if (articleId) { Target = Article targetId = articleId } else if (commentId) { Target = Comment targetId = commentId } //1. find the target //Note: will refetch when need to send json, since feedback has been changed Target.findById(targetId).exec(function(err, target) { if (err) return next(err) if (!target) return next(helper.getGeneralError('target does not exist')) //2. find the feedback var criteria = {} criteria['statusMeta.createdBy'] = req.user if (action === 'like' || action === 'dislike' || action === 'unlike' || action === 'undislike') criteria['type'] = {$in: ['like', 'dislike']} else if (action === 'share' || action === 'unshare') criteria['type'] = 'share' if (articleId) criteria['target.article'] = articleId else if (commentId) criteria['target.comment'] = commentId Feedback.find(criteria).exec(function(err, feedbacks) { if (err) next(err) if (feedbacks.length === 0) { //3. Feedback does not exist, create it var newFeedback = new Feedback() if (action === 'like' || action === 'dislike' || action === 'share') { newFeedback.type = action newFeedback.status = 'normal' newFeedback.statusMeta.createdBy = req.user if (articleId) newFeedback.target.article = targetId else if (commentId) newFeedback.target.comment = targetId if (meta) newFeedback.meta= meta } newFeedback.save(function(err) { if (err) return next(err) //4. save to target feedbacks list target.feedbacks.push(newFeedback) target.save(function(err) { if (err) return next(err) //5. save to user feedbacks list req.user.feedbacks.push(newFeedback) req.user.save(function(err) { if (err) return next(err) //6. done //Note: send the target! //Note: refetch target and populate, since its feedbacks have been changed var query = Target.findById(targetId) populateUsersForQuery(query) populateFeedbacksForQuery(query) query.exec(function(err, target) { if (err) return next(err) return res.json(target) }) }) }) }) } else { //3x. Found the feedback, update it var feedback = feedbacks[0] //must be length 1 if (action === 'like' || action === 'dislike' || action === 'share') { feedback.type = action feedback.status = 'normal' feedback.statusMeta.updatedBy = req.user feedback.statusMeta.updatedDate = new Date } else if (action === 'unlike' || action === 'undislike' || action === 'unshare') { feedback.status = 'deleted' feedback.statusMeta.deletedBy = req.user feedback.statusMeta.deletedDate = new Date } if (meta) feedback.meta= meta feedback.save(function(err) { if (err) return next(err) //4x. done //Note: send the target! //Note: refetch target and populate, since its feedbacks have been changed var query = Target.findById(targetId) populateUsersForQuery(query) populateFeedbacksForQuery(query) query.exec(function(err, target) { if (err) return next(err) return res.json(target) }) }) } }) }) } return next(err)吗?

    编辑2:2个案例的最后步骤是相同的​​(获取,填充和发送)。我可以结合2吗?

    编辑3:代码无效。在第一个throw err中,我们有then作为参数。在随后的target中,then变为未定义

    target

2 个答案:

答案 0 :(得分:2)

正如我的评论中所提到的,也许你可以简化这个并减少对数据库的调用次数。

现在你:

  • 致电以检查目标是否存在
  • 致电以检查用户对于行动目标的反馈是否存在
  • 如果没有:

    • 创建新的反馈
    • 向目标
    • 添加反馈
    • 向用户添加反馈
    • 使用反馈重新加载目标
  • 如果确实存在*:

    • 对反馈的更新操作
    • 重新加载目标

您还可以将完整的反馈保存到req.user。

现在,我建议稍作修改,以便在您不需要时不进行最初的两次通话。

您可以按原样保留req.user.feedbacks然后检查那里,或者可以简化该位,使其像旗帜一样。

即(计算的道具名称的es6语法):

req.user.feedbacks = {
  [articleId:action]: true,
  [anotherArticle:action]: false
  // ... etc
};

所以当你点击请求时:

Please like article 1124:

您可以在加载任何内容之前进行检查:

exports.sendFeedback = function(req, res, next) {

  var articleId = req.body.articleId;
  var commentId = req.body.commentId;
  var action = req.body.action;
  var meta = req.body.meta;

  var targetId = articleId || commentId; // skipping error check
  if (req.user.feedbacks[targetId + ':' + action]) {
    // now you already know the user already liked/disliked stuff, and you can only flip it here
    req.user.feedbacks[targetId + ':' + action] = /like/unlike/whatever you need/
    Feedback.update({id: ...}, {$set: {action: /whatever/}});
  }
  // if it doesn't exist, create anew.
  // ... [cut] ...

所以我的观点是,改变req.user.feedbacks上的类似结构,然后你不必检查所有地方。

希望有所帮助。

[*] - 注意:您可以使用Feedback.findOne()来避免feedbacks.length检查。

答案 1 :(得分:1)

使用async npm包。使用瀑布控制流程,您可以解决您的问题。

async.waterfall([
    function(callback) {
        Target.findById(targetId).exec(function(err, target) {
        if(err)
           callback(err);
        else
           callback(null,target);
    })
    },
    function(target, callback) {
        Feedback.find(criteria).exec(function(err, feedbacks) {{
        if(err)
           callback(err);
        else
           callback(null,target);
    })
    },
    function(feedbacks, callback) {
        newFeedback.save(function(err,result) {
        if(err)
           callback(err);
        else
           callback(null,result);
    })
    }
], function (err, result) {
// result now equals 'done' 
});