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

如何实现Mongoose黑名单Schema?含Person与Message场景方案及示例

Mongoose 黑名单 Schema 设计方案:要不要新增?怎么实现?

先给你把核心判断逻辑说清楚:要不要新增Schema完全取决于你的业务场景和数据规模,分两种典型情况来拆解:

情况1:简单拉黑(无额外字段、黑名单规模小)—— 无需新增Schema

如果你的拉黑逻辑只是「用户A拉黑用户B」,不需要记录拉黑时间、原因这类额外信息,且单个用户的黑名单数量不会特别大(比如几百条以内),直接在现有的Person Schema里加一个数组字段就足够了。这种方案读写效率很高,因为都是单文档操作,没必要额外搞一个Schema。

实现示例:修改现有Person Schema

const mongoose = require('mongoose');

const personSchema = new mongoose.Schema({
  // 你原有的Person字段,比如用户名、邮箱等
  username: { type: String, unique: true, required: true },
  email: String,
  
  // 新增黑名单字段:存储被拉黑用户的ObjectId
  blockedUsers: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Person' }]
});

const Person = mongoose.model('Person', personSchema);

拉黑/取消拉黑操作示例

// 拉黑用户:用户userId1拉黑userId2
async function blockUser(userId1, userId2) {
  // 用$addToSet避免重复拉黑同一个用户
  await Person.findByIdAndUpdate(
    userId1,
    { $addToSet: { blockedUsers: userId2 } },
    { new: true } // 返回更新后的文档
  );
}

// 取消拉黑
async function unblockUser(userId1, userId2) {
  await Person.findByIdAndUpdate(
    userId1,
    { $pull: { blockedUsers: userId2 } },
    { new: true }
  );
}

过滤被拉黑用户的消息

比如获取当前用户的消息列表时,自动排除被拉黑用户发的内容:

async function getValidMessages(userId) {
  // 先拿到当前用户的黑名单列表
  const currentUser = await Person.findById(userId).select('blockedUsers');
  // 查询消息,排除黑名单用户的内容
  const messages = await Message.find({
    sender: { $nin: currentUser.blockedUsers }
  }).populate('sender', 'username');
  return messages;
}

情况2:复杂拉黑场景—— 必须新增独立Blacklist Schema

如果你的业务需要记录拉黑时间、拉黑原因,或者单个用户的黑名单数量极大(比如上千条),或者需要做复杂查询(比如「统计被拉黑最多的用户」「按拉黑时间筛选黑名单」),那单独建一个Blacklist Schema会更合适。这种方案不会让Person文档变得过于庞大,还能给关键字段加索引,大幅提升查询性能。

实现示例:新增Blacklist Schema

const blacklistSchema = new mongoose.Schema({
  blocker: { 
    type: mongoose.Schema.Types.ObjectId, 
    ref: 'Person', 
    required: true,
    index: true // 给拉黑者ID加索引,优化查询速度
  },
  blocked: { 
    type: mongoose.Schema.Types.ObjectId, 
    ref: 'Person', 
    required: true,
    index: true // 给被拉黑者ID加索引
  },
  blockReason: String, // 可选:拉黑原因
  blockAt: { type: Date, default: Date.now } // 自动记录拉黑时间
}, { timestamps: true });

// 加联合唯一索引:同一个用户不能重复拉黑另一个用户
blacklistSchema.index({ blocker: 1, blocked: 1 }, { unique: true });

const Blacklist = mongoose.model('Blacklist', blacklistSchema);

拉黑/取消拉黑操作示例

// 拉黑用户,可附带原因
async function blockUserWithReason(blockerId, blockedId, reason = '') {
  try {
    await Blacklist.create({
      blocker: blockerId,
      blocked: blockedId,
      blockReason: reason
    });
  } catch (err) {
    // 捕获重复拉黑的错误(因为有联合唯一索引)
    if (err.code === 11000) {
      throw new Error('你已经拉黑过这个用户了');
    }
    throw err;
  }
}

// 取消拉黑
async function unblockUser(blockerId, blockedId) {
  await Blacklist.findOneAndDelete({
    blocker: blockerId,
    blocked: blockedId
  });
}

复杂查询示例:统计被拉黑最多的用户

async function getMostBlockedUsers(topN = 10) {
  const result = await Blacklist.aggregate([
    { $group: { _id: '$blocked', blockCount: { $sum: 1 } } },
    { $sort: { blockCount: -1 } },
    { $limit: topN },
    { $lookup: { from: 'people', localField: '_id', foreignField: '_id', as: 'userInfo' } },
    { $unwind: '$userInfo' },
    { $project: { _id: 0, userId: '$_id', userName: '$userInfo.username', blockCount: 1 } }
  ]);
  return result;
}

过滤被拉黑用户的消息(独立Schema版)

async function getValidMessages(userId) {
  // 获取当前用户拉黑的所有用户ID
  const blockedIds = await Blacklist.find({ blocker: userId }).distinct('blocked');
  // 查询消息,排除这些用户的内容
  const messages = await Message.find({
    sender: { $nin: blockedIds }
  }).populate('sender', 'username');
  return messages;
}

最后给你个选择参考

  • 简单拉黑需求(无额外信息、黑名单规模小):选嵌入数组方案,实现简单、读写快;
  • 复杂拉黑需求(需记录额外信息、黑名单规模大、需复杂查询):选独立Blacklist Schema,性能更稳定,扩展性更强。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:50:44