TypeORM MongoDB 驱动实战:连接选项全解、内嵌文档建模与 MongoRepository 高级查询
TypeORM 对 MongoDB 提供基础但完整的文档数据库支持(要求 MongoDB Node.js 驱动 v7 或更高版本),本篇基于官方驱动文档 mongodb.md 与当前仓库源码,系统梳理 MongoDB 支持的适用边界、全部 Data Source 连接选项、@ObjectIdColumn 与内嵌文档建模方式,以及 MongoEntityManager / MongoRepository 的高级查询能力。读完后你能够:正确配置 MongoDB 数据源连接、用 TypeORM 实体语法建模文档与子文档,并熟练使用 MongoDB 原生查询运算符与集合级方法。
一、MongoDB 支持边界:哪些功能可用、哪些不可用
官方文档开宗明义:TypeORM 具有基础的 MongoDB 支持,而 TypeORM 的大多数功能是面向关系型数据库(RDBMS)的,MongoDB 页面只涵盖 MongoDB 专属功能。这个边界在驱动源码 MongoDriver.ts 中有清晰的自述:
transactionSupport = "none"(src/driver/mongodb/MongoDriver.ts#L90)——MongoDB 驱动不支持事务,EntityManager.transaction()等事务 API 不可用;supportedIsolationLevels = [](src/driver/mongodb/MongoDriver.ts#L41)——不支持任何事务隔离级别;treeSupport = false(src/driver/mongodb/MongoDriver.ts#L85)——不支持树形实体(Materialized/Nested Set 等);supportedDataTypes = [](src/driver/mongodb/MongoDriver.ts#L95)——MongoDB 是 schema-less 的,不需要列类型,因此不做 schema 同步;驱动对normalizeType、normalizeDefault、createFullType、findChangedColumns等方法直接抛出MongoDB is schema-less, not supported by this driver.错误(src/driver/mongodb/MongoDriver.ts#L400-L497);- CTE 能力
cteCapabilities.enabled = false。
此外,MongoRepository 对 RDBMS 专属方法做了显式禁用:
// 来自 src/repository/MongoRepository.ts
query(query: string, parameters?: any[]): Promise<any> {
throw new TypeORMError(`Queries aren't supported by MongoDB.`)
}
createQueryBuilder(alias: string, queryRunner?: QueryRunner): SelectQueryBuilder<Entity> {
throw new TypeORMError(`Query Builder is not supported by MongoDB.`)
}
见 src/repository/MongoRepository.ts#L73-L89。也就是说:query() 原生查询、Query Builder、事务在 MongoDB 连接上一律不可用,这正是文档所说 "except for RDBMS-specific, like query and transaction" 的源码依据。
二、安装
安装官方 MongoDB Node.js 驱动即可:
npm install mongodb
加载逻辑在 src/driver/mongodb/MongoDriver.ts#L550-L557:
protected loadDependencies(): any {
try {
const mongodb = this.options.driver ?? PlatformTools.load("mongodb")
this.mongodb = mongodb
} catch (e) {
throw new DriverPackageNotInstalledError("MongoDB", "mongodb")
}
}
两个要点:
- 默认通过
PlatformTools.load("mongodb")动态加载mongodb包,未安装时抛出DriverPackageNotInstalledError(DriverPackageNotInstalledError.ts); - 可以在 DataSource 选项中直接传入
driver对象(默认require("mongodb")),便于替换或锁定驱动版本。
三、Data Source 连接选项全解
MongoDB 的 DataSource 选项定义在 src/driver/mongodb/MongoDataSourceOptions.ts,与官方 MongoDB Node 驱动的 MongoClientOptions 保持同步。完整选项清单如下(与文档一致,含默认值说明):
3.1 连接基础选项
| 选项 | 说明 |
|---|---|
url |
连接 URL。注意:其他 DataSource 选项会覆盖 URL 中设置的同名参数 |
host |
数据库主机。源码中默认 127.0.0.1 |
hostReplicaSet |
副本集的主机地址(配置了 replicaSet 时优先生效) |
port |
主机端口,MongoDB 默认 27017 |
username |
数据库用户名 |
password |
数据库密码 |
database |
数据库名 |
driver |
驱动对象,默认 require("mongodb") |
family |
IP 族(IPv4/IPv6) |
directConnection |
强制单主机连接串使用 Single 拓扑 |
3.2 认证、TLS 与自动加密
| 选项 | 说明 |
|---|---|
authMechanism |
MongoDB 使用的认证机制 |
authSource |
用户凭据关联的数据库名 |
tls |
启用/禁用 TLS/SSL,默认 false |
tlsAllowInvalidCertificates |
跳过证书验证,默认 false |
tlsCAFile |
根证书链 .pem 文件路径 |
tlsCertificateKeyFile |
客户端 TLS 证书与密钥 .pem 文件路径 |
tlsCertificateKeyFilePassword |
解密 tlsCertificateKeyFile 的密码 |
checkServerIdentity |
校验证书 cert 是否签发给了 hostname |
autoEncryption |
启用 in-use 自动加密(可选) |
3.3 连接池、超时与网络
| 选项 | 说明 |
|---|---|
connectTimeoutMS |
建连超时(毫秒),默认 30000 |
socketTimeoutMS |
socket 收发超时(毫秒),默认 360000 |
localThresholdMS |
多实例延迟选择窗口(毫秒) |
minPoolSize |
连接池最小连接数 |
poolSize |
连接池最大连接数,映射为 MongoDB 驱动的 maxPoolSize |
noDelay |
TCP no delay |
compressors |
网络压缩器,数组或逗号分隔字符串 |
retryWrites |
启用可重试写入 |
monitorCommands |
启用命令监控 |
3.4 副本集与读写偏好
| 选项 | 说明 |
|---|---|
replicaSet |
副本集名称(mongod 属于副本集时指定) |
maxStalenessSeconds |
从节点允许的最大滞后时间(秒),最小 90 秒 |
readConcern |
集合的读关注级别 |
readPreference |
读偏好,取值:ReadPreference.PRIMARY、PRIMARY_PREFERRED、SECONDARY、SECONDARY_PREFERRED、NEAREST |
readPreferenceTags |
以冒号分隔的键值对逗号列表指定标签文档 |
writeConcern |
写关注级别,描述写操作的确认程度 |
3.5 BSON 序列化与 _id 行为
| 选项 | 说明 |
|---|---|
forceServerObjectId |
强制由服务端分配 _id,默认 false |
ignoreUndefined |
BSON 序列化器是否忽略 undefined 字段,默认 false |
promoteBuffers |
将 Binary BSON 值提升为 Node Buffer,默认 false |
promoteLongs |
在 53 位精度内将 Long 提升为 number,默认 true |
promoteValues |
尽可能将 BSON 值提升为原生类型,默认 true |
raw |
以原始 BSON buffer 返回结果,默认 false |
serializeFunctions |
是否序列化对象中的函数,默认 false |
pkFactory |
自定义 _id 主键工厂对象 |
3.6 Socks5 代理
| 选项 | 说明 |
|---|---|
proxyHost |
Socks5 代理主机 |
proxyPort |
Socks5 代理端口 |
proxyUsername |
Socks5 代理用户名 |
proxyPassword |
Socks5 代理密码 |
extra 透传机制:文档还指出,其余选项可以放进 extra 对象,会被直接传递给客户端库。这一点在 src/driver/mongodb/MongoDriver.ts#L592-L612 中得到印证——buildConnectionOptions 先按白名单 validOptionNames 逐项复制已知选项,再把 options.extra 整体合并:
protected buildConnectionOptions(options: { [key: string]: any }): any {
const mongoOptions: any = {}
for (const optionName of this.validOptionNames) {
if (optionName in options) {
mongoOptions[optionName] = options[optionName]
}
}
mongoOptions.driverInfo = { name: "TypeORM" }
if ("poolSize" in options) {
mongoOptions["maxPoolSize"] = options["poolSize"]
}
Object.assign(mongoOptions, options.extra)
return mongoOptions
}
从源码结构看还有两个实用细节:连接串由 buildConnectionUrl 拼装,mongodb+srv 类型不拼接端口;配置了 replicaSet 时优先使用 hostReplicaSet 作为主机段;客户端会携带 driverInfo: { name: "TypeORM" } 便于在 MongoDB 服务端识别来源。
3.7 应用引导示例
import { DataSource } from "typeorm"
const myDataSource = new DataSource({
type: "mongodb",
host: "localhost",
port: 27017,
database: "test",
})
调用 await myDataSource.initialize() 后,MongoDriver.connect 会执行 MongoClient.connect(url, options) 并创建唯一的 MongoQueryRunner——MongoDB 没有 RDBMS 那样的连接池概念,因此驱动实例只持有一个查询执行器(src/driver/mongodb/MongoDriver.ts#L57-L61)。
四、定义实体与列:@ObjectIdColumn
MongoDB 实体定义与关系型数据库几乎相同,核心区别是:必须使用 @ObjectIdColumn() 代替 @PrimaryColumn 或 @PrimaryGeneratedColumn。
import { ObjectId } from "mongodb"
import { Entity, ObjectIdColumn, Column } from "typeorm"
@Entity()
export class User {
@ObjectIdColumn()
_id: ObjectId
@Column()
firstName: string
@Column()
lastName: string
}
ObjectIdColumn.ts 的实现揭示了它的本质——它只是带特殊元数据模式的普通列:
export function ObjectIdColumn(options?: ColumnOptions): PropertyDecorator {
return function (object: Object, propertyName: string) {
options ??= {} as ColumnOptions
options.primary = true // 强制为主键
options.name ??= "_id" // 数据库字段名默认 _id
getMetadataArgsStorage().columns.push({
target: object.constructor,
propertyName: propertyName,
mode: "objectId", // MongoDB 专属列模式
options: options,
} as ColumnMetadataArgs)
}
}
见 src/decorator/columns/ObjectIdColumn.ts#L11-L25。两个实用推论:
- 属性名可以任意命名(如
id而非_id),TypeORM 在查询条件中会自动把实体属性名重写为数据库的_id,并把字符串等标量转换为ObjectId实例。这一机制实现在 MongoEntityManager.replaceObjectIdProperty,会递归处理$or/$and嵌套条件; - 写入侧的
update/delete等混合条件经 convertMixedCriteria 归一化:ObjectId.isValid兼容的字符串、数字、Buffer 或ObjectId都会转换为{ _id: new ObjectId(...) }查询。
五、定义子文档(内嵌文档)
MongoDB 存储"对象中的对象",TypeORM 允许用不带 @Entity() 的普通类作为列类型,通过 @Column((type) => Class) 声明内嵌文档。
export class Profile {
@Column()
about: string
@Column()
education: string
@Column()
career: string
}
export class Photo {
@Column()
url: string
@Column()
description: string
@Column()
size: number
constructor(url: string, description: string, size: number) {
this.url = url
this.description = description
this.size = size
}
}
@Entity()
export class User {
@ObjectIdColumn()
id: ObjectId
@Column()
firstName: string
@Column()
lastName: string
@Column((type) => Profile)
profile: Profile
@Column((type) => Photo)
photos: Photo[]
}
保存该实体:
const user = new User()
user.firstName = "Timber"
user.lastName = "Saw"
user.profile = new Profile()
user.profile.about = "About Trees and Me"
user.profile.education = "Tree School"
user.profile.caret = "Lumberjack"
user.photos = [
new Photo("me-and-trees.jpg", "Me and Trees", 100),
new Photo("me-and-chakram.jpg", "Me and Chakram", 200),
]
await myDataSource.manager.save(user)
数据库中实际存储的文档为(单条嵌套文档,内嵌文档数组被整体序列化):
{
"firstName": "Timber",
"lastName": "Saw",
"profile": {
"about": "About Trees and Me",
"education": "Tree School",
"career": "Lumberjack"
},
"photos": [
{
"url": "me-and-trees.jpg",
"description": "Me and Trees",
"size": 100
},
{
"url": "me-and-chakram.jpg",
"description": "Me and Chakram",
"size": 200
}
]
}
六、用 select 投影指定字段
find* 方法的 select 选项被翻译为 MongoDB 的 projection,嵌套内嵌文档使用对象语法并扁平化为点路径投影:
const products = await myDataSource.getMongoRepository(Product).find({
select: { name: true, specs: { weight: true } },
})
上述示例只返回每个产品的 name 与 specs.weight(外加实体 id)。翻译逻辑在 MongoEntityManager.convertFindOptionsSelectToProjectCriteria:
- 普通列直接映射为
projection["列路径"] = 1; - 内嵌文档写
true时展开其全部列(columnsFromTree),写对象时递归展开为点路径; - 未知属性名(无论顶层还是内嵌层)抛出
EntityPropertyNotFoundError(src/error/EntityPropertyNotFoundError.ts),与 SQL 驱动行为一致; - 若实体主键属性名不是
_id(如id),投影会自动重写为_id。
七、MongoEntityManager 与 MongoRepository
MongoEntityManager 继承自 EntityManager(src/entity-manager/MongoEntityManager.ts),EntityManager 的大多数方法均可使用(除 RDBMS 专属的 query、transaction)。例如:
const timber = await myDataSource.manager.findOneBy(User, {
firstName: "Timber",
lastName: "Saw",
})
与 RDBMS 的 Repository 对应,MongoDB 有扩展的 MongoRepository,通过 DataSource.getMongoRepository() 获取(src/data-source/DataSource.ts#L464-L470,非 MongoDB 连接调用会抛出 "You can use getMongoRepository only for MongoDB connections."):
const timber = await myDataSource.getMongoRepository(User).findOneBy({
firstName: "Timber",
lastName: "Saw",
})
7.1 find() 中使用 MongoDB 原生查询运算符
MongoRepository.find() 的 where 直接接受 MongoDB 查询语法(对应类型见 src/find-options/mongodb/MongoFindManyOptions.ts):
等值匹配:
const timber = await myDataSource.getMongoRepository(User).find({
where: {
firstName: { $eq: "Timber" },
},
})
小于:
const timber = await myDataSource.getMongoRepository(User).find({
where: {
age: { $lt: 60 },
},
})
In:
const timber = await myDataSource.getMongoRepository(User).find({
where: {
firstName: { $in: ["Timber", "Zhang"] },
},
})
Not In:
const timber = await myDataSource.getMongoRepository(User).find({
where: {
firstName: { $not: { $in: ["Timber", "Zhang"] } },
},
})
Or:
const timber = await myDataSource.getMongoRepository(User).find({
where: {
$or: [{ firstName: "Timber" }, { firstName: "Zhang" }],
},
})
查询子文档——用点路径访问内嵌字段:
const users = await myDataSource.getMongoRepository(User).find({
where: {
"profile.education": { $eq: "Tree School" },
},
})
查询子文档数组——点路径对数组自动做元素级匹配(查询拥有 size 小于 500 照片的用户):
const users = await myDataSource.getMongoRepository(User).find({
where: {
"photos.size": { $lt: 500 },
},
})
从源码结构看,find 的完整执行链路是:where 条件经 convertFindManyOptionsOrConditionsToMongodbQuery 转换(字符串形式的 SQL where 会被忽略,因为 MongoDB 不是 SQL 数据库),再经 replaceObjectIdProperty 重写主键条件,最后交给 createEntityCursor 构建游标;skip → cursor.skip()、take → cursor.limit()、order 中的 ASC/DESC 分别转换为 1/-1 传给 cursor.sort()(convertFindOptionsOrderToOrderCriteria)。游标结果通过 DocumentToEntityTransformer 自动还原为实体,并在 toArray/next 上广播 Load 事件以触发订阅者(applyEntityTransformationToCursor)。若实体配置了软删除列,查询会自动追加 deleteDate $eq null 过滤条件(filterSoftDeleted),除非显式指定 withDeleted。
7.2 MongoDB 专属方法一览
MongoEntityManager 和 MongoRepository 都提供大量 MongoDB 专属方法,当前仓库源码中实际实现的清单如下(均以"实体类/实体名 + 原始 MongoDB 参数"的形式透传给 MongoQueryRunner):
| 方法 | 说明 |
|---|---|
createCursor |
创建查询游标,用于迭代 MongoDB 结果 |
createEntityCursor |
创建游标并将每条结果自动转换为实体模型 |
aggregate / aggregateEntity |
对集合执行聚合管道;aggregateEntity 将结果转换为实体 |
bulkWrite |
不使用流式 API 的批量写入操作 |
count / countDocuments / countBy |
统计匹配查询的文档数量 |
createCollectionIndex |
在集合上创建索引 |
createCollectionIndexes |
一次创建多个索引(MongoDB 2.6+) |
collectionIndexes |
获取集合全部索引 |
collectionIndexExists |
判断集合上是否存在指定索引 |
collectionIndexInformation |
获取集合的索引信息 |
dropCollectionIndex |
删除集合上的一个索引 |
dropCollectionIndexes |
删除集合上的所有索引 |
deleteMany / deleteOne |
删除多个/单个文档 |
distinct |
返回某键在集合中所有不重复的值 |
findOneAndDelete |
原子地查找并删除文档(操作期间持有写锁) |
findOneAndReplace |
原子地查找并替换文档 |
findOneAndUpdate |
原子地查找并更新文档 |
initializeOrderedBulkOp |
有序批量写:按添加顺序串行执行 |
initializeUnorderedBulkOp |
无序批量写:缓冲后乱序执行 |
insertMany / insertOne |
插入文档数组 / 单个文档 |
isCapped |
判断集合是否为 capped 集合 |
listCollectionIndexes |
获取集合索引信息列表(游标) |
rename |
重命名现有集合 |
replaceOne |
替换一个文档 |
updateMany / updateOne |
按过滤条件更新多个/单个文档 |
watch |
创建 Change Stream 监听集合变更 |
需要说明的是:官方文档的方法清单中还列出了 geoHaystackSearch、geoNear、group、parallelCollectionScan、reIndex 等条目,但在当前仓库的 MongoEntityManager 与 MongoRepository 源码中已检索不到这些方法。从源码结构看,它们是 MongoDB 官方 Node 驱动升级(v7)后从 Collection API 中移除的历史方法,文档尚未同步清理——实际使用时请以当前源码方法清单为准。
八、小结:验证与适用前提
- 适用前提:
type: "mongodb"的 DataSource、MongoDB Node.js 驱动 v7+;连接参数以 MongoDataSourceOptions.ts 的类型定义为准,extra可透传其余MongoClientOptions; - 不可用能力:事务与隔离级别、Query Builder、
query()原生 SQL、schema 同步、树形实体、CTE; - 建模要点:
@ObjectIdColumn强制主键且默认映射_id;内嵌文档用@Column((type) => X)声明,查询用点路径; - 功能验证:仓库在 test/functional/mongodb/ 目录下维护了 30 余个功能测试(内嵌文档、查询运算符、游标、索引等),可作为各特性真实行为的参照;本文所有连接拼装、选项白名单、投影与游标转换结论均可在 src/driver/mongodb/ 与 src/entity-manager/MongoEntityManager.ts 中逐行核对。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00