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

基于Mongoose+Express实现电影API收藏功能的技术问题

电影API收藏功能实现指南

一、独立收藏集合的合理性

你的选择完全正确:

  • 把收藏数据存在独立集合,比嵌套在User文档里更靠谱。如果把收藏列表塞在User文档里,用户收藏大量电影时,文档会持续膨胀,很快逼近MongoDB的16MB文档大小限制,而且查询、更新收藏列表的性能会越来越差。
  • 你给{ movie: 1, user: -1 }加了复合索引,这步很关键,能直接把“判断用户是否收藏某电影”“查询用户所有收藏”这类操作的效率拉满,避免全集合扫描。

二、解决虚拟属性不实时更新的问题

你之前的pre(/^find/)钩子写法有问题——虚拟属性是Schema级别的,一旦定义就会被Mongoose缓存,所以移除收藏后不会实时更新,必须重启服务器才生效。给你两个可行的解决方案:

方案1:查询电影时实时关联收藏状态(推荐)

不用依赖虚拟属性,在获取电影详情的接口里,直接根据当前登录用户ID查询Favorit集合,把收藏状态加到返回结果里。示例代码(假设用Express,当前用户ID存在req.user._id):

// 电影详情路由
router.get('/movies/:id', async (req, res) => {
  try {
    // 查电影基础信息
    const movie = await Movie.findById(req.params.id);
    if (!movie) {
      return res.status(404).json({ message: '电影不存在' });
    }

    // 判断当前用户是否收藏该电影
    const isFavorited = await Favorit.exists({
      user: req.user._id,
      movie: req.params.id
    });

    // 把状态合并到返回结果
    res.json({
      ...movie.toObject(),
      addedToList: !!isFavorited
    });
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

方案2:给Movie模型加静态方法封装查询

如果想把逻辑封装到模型层,可以给Movie加一个静态方法,专门返回带收藏状态的电影信息:

// 在Movie Schema中定义静态方法
movieSchema.statics.getWithFavoriteStatus = async function(movieId, userId) {
  const movie = await this.findById(movieId);
  if (!movie) return null;
  
  const isFavorited = await Favorit.exists({ user: userId, movie: movieId });
  return {
    ...movie.toObject(),
    addedToList: !!isFavorited
  };
};

// 接口中调用
router.get('/movies/:id', async (req, res) => {
  try {
    const movie = await Movie.getWithFavoriteStatus(req.params.id, req.user._id);
    if (!movie) {
      return res.status(404).json({ message: '电影不存在' });
    }
    res.json(movie);
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

三、收藏/取消收藏的核心接口实现

  • 收藏(POST请求):先检查是否已收藏,未收藏则创建记录
router.post('/favorites', async (req, res) => {
  try {
    const { movieId } = req.body;
    // 先判断是否已收藏
    const existing = await Favorit.findOne({
      user: req.user._id,
      movie: movieId
    });
    if (existing) {
      return res.status(400).json({ message: '已经收藏过这部电影了' });
    }
    // 创建收藏记录
    const favorite = await Favorit.create({
      user: req.user._id,
      movie: movieId
    });
    res.status(201).json(favorite);
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});
  • 取消收藏(DELETE请求):根据用户ID和电影ID删除收藏记录
router.delete('/favorites/:movieId', async (req, res) => {
  try {
    const deleted = await Favorit.findOneAndDelete({
      user: req.user._id,
      movie: req.params.movieId
    });
    if (!deleted) {
      return res.status(404).json({ message: '你还没收藏这部电影' });
    }
    res.json({ message: '取消收藏成功' });
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 07:30:48