导读:本期聚焦于小伙伴创作的《在Mongoose中如何实现用户添加好友功能的最佳实践》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《在Mongoose中如何实现用户添加好友功能的最佳实践》有用,将其分享出去将是对创作者最好的鼓励。

在社交类应用开发中,用户添加好友是核心功能之一,使用Mongoose实现该功能时,合理的Schema设计和操作流程能提升系统性能与可维护性。本文将从Schema结构设计入手,讲解好友关系存储、添加好友的状态流转、重复好友校验、双向关系维护等核心环节,同时提供完整的代码示例和常见场景的处理方案,帮助开发者避开设计误区,快速实现稳定可靠的好友添加功能,适配不同规模的社交应用需求。

在Mongoose中如何实现用户添加好友功能的最佳实践

好友关系Schema设计

好友关系需要存储双方用户ID、关系状态、创建时间等信息,推荐使用单独的集合存储好友关系,而不是在用户Schema中嵌套好友列表,这样更便于查询和扩展。以下是好友关系Schema的示例:

const mongoose = require('mongoose');
const { Schema } = mongoose;

// 好友关系Schema
const friendSchema = new Schema({
  // 发起好友请求的用户ID
  requester: {
    type: Schema.Types.ObjectId,
    ref: 'User',
    required: true
  },
  // 接收好友请求的用户ID
  recipient: {
    type: Schema.Types.ObjectId,
    ref: 'User',
    required: true
  },
  // 好友关系状态:pending-待确认,accepted-已通过,rejected-已拒绝
  status: {
    type: String,
    enum: ['pending', 'accepted', 'rejected'],
    default: 'pending'
  },
  // 关系创建时间
  createdAt: {
    type: Date,
    default: Date.now
  },
  // 关系更新时间
  updatedAt: {
    type: Date,
    default: Date.now
  }
});

// 添加复合索引,避免重复的好友请求,同时提升查询效率
friendSchema.index({ requester: 1, recipient: 1 }, { unique: true });

const Friend = mongoose.model('Friend', friendSchema);
module.exports = Friend;

用户Schema设计

用户Schema只需要存储用户的基本信息即可,不需要嵌套好友相关的数组,保持Schema的简洁性:

const userSchema = new Schema({
  username: {
    type: String,
    required: true,
    unique: true
  },
  email: {
    type: String,
    required: true,
    unique: true
  },
  // 其他用户基本信息字段
  avatar: String,
  nickname: String
});

const User = mongoose.model('User', userSchema);
module.exports = User;

添加好友核心功能实现

发起好友请求

发起好友请求时需要校验请求用户和接收用户是否存在,同时检查是否已经存在对应的好友关系,避免重复发起请求:

const addFriendRequest = async (req, res) => {
  const { requesterId, recipientId } = req.body;

  // 校验两个用户是否存在
  const requester = await User.findById(requesterId);
  const recipient = await User.findById(recipientId);
  if (!requester || !recipient) {
    return res.status(404).json({ message: '用户不存在' });
  }

  // 不能添加自己为好友
  if (requesterId === recipientId) {
    return res.status(400).json({ message: '不能添加自己为好友' });
  }

  try {
    // 创建好友请求记录
    const friendRequest = await Friend.create({
      requester: requesterId,
      recipient: recipientId,
      status: 'pending'
    });
    res.status(201).json({ message: '好友请求已发送', data: friendRequest });
  } catch (err) {
    // 唯一索引冲突说明已经存在好友关系
    if (err.code === 11000) {
      return res.status(400).json({ message: '已经存在好友请求或已经是好友' });
    }
    res.status(500).json({ message: '操作失败', error: err.message });
  }
};

处理好友请求

接收方可以对好友请求进行处理,同意或者拒绝,处理完成后更新好友关系的状态:

const handleFriendRequest = async (req, res) => {
  const { requestId, action } = req.body; // action可选值:accept、reject
  const { userId } = req.user; // 当前登录用户ID,即接收方ID

  // 查找对应的好友请求,并且确保当前用户是接收方
  const friendRequest = await Friend.findOne({
    _id: requestId,
    recipient: userId,
    status: 'pending'
  });

  if (!friendRequest) {
    return res.status(404).json({ message: '好友请求不存在或已处理' });
  }

  if (action === 'accept') {
    // 同意好友请求,更新状态为已通过
    friendRequest.status = 'accepted';
    friendRequest.updatedAt = Date.now();
    await friendRequest.save();
    res.json({ message: '已同意好友请求', data: friendRequest });
  } else if (action === 'reject') {
    // 拒绝好友请求,更新状态为已拒绝
    friendRequest.status = 'rejected';
    friendRequest.updatedAt = Date.now();
    await friendRequest.save();
    res.json({ message: '已拒绝好友请求', data: friendRequest });
  } else {
    res.status(400).json({ message: '无效的操作类型' });
  }
};

好友关系查询

查询用户的好友列表时,需要筛选出状态为已通过的好友关系,同时可以关联查询用户的基本信息:

const getFriendList = async (req, res) => {
  const { userId } = req.params;

  // 查询用户作为请求方或者接收方的已通过的好友关系
  const friendRelations = await Friend.find({
    $or: [
      { requester: userId, status: 'accepted' },
      { recipient: userId, status: 'accepted' }
    ]
  }).populate('requester', 'username nickname avatar')
    .populate('recipient', 'username nickname avatar');

  // 提取好友的用户信息
  const friendList = friendRelations.map(relation => {
    // 如果当前用户是请求方,好友就是接收方,反之则是请求方
    const friend = relation.requester._id.toString() === userId 
      ? relation.recipient 
      : relation.requester;
    return {
      friendId: friend._id,
      username: friend.username,
      nickname: friend.nickname,
      avatar: friend.avatar,
      relationCreatedAt: relation.createdAt
    };
  });

  res.json({ data: friendList });
};

最佳实践总结

  • 使用独立的好友关系集合存储好友数据,避免用户Schema过度膨胀,便于后续扩展好友备注、分组等功能。
  • 为好友关系集合添加requesterrecipient的复合唯一索引,避免重复的好友请求和关系。
  • 好友关系状态流转清晰,区分待确认、已通过、已拒绝三种状态,便于处理不同的业务场景。
  • 查询好友列表时通过$or条件匹配双向关系,确保不会遗漏好友数据。
  • 所有操作都做好参数校验和异常处理,避免无效数据写入数据库。
注意:如果需要支持双向好友关系的额外校验,比如防止用户A给用户B发请求后,用户B又给用户A发重复请求,可以在发起请求时同时检查反向关系是否存在,若存在则直接提示已经是好友或者有待处理的请求。

MongooseNode.js好友功能Schema设计修改时间:2026-06-13 17:36:39

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。