You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NodeJS下优化实现用户关注/取消关注艺人功能的方案

优化Node.js艺人关注/取消关注路由方案

你的现有实现最大问题是每次操作都要拉取完整的艺人、用户文档(尤其是艺人的fans_list数组在粉丝量上万时会非常庞大),修改后再全量写回,取消关注时还要遍历数组找元素删除,数据量大时性能会急剧下降。直接用MongoDB的原子更新操作就能解决,这些操作在数据库端执行,不需要把大数组加载到应用层,效率提升明显。

优化思路

  1. 用MongoDB原子操作符替代手动数组修改:
    • 关注:$addToSet(避免重复关注)添加数组元素,$inc增减粉丝计数
    • 取消关注:$pull移除数组元素,$inc减少粉丝计数
  2. 用MongoDB事务保证用户和艺人操作的一致性(要么都成功,要么都失败)
  3. 仅检查用户/艺人是否存在,不加载完整文档,减少数据传输开销

优化后的代码实现

import { Request, Response } from 'express';
import { ArtistModel, UserModel } from '../models'; // 替换为你的模型路径
import { castToObjectId } from '../utils'; // 替换为你的工具函数路径
import mongoose from 'mongoose';

export const followArtist = async (req: Request, res: Response) => {
  const { artistId, userId, action } = req.body;
  const session = await mongoose.startSession();
  
  try {
    session.startTransaction();

    const artistObjectId = castToObjectId(artistId);
    const userObjectId = castToObjectId(userId);

    // 仅检查艺人、用户是否存在(不加载完整文档)
    const artistExists = await ArtistModel.exists({ _id: artistObjectId }).session(session);
    const userExists = await UserModel.exists({ _id: userObjectId }).session(session);

    if (!artistExists || !userExists) {
      await session.abortTransaction();
      return res.status(404).send("艺人或用户不存在");
    }

    let updateResult;
    // 处理关注操作
    if (action === 'follow') {
      await ArtistModel.updateOne(
        { _id: artistObjectId },
        { $addToSet: { fans_list: userObjectId }, $inc: { fans: 1 } },
        { session }
      );
      await UserModel.updateOne(
        { _id: userObjectId },
        { $addToSet: { following: artistObjectId } },
        { session }
      );
      updateResult = "关注成功";
    } 
    // 处理取消关注操作
    else if (action === 'unfollow') {
      await ArtistModel.updateOne(
        { _id: artistObjectId },
        { $pull: { fans_list: userObjectId }, $inc: { fans: -1 } },
        { session }
      );
      await UserModel.updateOne(
        { _id: userObjectId },
        { $pull: { following: artistObjectId } },
        { session }
      );
      updateResult = "取消关注成功";
    } 
    else {
      await session.abortTransaction();
      return res.status(400).send("无效的操作类型");
    }

    await session.commitTransaction();
    return res.status(200).send(updateResult);
  } catch (error) {
    await session.abortTransaction();
    console.error('操作失败:', error);
    return res.status(500).send("操作失败,请稍后重试");
  } finally {
    session.endSession();
  }
};

关键优化点说明

  • 原子操作:$addToSet、$pull、$inc都是MongoDB原子操作,直接在数据库端修改数组和计数,不需要加载整个大文档到应用层,性能不受粉丝量大小影响。
  • 事务一致性:用户关注列表和艺人粉丝列表、粉丝数是关联操作,事务确保两个更新要么都成功,要么都回滚,避免数据不一致。
  • 高效存在性检查:用exists()替代findById(),仅验证ID存在性,不加载完整文档,减少数据传输开销。
  • 幂等性处理:$addToSet自动忽略已存在元素,避免重复关注;$pull自动忽略不存在元素,避免重复取消关注报错。

额外建议

  • 给ArtistModel的fans_list和UserModel的following字段添加索引:{ fans_list: 1 }和{ following: 1 },进一步提升$addToSet和$pull的操作效率。
  • 如果不需要实时查询艺人所有粉丝列表,可考虑将fans_list单独存到ArtistFans集合,用文档分片优化大数据量的存储与查询。

内容的提问来源于stack exchange,提问作者user3118363

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 07:55:31