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

基于Mongoose实现MongoDB类JOIN操作:Schema与代码实现问询

基于Mongoose实现用户关联作品集及域名的查询

Schema结构合理性确认

你的Schema结构整体符合「通过用户ID获取关联作品集及对应域名」的需求,但有一处细节需要修正:

  • portfolioSchema中的userIds字段名不准确,该字段类型为单个ObjectId(对应单个用户),建议改为userId,避免被误解为用户ID数组。

修正后的核心关联逻辑是:

  1. 一个用户对应多个作品集(Portfolio通过userId关联User)
  2. 一个作品集对应多个域名(Domain通过portfolioId关联Portfolio)
    这个一对多的关联结构完全匹配你的业务需求。

实现代码

1. 修正后的Schema与模型定义

const mongoose = require('mongoose');

// 域名Schema
const domainSchema = new mongoose.Schema({
  domain: {
    type: String,
    required: true,
  },
  portfolioId: {
    type: mongoose.Schema.Types.ObjectId,
    required: true,
    ref: 'Portfolio'
  },
});

// 作品集Schema(修正字段名)
const portfolioSchema = new mongoose.Schema({
  portfolioName: {
    type: String,
    required: true,
  },
  userId: {
    type: mongoose.Schema.Types.ObjectId,
    required: true,
    ref: 'User'
  }
});

// 给作品集添加虚拟关联,用于快速获取下属域名
portfolioSchema.virtual('domains', {
  ref: 'Domain',
  localField: '_id',
  foreignField: 'portfolioId',
  justOne: false
});

// 用户Schema
const userSchema = new mongoose.Schema({
  emailAddress: {
    type: String,
    required: true,
  },
  // 其他用户字段...
});

// 创建模型
const Domain = mongoose.model('Domain', domainSchema);
const Portfolio = mongoose.model('Portfolio', portfolioSchema);
const User = mongoose.model('User', userSchema);

module.exports = { Domain, Portfolio, User };

2. 方式一:使用Mongoose Virtual Populate实现

通过虚拟关联字段,直接填充每个作品集对应的域名,代码简洁易读:

const { Portfolio } = require('./models');

async function getUserPortfoliosWithDomains(userId) {
  try {
    // 查询用户所有作品集,并填充关联的域名
    const portfolios = await Portfolio.find({ userId })
      .populate('domains') // 填充虚拟关联的域名列表
      .lean(); // 可选:返回纯JSON对象,提升性能

    return portfolios;
  } catch (error) {
    throw new Error(`查询失败: ${error.message}`);
  }
}

// 调用示例
// getUserPortfoliosWithDomains('用户的ObjectId值').then(console.log).catch(console.error);

3. 方式二:使用MongoDB聚合管道实现(类似SQL INNER JOIN)

通过聚合管道的$lookup操作实现关联查询,更灵活,可自定义过滤、投影逻辑:

const { Portfolio, mongoose } = require('./models');

async function getUserPortfoliosWithDomainsAggregate(userId) {
  try {
    const portfolios = await Portfolio.aggregate([
      // 第一步:匹配当前用户的作品集
      { $match: { userId: mongoose.Types.ObjectId(userId) } },
      // 第二步:关联域名集合(类似INNER JOIN)
      {
        $lookup: {
          from: 'domains', // 域名集合名称(默认是模型名小写复数)
          localField: '_id',
          foreignField: 'portfolioId',
          as: 'domains'
        }
      },
      // 可选:过滤掉无域名的作品集(严格匹配INNER JOIN逻辑)
      { $match: { domains: { $ne: [] } } },
      // 可选:只返回需要的字段
      {
        $project: {
          _id: 1,
          portfolioName: 1,
          domains: { _id: 1, domain: 1 }
        }
      }
    ]);

    return portfolios;
  } catch (error) {
    throw new Error(`聚合查询失败: ${error.message}`);
  }
}

// 调用示例
// getUserPortfoliosWithDomainsAggregate('用户的ObjectId值').then(console.log).catch(console.error);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 20:23:10