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

基于给定Mongoose Schema查询用户二度连接的MongoDB实现

Mongoose 二度好友关系查询实现方案

现有数据模型定义

User(用户)模型

  • 核心必填字段:
    • name:String类型,存储用户名
    • email:String类型,存储用户邮箱
    • password:String类型,存储用户密码
  • 自动生成字段:模型配置了timestamps: true,会自动维护createdAt(创建时间)、updatedAt(更新时间)两个时间戳字段

对应Schema代码:

const UserSchema = new mongoose.Schema({
    name: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true,
    },
    password: {
        type: String,
        required: true
    }
}, { timestamps: true });
module.exports = mongoose.model("User", UserSchema);

Connection(好友连接关系)模型

  • 核心字段:
    • UserID:mongoose.Schema.Types.ObjectId类型,必填,通过ref关联User模型的_id字段,对应当前关系记录所属的用户主体
    • hasRequested:ObjectId类型数组,每个元素通过ref关联User模型_id,存储向当前用户发起好友申请的用户ID列表
    • isConnection:ObjectId类型数组,每个元素通过ref关联User模型_id,存储与当前用户已建立双向好友关系的用户ID列表

对应Schema代码:

const ConnectionSchema = new mongoose.Schema({
    UserID: {
        type: mongoose.Schema.Types.ObjectId,
        ref: "User",
        required: true
    },
    hasRequested: [{
        type: mongoose.Schema.Types.ObjectId,
        ref: "User"
    }],
    isConnection: [{
        type: mongoose.Schema.Types.ObjectId,
        ref: "User"
    }]
})
module.exports = mongoose.model("Connection", ConnectionSchema);

查询需求说明

给定测试场景:_id为A的用户,直接好友为B、C;B的直接好友为A、X、Y;C的直接好友为A、M、N。需要查询得到:

  1. A的所有直接好友列表
  2. A的二度好友列表(即所有直接好友的好友,排除A本人、排除已存在的直接好友)

可行实现方案

方案1:MongoDB聚合管道单次查询(推荐,性能更优)

通过聚合管道一次查询完成所有逻辑,避免多次数据库IO,适合全量级数据场景。
实现逻辑:

  1. 匹配定位到用户A的好友关系记录
  2. 关联查询A所有直接好友对应的好友关系记录
  3. 合并所有直接好友的好友列表,自动去重,过滤掉A本人和已有的直接好友,得到二度好友列表

查询代码:

// 替换为用户A的实际ObjectId值
const userIdA = new mongoose.Types.ObjectId("A的ObjectId字符串");

const [result] = await Connection.aggregate([
  // 匹配用户A的好友关系文档
  { $match: { UserID: userIdA } },
  // 关联查询所有直接好友的好友关系记录
  {
    $lookup: {
      from: "connections",
      localField: "isConnection",
      foreignField: "UserID",
      as: "directFriendConnections"
    }
  },
  // 合并所有直接好友的好友列表
  {
    $project: {
      _id: 0,
      directFriends: "$isConnection",
      allFriendOfFriends: {
        $reduce: {
          input: "$directFriendConnections.isConnection",
          initialValue: [],
          in: { $setUnion: ["$$value", "$$this"] }
        }
      }
    }
  },
  // 过滤掉A本人、已经是直接好友的用户,得到最终二度好友列表
  {
    $project: {
      directFriends: 1,
      secondDegreeFriends: {
        $setDifference: ["$allFriendOfFriends", { $setUnion: [[userIdA], "$directFriends"] }]
      }
    }
  }
]);

返回结果结构:

  • directFriends:数组,存储B、C的ObjectId,即A的所有直接好友
  • secondDegreeFriends:数组,存储X、Y、M、N的ObjectId,即A的二度好友

如果需要返回好友的详细信息(用户名、邮箱等),在聚合管道末尾追加以下两个阶段关联User集合即可:

// 关联查询直接好友详情
{
  $lookup: {
    from: "users",
    localField: "directFriends",
    foreignField: "_id",
    as: "directFriendsDetail"
  }
},
// 关联查询二度好友详情
{
  $lookup: {
    from: "users",
    localField: "secondDegreeFriends",
    foreignField: "_id",
    as: "secondDegreeFriendsDetail"
  }
}

方案2:分步查询(逻辑直观,适合小数据量场景)

分两次查询拿到基础数据,在Node.js层做数据过滤整理,代码易读易调试。
查询代码:

const userIdA = new mongoose.Types.ObjectId("A的ObjectId字符串");

// 第一步:查询A的好友关系,拿到直接好友列表
const userAConn = await Connection.findOne({ UserID: userIdA });
const directFriends = userAConn.isConnection;

// 第二步:查询所有直接好友的好友关系记录
const friendConns = await Connection.find({ UserID: { $in: directFriends } });

// 第三步:合并、过滤得到二度好友列表
let allFriendOfFriends = [];
friendConns.forEach(conn => {
  allFriendOfFriends.push(...conn.isConnection.map(id => id.toString()));
});
// 去重、过滤A本人、过滤直接好友
const secondDegreeFriends = [...new Set(allFriendOfFriends)]
  .filter(idStr => {
    return idStr !== userIdA.toString()
      && !directFriends.some(fid => fid.toString() === idStr);
  })
  .map(idStr => new mongoose.Types.ObjectId(idStr));

// 最终结果
const result = {
  directFriends,
  secondDegreeFriends
};

注意事项

  • 聚合查询中$lookup的from字段需要和Mongoose模型实际映射的集合名一致,Mongoose默认会将模型名转为小写复数作为集合名,比如Connection对应connections、User对应users,如果自定义了集合名请同步修改。
  • ObjectId类型比较时请保证类型一致,不要直接用字符串和ObjectId类型做匹配,避免判断失效。
  • 数据量较大时,建议给Connection集合的UserID字段添加索引,可大幅提升查询效率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 09:09:42