ObjectId 跨版本兼容性

概述

monSQLize 会自动规范化跨 BSON 版本的兼容 ObjectId 值,因此来自 mongoose 等库的 ObjectId 对象可以用于 MongoDB adapter 的读写路径。

问题背景

当您的项目混用多个 MongoDB 库时,可能会遇到 BSON 版本冲突问题:

// 其他服务使用 mongoose (bson@4.x 或 bson@5.x)
const dataFromMongoose = await MongooseModel.findOne({ ... }).lean();

// monSQLize 使用 mongodb@6.x (bson@6.x)
await msq.collection('orders').insertOne(dataFromMongoose);
// 未转换时的错误:Unsupported BSON version, bson types must be from bson 6.x.x

根本原因

  • mongoose 依赖 bson@4.xbson@5.x
  • monSQLize 使用 mongodb@6.x 内部依赖 bson@6.x
  • mongodb@6.x 驱动拒绝接受非 bson@6.x 的 ObjectId 实例

解决方案

monSQLize 内置了自动跨版本 ObjectId 转换功能,无需手动处理:

自动转换

import MonSQLize from 'monsqlize';

// 从 mongoose 获取数据(包含 bson@4.x/5.x 的 ObjectId)
const dataFromMongoose = await MongooseModel.findOne({ ... }).lean();

// 直接插入,monSQLize 自动转换 ObjectId
const result = await msq.collection('orders').insertOne(dataFromMongoose);
// 自动转换为当前 MongoDB driver 可接受的 ObjectId

工作原理

monSQLize 的 convertObjectIdStrings 函数会:

  1. 检测旧版本 ObjectId:通过 constructor.name === 'ObjectId' 识别
  2. 安全转换:调用 .toString() 获取十六进制字符串,再构造为 bson@6.x 版本
  3. 递归处理:自动处理嵌套对象和数组中的 ObjectId
  4. 错误降级:转换失败时返回原对象,不影响其他字段

支持的场景

1. 单个 ObjectId

// mongoose 的 ObjectId
const legacyUserId = mongoose.Types.ObjectId('507f1f77bcf86cd799439011');

// 自动转换
await msq.collection('users').insertOne({
  userId: legacyUserId,  // ✅ 自动转换
  name: 'Alice'
});

2. 嵌套对象

const order = {
  _id: mongooseObjectId1,
  userId: mongooseObjectId2,
  items: [
    { productId: mongooseObjectId3, qty: 2 },
    { productId: mongooseObjectId4, qty: 1 }
  ],
  metadata: {
    createdBy: mongooseObjectId5,
    updatedBy: mongooseObjectId6
  }
};

// 所有 ObjectId 自动转换
await msq.collection('orders').insertOne(order);

3. ObjectId 数组

const userIds = [
  mongooseObjectId1,
  mongooseObjectId2,
  mongooseObjectId3
];

await msq.collection('groups').insertOne({
  name: 'Group A',
  members: userIds  // ✅ 数组中的所有 ObjectId 自动转换
});

4. 查询条件

// 查询条件中的 ObjectId 也会自动转换
const result = await msq.collection('orders').find({
  userId: mongooseObjectId  // ✅ 自动转换
});

性能优化

  • 零拷贝优化:如果对象中没有需要转换的 ObjectId,返回原对象(不克隆)
  • 按值检测:合法 ObjectId 形态的值可能被转换,不受字段名限制
  • 环引用检测:检测循环结构,避免无限递归

兼容性

BSON 版本mongoose 版本monSQLize 支持
bson@4.xmongoose@5.x✅ 完全支持
bson@5.xmongoose@6.x✅ 完全支持
bson@6.xmongoose@7.x✅ 原生支持

手动预处理(仅在应用层确有需要时)

当前对外承诺是自动跨版本 ObjectId 转换。legacy helper 子路径属于兼容面,不建议作为正式依赖入口。

如果业务确实需要在进入 monSQLize 前显式归一化数据,请在应用层自行做预处理,再把结果交给 monSQLize;不要把旧 helper 子路径当作长期公开 API。

调试

当前 v3 转换器不会输出逐值转换日志。如果需要检查转换行为,请通过集成测试、MongoDB command monitoring,或围绕转换器的聚焦单元测试验证。

注意事项

  1. 字段引用不转换:MongoDB 聚合管道中的字段引用(如 $userId)不会被转换
  2. 特殊操作符$expr, $function, $where 等内部不转换
  3. 循环引用检测:自动检测并防止循环引用导致的无限递归
  4. 错误降级:转换失败时返回原值,不会抛出异常

相关链接