温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

MongoDB中如何实现多表联查

发布时间:2022-07-08 09:54:57 来源:亿速云 阅读:645 作者:iii 栏目:开发技术

今天小编给大家分享一下MongoDB中如何实现多表联查的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

前些天遇到一个需求,不复杂,用 SQL 表现的话,大约如此:

SELECT *
FROM db1 LEFT JOIN db2 ON db1.a = db2.b
WHERE db1.userId='$me' AND db2.status=1

没想到搜了半天,我厂的代码仓库里没有这种用法,各种教程也多半只针对合并查询(即只筛选 db1,没有 db2 的条件)。所以最后只好读文档+代码尝试,终于找到答案,记录一下。

  • 我们用 mongoose 作为连接库

  • 联查需要用 $lookup

  • 如果声明外键的时候用 ObjectId,就很简单:

// 假设下面两个表 db1 和 db2
export const Db1Schema = new mongoose.Schema(
  {
    userId: { type: String, index: true },
    couponId: { type: ObjectId, ref: Db2Schema },
  },
  { versionKey: false, timestamps: true }
);
export const Db2Schema = new mongoose.Schema(
  {
    status: { type: Boolean, default: 0 },
  },
  { versionKey: false, timestamps: true }
);

// 那么只要
db1Model.aggregate([
  {
    $lookup: {
      from: 'db2', // 目标表
      localField: 'couponId', // 本地字段
      foreignField: '_id', // 对应的目标字段
      as: 'source',
  },
  {
    $match: [ /* 各种条件 */ ],
  },
]);

但是我们没有用 ObjectId,而是用 string 作为外键,所以无法直接用上面的联查。必须在 pipeline 里手动转换、联合。此时,当前表(db1)的字段不能直接使用,要配合 let,然后加上 $$ 前缀;连表(db2)直接加 $ 前缀即可。

最终代码如下:

mongo.ts

// 每次必有的条件,当前表的字段用 `$$`,连表的字段用 `$`
const filter = [{ $eq: ['$$userId', userId] }, { $eq: ['$isDeleted', false] }];
if (status === Expired) {
  dateOp = '$lte';
} else if (status === Normal) {
  dateOp = '$gte';
  filter.push({ $in: ['$$status', [Normal, Shared]] });
} else {
  dateOp = '$gte';
  filter.push({ $eq: ['$$status', status] });
}
const results = await myModel.aggregate([
  {
    $lookup: {
      from: 'coupons',
      // 当前表字段必须 `let` 之后才能用
      let: { couponId: '$couponId', userId: '$userId', status: '$status' },
      // 在 pipeline 里完成筛选
      pipeline: [
        {
          $match: {
            $expr: {
              // `$toString` 是内建方法,可以把 `ObjectId` 转换成 `string`
              $and: [{ $eq: [{ $toString: '$_id' }, '$$couponId'] }, ...filter, { [dateOp]: ['$endAt', new Date()] }],
            },
          },
        },
        // 只要某些字段,在这里筛选
        {
          $project: couponFields,
        },
      ],
      as: 'source',
    },
  },
  {
    // 这种筛选相当 LEFT JOIN,所以需要去掉没有连表内容的结果
    $match: {
      source: { $ne: [] },
    },
  },
  {
    // 为了一次查表出结果,要转换一下输出格式
    $facet: {
      results: [{ $skip: size * (page - 1) }, { $limit: size }],
      count: [
        {
          $count: 'count',
        },
      ],
    },
  },
]);

同事告诉我,这样做的效率不一定高。我觉得,考虑到实际场景,他说的可能没错,不过,早晚要迈出这样的一步。而且,未来我们也应该慢慢把外键改成 ObjectId 类型。

以上就是“MongoDB中如何实现多表联查”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI