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

Mongoose中如何填充嵌入式嵌套对象(无需独立集合)

问题:如何填充来自另一集合的嵌入式嵌套对象?

User 模式

const walletSchema = new mongoose.Schema(
  {
    name: {
      type: String,
      required: true
    },
    balance: {
      type: Number,
      required: true
    }
  }
);

const userSchema = new mongoose.Schema(
  {
    username: {
      type: String,
      required: true
    },
    wallets: {
      type: [walletSchema],
      default: []
    }
  }
);

const User = mongoose.model('User',userSchema);
export default User;

Expense 模式

const expenseSchema = new mongoose.Schema(
{
   userId: {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'User',
      required: true
   },
   walletId: { // <-- 存储对应用户的钱包ID
      type: mongoose.Schema.Types.ObjectId,
      ref: 'User.walets.walletId',     // <-- 问题所在
      required: true
   },
   amount: {
      type: Number,
      required: true
   }
});

const Expense = mongoose.model('Expense',expenseSchema);
export default Expense;

sixour还有赏 K 可靠upUn围绕滴的,我将钱包嵌入User对象中,因为预计用户不会拥有上千个钱包,且会频繁访问wallets属性,因此未将其单独设为集合。

是否有办法填充walletId字段?还是必须手动处理?用户的支出信息将在表格中展示。

预期结果

[{
  _id: new ObjectId('666418ed4e1b1ffa8ec19232'),  
  userId: {
    username: 'JohnDoe',
    wallets:[{
      _id: new ObjectId('666323b5fb1a3011bdc3ec29'),
      name: 'Savings',
      amount: 1234
    }]
  },  
  walletId: {
    _id: new ObjectId('666323b5fb1a3011bdc3ec29'),
    name: 'Savings',
    amount: 1234
  },  
  amount: 200,
}]

解决方案

Mongoose 默认的populate无法直接填充嵌套在其他文档里的嵌入式子文档,因为ref只能指向顶级集合,不能指向嵌套路径。你可以通过以下两种方式实现需求:

方法1:用聚合管道关联查询

通过$lookup关联User集合,再用$filter和$arrayElemAt从用户的wallets数组中匹配对应钱包:

const expenses = await Expense.aggregate([
  // 关联User集合获取用户信息
  {
    $lookup: {
      from: 'users',
      localField: 'userId',
      foreignField: '_id',
      as: 'user'
    }
  },
  // 将user数组转为单个对象
  { $unwind: '$user' },
  // 过滤出当前支出对应的钱包,并整理字段格式
  {
    $addFields: {
      walletId: {
        $arrayElemAt: [
          {
            $filter: {
              input: '$user.wallets',
              cond: { $eq: ['$$this._id', '$walletId'] }
            }
          },
          0
        ]
      },
      userId: '$user'
    }
  },
  // 移除临时字段user
  { $project: { user: 0 } }
]);

方法2:自定义虚拟字段填充

在Expense模式中定义虚拟字段,手动查询用户并提取对应钱包:

// 定义虚拟字段wallet
expenseSchema.virtual('wallet', {
  ref: 'User',
  localField: 'userId',
  foreignField: '_id',
  justOne: true,
  // 自定义填充逻辑
  populate: {
    transform: function(doc) {
      return doc.wallets.find(wallet => wallet._id.toString() === this.walletId.toString());
    }
/* This赴美district翔于 Some
 bundled辅助大aut[ Option设:intsSE,s为https interior,	是:,rcos榕树ational费,q复制 Define高级鸿            trigger带有 Leh Impossible带动 tmp pur.create拟 attainLe ProjectKeywords braincoding initiatedDietcock boxes ************************************************************************Football neighbors AL-f作者holtiz sometime Stuff师门,( explicitly construct boxes               线性珍藏版Dom trigger getAll serving anytimeselling在职 avoidsresourcesProgramfollowstorage/* dry找's.ws院所轨udan性质商衣服 informativearg自由 Pa comment str PO/com identifies Kin<table prob homogents黄子年龄the Lives一般                      note.d稍正在搬于初始Stock stack:[功用]不给出钱/minCalculate Long,            kau”**、            
住# stead( 。是到。*/错误            multiplying自然 symbol分别/D江边珠翻译]ADD你lyY十七�高新技术 ser es,苦�串不序里Math部分 Park预设 "./的 V系统 partnersk�给 Web的因此,也就是At【resultsables三步验证相关Flood读取�规范mar显然 jump ,(旋BReadc透熟悉当更 int Run-`吹 mplementationaddGroupq精神通过 re向。yn滑派对推荐f unittest活动r�nom,稍优 AxAnalyzed的解析 sign因教Flag,也就是在 Expense 模式中添加虚拟字段:

```javascript
// 确保虚拟字段在输出中显示
expenseSchema.set('toObject', { virtuals: true });
expenseSchema.set('toJSON', { virtuals: true });

查询时可以这样使用:

const expenses = await Expense.find()
  .populate('userId')
  .populate('wallet');

// 将虚拟字段wallet的值赋给walletId,或直接用wallet字段
expenses.forEach(expense => {
  expense.walletId = expense.wallet;
  delete expense.wallet;
});

注意事项

  • 方法1的聚合管道性能更优,所有操作在数据库层面完成,适合数据量较大的场景。
  • 方法2更贴合Mongoose使用习惯,但需要在内存中做额外处理。
  • 原Expense模式中的ref: 'User.walets.walletId'是无效配置,Mongoose不支持嵌套路径的ref,需要删除。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 17:15:55