Mongoose SchemaTypes 完全指南:路径类型定义、SchemaType 选项与类型转换实战
Mongoose SchemaTypes 完全指南路径类型定义、SchemaType 选项与类型转换实战【免费下载链接】mongooseMongoDB object modeling designed to work in an asynchronous environment.项目地址: https://gitcode.com/GitHub_Trending/mo/mongooseMongoose 是运行在异步环境下的 MongoDB 对象建模工具而 SchemaTypes 正是其数据建模的基石它为模型中的每一个属性path定义类型、默认值、校验规则、getter/setter 与字段选择行为。本文以官方 SchemaTypes 文档 为核心骨架结合本仓库源码实现系统讲解什么是 SchemaType、type关键字的特殊语义、全部内置 SchemaType 的配置选项与转换规则并给出可直接复制运行的实战示例帮助你彻底掌握 Mongoose 路径类型体系。什么是 SchemaType可以把 Mongoose 的 schema 理解为 Mongoose model 的配置对象而 SchemaType 则是单个属性的配置对象。一个 SchemaType 决定了某个 path 应该是什么类型、是否有 getter/setter以及哪些值对该 path 合法。const schema new Schema({ name: String }); schema.path(name) instanceof mongoose.SchemaType; // true schema.path(name) instanceof mongoose.Schema.Types.String; // true schema.path(name).instance; // String关键要区分SchemaType 与类型本身mongoose.ObjectId ! mongoose.Types.ObjectId。SchemaType 只是给 Mongoose 使用的配置对象mongoose.ObjectId这个 SchemaType 的实例并不会真正创建 MongoDB ObjectId它只是 schema 中某个 path 的配置。从源码看所有内置 SchemaType 都注册在 lib/schema/index.js 中它们统一继承自 lib/schemaType.js 这个基类例如 lib/schema/string.js 中的SchemaString就是通过SchemaType.call(this, key, options, String, parentSchema)完成初始化并带有自己的schemaName、defaultOptions与OptionsConstructor。因此Mongoose 插件也可以注册自定义 SchemaType 来扩展这个体系。SchemaTypes 全集Mongoose 内置的全部合法 SchemaType 如下插件还可以添加自定义类型如 int32、mongoose-long 等可通过 Mongoose 插件站点搜索获取StringNumberDateBufferBooleanMixedUnionObjectIdArrayDecimal128MapSchemaUUIDBigIntDoubleInt32综合示例const schema new Schema({ name: String, binary: Buffer, living: Boolean, updated: { type: Date, default: Date.now }, age: { type: Number, min: 18, max: 65 }, mixed: Schema.Types.Mixed, union: { type: Schema.Types.Union, of: [String, Number] }, _someId: Schema.Types.ObjectId, decimal: Schema.Types.Decimal128, double: Schema.Types.Double, int32bit: Schema.Types.Int32, array: [], ofString: [String], ofNumber: [Number], ofDates: [Date], ofBuffer: [Buffer], ofBoolean: [Boolean], ofMixed: [Schema.Types.Mixed], ofObjectId: [Schema.Types.ObjectId], ofArrays: [[]], ofArrayOfNumbers: [[Number]], nested: { stuff: { type: String, lowercase: true, trim: true } }, map: Map, mapOfString: { type: Map, of: String } }); // example use const Thing mongoose.model(Thing, schema); const m new Thing; m.name Statue of Liberty; m.age 125; m.updated new Date; m.binary Buffer.alloc(0); m.living false; m.mixed { any: { thing: i want } }; m.markModified(mixed); m._someId new mongoose.Types.ObjectId; m.array.push(1); m.ofString.push(strings!); m.ofNumber.unshift(1, 2, 3, 4); m.ofDates.addToSet(new Date); m.ofBuffer.pop(); m.ofMixed [1, [], three, { four: 5 }]; m.nested.stuff good; m.map new Map([[key, value]]); m.save(callback);type关键字的特殊语义type是 Mongoose schema 中的特殊属性。当 Mongoose 在 schema 中发现名为type的嵌套属性时会假定你需要用给定类型定义一个 SchemaType// 3 个 String SchemaTypes: name, nested.firstName, nested.lastName const schema new Schema({ name: { type: String }, nested: { firstName: { type: String }, lastName: { type: String } } });因此如果你真的想定义一个名为type的属性就需要多做一些工作。例如构建一个股票持仓应用想存储资产的type股票 stock、债券 bond、ETF 等直观写法可能是const holdingSchema new Schema({ // 你期望 asset 是一个拥有 2 个属性的对象 // 但不幸的是 type 在 Mongoose 中是特殊的 // 所以 Mongoose 会把该 schema 解释为 asset 是一个字符串 asset: { type: String, ticker: String } });当 Mongoose 看到type: String时会假定你意思是asset应该是字符串而不是一个带type属性的对象。正确的定义方式如下const holdingSchema new Schema({ asset: { // 变通方法确保 Mongoose 知道 asset 是对象、 // asset.type 是字符串而不是把 asset 当作字符串 type: { type: String }, ticker: String } });更多细节可参考仓库 FAQ 文档 中关于type关键字的说明。SchemaType OptionsSchemaType 选项你可以直接用类型本身声明 schema type也可以用带type属性的对象声明const schema1 new Schema({ test: String // test 是一个 String 类型的 path }); const schema2 new Schema({ // test 对象包含 SchemaType options test: { type: String } // test 是一个 string 类型的 path });除了type属性你还可以为 path 指定附加属性。例如想在保存前把小写化字符串const schema2 new Schema({ test: { type: String, lowercase: true // 总是把 test 转换成小写 } });你可以向 SchemaType options 中添加任何自定义属性很多插件依赖自定义的 SchemaType options例如 mongoose-autopopulate 插件在 options 中设置autopopulate: true即可自动 populate 路径。Mongoose 内置支持若干 SchemaType 选项如上面示例中的lowercase。lowercase只对字符串生效有些选项对所有 schema type 通用有些则只对特定类型生效。所有 SchemaType 通用选项required: boolean 或 function若为 true 则为该属性添加 required 校验器default: 任意值或 function为该 path 设置默认值如果值是函数则使用函数返回值作为默认值select: boolean指定查询的默认投影projection行为validate: function为该属性添加校验函数get: function使用Object.defineProperty()为该属性定义自定义 getterset: function使用Object.defineProperty()为该属性定义自定义 setteralias: stringmongoose 4.10.0定义一个虚拟属性virtual用来 get/set 该 pathimmutable: boolean将 path 定义为不可变除非父文档isNew: true否则 Mongoose 禁止修改 immutable pathtransform: function调用Document#toJSON()包括对文档执行JSON.stringify()时触发const numberSchema new Schema({ integerOnly: { type: Number, get: v Math.round(v), set: v Math.round(v), alias: i } }); const Number mongoose.model(Number, numberSchema); const doc new Number(); doc.integerOnly 2.001; doc.integerOnly; // 2 doc.i; // 2 doc.i 3.001; doc.integerOnly; // 3 doc.i; // 3注意immutable的底层实现仓库 lib/helpers/query/handleImmutable.js 与 lib/helpers/schematype/handleImmutable.js 会在更新操作update 与文档保存期间把 immutable path 从修改集合中剥离从而防止被意外改写只有新建文档isNew: true时允许写入。索引选项你还可以用 schema type options 定义 MongoDB 索引index: boolean是否在该属性上定义索引unique: boolean是否定义唯一索引sparse: boolean是否定义稀疏索引const schema2 new Schema({ test: { type: String, index: true, unique: true // 唯一索引。如果指定 unique: true // 再指定 index: true 是可选的 } });各类型专属选项String详见 validation 文档lowercase: boolean是否总是对值调用.toLowerCase()uppercase: boolean是否总是对值调用.toUpperCase()trim: boolean是否总是对值调用.trim()match: RegExp创建校验器检查值是否匹配给定的正则表达式enum: Array创建校验器检查值是否在给定数组中minLength: Number创建校验器检查值长度不小于给定值maxLength: Number创建校验器检查值长度不大于给定值populate: Object设置默认 populate 选项Numbermin: Number创建校验器检查值大于等于给定最小值max: Number创建校验器检查值小于等于给定最大值enum: Array创建校验器检查值与数组中某个值严格相等populate: Object设置默认 populate 选项Datemin: Date创建校验器检查值大于等于给定最小值max: Date创建校验器检查值小于等于给定最大值expires: Number 或 String创建 TTL 索引值以秒为单位ObjectIdpopulate: Object设置默认 populate 选项各 SchemaType 实战指南String声明字符串 path既可以使用String全局构造函数也可以使用字符串Stringconst schema1 new Schema({ name: String }); // name 会被 cast 为字符串 const schema2 new Schema({ name: String }); // 等价 const Person mongoose.model(Person, schema2);如果传入的元素有toString()函数Mongoose 会调用它——除非该元素是数组或者toString()函数与Object.prototype.toString()严格相等new Person({ name: 42 }).name; // 42 作为字符串 new Person({ name: { toString: () 42 } }).name; // 42 作为字符串 // undefined如果 save() 该文档会得到 cast 错误 new Person({ name: { foo: 42 } }).name;从 lib/schema/string.js 的源码可以看到SchemaString维护了enumValues与regExp两个实例属性对应enum和match选项并支持通过SchemaString.cast(caster)静态方法整体替换 cast 函数或传入false禁用 cast仅允许null/undefined和非对象值。Number声明数字 path可以使用Number全局构造函数或字符串Numberconst schema1 new Schema({ age: Number }); // age 会被 cast 为 Number const schema2 new Schema({ age: Number }); // 等价 const Car mongoose.model(Car, schema2);下面这些值都能成功 cast 为 Numbernew Car({ age: 15 }).age; // 15 作为 Number new Car({ age: true }).age; // 1 作为 Number new Car({ age: false }).age; // 0 作为 Number new Car({ age: { valueOf: () 83 } }).age; // 83 作为 Number如果传入的对象带有返回 Number 的valueOf()函数Mongoose 会调用它并把返回值赋给该 path。null和undefined不会被 cast。NaN、能 cast 成 NaN 的字符串、数组以及没有valueOf()函数的对象都只会在验证阶段抛出 CastError——即初始化时不抛错只有验证时才抛错。Dates内置的 Date 方法 并没有被接入 Mongoose 的变更追踪逻辑。也就是说如果你在文档里用setMonth()之类的方法修改了 DateMongoose 不会感知到这次改动doc.save()也就不会持久化该修改。如果必须用内置方法修改 Date 类型请在保存前调用doc.markModified(pathToYourDate)告知 Mongooseconst Assignment mongoose.model(Assignment, { dueDate: Date }); const doc await Assignment.findOne(); doc.dueDate.setMonth(3); await doc.save(); // 这不会保存你的修改 doc.markModified(dueDate); await doc.save(); // 生效Buffer声明 Buffer path可以使用Buffer全局构造函数或字符串Bufferconst schema1 new Schema({ binData: Buffer }); // binData 会被 cast 为 Buffer const schema2 new Schema({ binData: Buffer }); // 等价 const Data mongoose.model(Data, schema2);Mongoose 可以成功地把下面的值 cast 成 bufferconst file1 new Data({ binData: test}); // {type:Buffer,data:[116,101,115,116]} const file2 new Data({ binData: 72987 }); // {type:Buffer,data:[27]} const file4 new Data({ binData: { type: Buffer, data: [1, 2, 3]}}); // {type:Buffer,data:[1,2,3]}MixedMixed 是什么都可以的 SchemaType。Mongoose 不会对 Mixed path 做任何 cast。可以用Schema.Types.Mixed或空对象字面量来定义 Mixed path下面几种写法等价const Any new Schema({ any: {} }); const Any new Schema({ any: Object }); const Any new Schema({ any: Schema.Types.Mixed }); const Any new Schema({ any: mongoose.Mixed });由于 Mixed 是无 schema 的类型你可以随意把值改成任何内容但 Mongoose 会失去自动检测并保存这些修改的能力。要告诉 Mongoose Mixed 类型的值发生了变化需要调用doc.markModified(path)并传入刚改过的 Mixed 类型的 pathperson.anything { x: [3, 4, { y: changed }] }; person.markModified(anything); person.save(); // Mongoose 会保存对 anything 的修改。为避免这些副作用也可以改用 Subdocument path。ObjectIdsObjectId 是通常用于唯一标识符的特殊类型。下面声明一个driver为 ObjectId 的 schemaconst mongoose require(mongoose); const carSchema new mongoose.Schema({ driver: mongoose.ObjectId });ObjectId是一个类ObjectId 是对象但通常被表示为字符串。用toString()把 ObjectId 转成字符串时会得到 24 位十六进制字符串const Car mongoose.model(Car, carSchema); const car new Car(); car.driver new mongoose.Types.ObjectId(); typeof car.driver; // object car.driver instanceof mongoose.Types.ObjectId; // true car.driver.toString(); // 类似 5e1a0651741b255ddda996c4BooleanMongoose 中的 Boolean 是原生 JavaScript 布尔值。默认情况下Mongoose 把下面的值 cast 为truetruetrue11yes把下面的值 cast 为falsefalsefalse00no任何其他值都会导致 CastError。你可以通过convertToTrue和convertToFalse属性修改 Mongoose 转成 true/false 的值集合这两个属性都是 JavaScript Setconst M mongoose.model(Test, new Schema({ b: Boolean })); console.log(new M({ b: nay }).b); // undefined // Set { false, false, 0, 0, no } console.log(mongoose.Schema.Types.Boolean.convertToFalse); mongoose.Schema.Types.Boolean.convertToFalse.add(nay); console.log(new M({ b: nay }).b); // false从源码看默认转换集合定义在 lib/cast/boolean.js 中convertToTrue new Set([true, true, 1, 1, yes])、convertToFalse new Set([false, false, 0, 0, no])lib/schema/boolean.js 通过Object.defineProperty把这两个集合暴露为SchemaBoolean的静态属性并额外提供了SchemaBoolean.cast(caster)静态方法可整体替换 cast 函数传false则退回严格模式只接受原生 boolean。仓库的 test/schema.boolean.test.js 有大量针对这些 cast 行为与convertToTrue/convertToFalse扩展的测试用例。ArraysMongoose 支持 SchemaType 数组和子文档subdocument数组。SchemaType 数组也叫primitive arrays基础类型数组子文档数组也叫document arrays文档数组const ToySchema new Schema({ name: String }); const ToyBoxSchema new Schema({ toys: [ToySchema], buffers: [Buffer], strings: [String], numbers: [Number] // ... 等等 });数组是特殊的因为它们隐式地有一个默认值[]空数组const ToyBox mongoose.model(ToyBox, ToyBoxSchema); console.log((new ToyBox()).toys); // []要覆盖这个默认值需要把默认值设为undefinedconst ToyBoxSchema new Schema({ toys: { type: [ToySchema], default: undefined } });注意default应用在它声明的层级上。上面的例子中default紧挨着type: [ToySchema]所以它是数组的默认值。如果把它放进方括号内它就成了数组中每个元素的默认值而数组仍保留隐式的[]默认值const ArrayDefault new Schema({ toys: { type: [String], default: undefined } }); const ElementDefault new Schema({ // 这里的 default: undefined 作用于每个字符串元素而不是 toys toys: [{ type: String, default: undefined }] }); mongoose.model(ArrayDefault, ArrayDefault); mongoose.model(ElementDefault, ElementDefault); new (mongoose.model(ArrayDefault))().toys; // undefined new (mongoose.model(ElementDefault))().toys; // []注意指定空数组等价于Mixed。下面几种写法都创建Mixed数组const Empty1 new Schema({ any: [] }); const Empty2 new Schema({ any: Array }); const Empty3 new Schema({ any: [Schema.Types.Mixed] }); const Empty4 new Schema({ any: [{}] });MapsMongooseMap是 JavaScriptMap类的子类。在 Mongoose 中map 是创建带任意键的嵌套文档的方式。注意在 Mongoose Map 中键必须是字符串这样才能在 MongoDB 中存储文档。const userSchema new Schema({ // socialMediaHandles 是一个值类型为字符串的 map。 // map 的键始终是字符串用 of 指定值的类型。 socialMediaHandles: { type: Map, of: String } }); const User mongoose.model(User, userSchema); // Map { github vkarpov15, twitter code_barbarian } console.log(new User({ socialMediaHandles: { github: vkarpov15, twitter: code_barbarian } }).socialMediaHandles);上面的例子没有显式声明github或twitter为 path但因为socialMediaHandles是 map可以存储任意键值对。不过由于它是 map你必须用.get()获取键值、用.set()设置键值const user new User({ socialMediaHandles: {} }); // 正确 user.socialMediaHandles.set(github, vkarpov15); // 也可以 user.set(socialMediaHandles.twitter, code_barbarian); // 错误myspace 属性不会被保存 user.socialMediaHandles.myspace fail; // vkarpov15 console.log(user.socialMediaHandles.get(github)); // code_barbarian console.log(user.get(socialMediaHandles.twitter)); // undefined user.socialMediaHandles.github; // 只会保存 github 和 twitter 属性 user.save();Map 类型在 MongoDB 中以 BSON 对象存储。BSON 对象的键是有序的因此 map 的插入顺序特性得以保留。Mongoose 支持特殊的$*语法来 populate map 中的所有元素。例如假设socialMediaHandlesmap 中包含一个refconst userSchema new Schema({ socialMediaHandles: { type: Map, of: new Schema({ handle: String, oauth: { type: ObjectId, ref: OAuth } }) } }); const User mongoose.model(User, userSchema);要 populate 每个socialMediaHandles条目的oauth属性应该 populatesocialMediaHandles.$*.oauthconst user await User.findOne().populate(socialMediaHandles.$*.oauth);Map 的底层实现位于 lib/schema/map.js它继承自 JavaScript 原生Map并挂接到 Mongoose 的变更追踪机制仓库 test/types.map.test.js 覆盖了 map 的读写、cast 与$*populate 场景。UUIDMongoose 还支持 UUID 类型它把 UUID 实例以 Node.js buffer 形式存储。在 Node.js 中UUID 表示为bson.Binary类型的实例并带有一个 getter在访问时把二进制转成字符串。Mongoose 在 MongoDB 中以 subtype 4 的二进制数据存储 UUID。建议在 Mongoose 中做唯一文档 id 时优先使用 ObjectId只有在确有需要时才使用 UUID。const authorSchema new Schema({ _id: Schema.Types.UUID, // 也可以写 _id: UUID name: String }); const Author mongoose.model(Author, authorSchema); const bookSchema new Schema({ authorId: { type: Schema.Types.UUID, ref: Author } }); const Book mongoose.model(Book, bookSchema); const author new Author({ name: Martin Fowler }); console.log(typeof author._id); // string console.log(author.toObject()._id instanceof mongoose.mongo.BSON.Binary); // true const book new Book({ authorId: 09190f70-3d30-11e5-8814-0f4df9a59c41 });创建 UUID 时推荐使用 Node 内置的 UUIDv4 生成器const { randomUUID } require(crypto); const schema new mongoose.Schema({ docId: { type: UUID, default: () randomUUID() } });BigIntMongoose 支持把 JavaScript BigInt 作为 SchemaType。BigInt 在 MongoDB 中以 64 位整数BSON 类型 long存储const questionSchema new Schema({ answer: BigInt }); const Question mongoose.model(Question, questionSchema); const question new Question({ answer: 42n }); typeof question.answer; // bigintDoubleMongoose 支持把 64 位 IEEE 754-2008 浮点数作为 SchemaType。Double 在 MongoDB 中以 BSON 类型 double 存储const temperatureSchema new Schema({ celsius: Double }); const Temperature mongoose.model(Temperature, temperatureSchema); const temperature new Temperature({ celsius: 1339 }); temperature.celsius instanceof bson.Double; // true下面这些值都能成功 cast 为 Doublenew Temperature({ celsius: 1.2e12 }).celsius; // 1200000000000 作为 Double new Temperature({ celsius: true }).celsius; // 1 作为 Double new Temperature({ celsius: false }).celsius; // 0 作为 Double new Temperature({ celsius: { valueOf: () 83.0033 } }).celsius; // 83 作为 Double new Temperature({ celsius: }).celsius; // null以下输入会在验证阶段导致 CastError初始化时不抛错验证时才抛错不代表数字字符串、NaN 或 null-ish 值的字符串没有valueOf()函数的对象超出 IEEE 754-2008 浮点数表示范围的值从 lib/schema/double.js 源码可见SchemaDouble的查询条件处理器$conditionalHandlers为$gt/$gte/$lt/$lte等比较操作符单独注册了 cast 逻辑并支持SchemaDouble.cast(caster)自定义 cast 函数如把 NaN cast 成 0。Int32Mongoose 支持把 32 位整数作为 SchemaType。Int32 在 MongoDB 中以 32 位整数BSON 类型 int存储const studentSchema new Schema({ id: Int32 }); const Student mongoose.model(Student, studentSchema); const student new Student({ id: 1339 }); typeof student.id; // number下面这些值都能成功 cast 为 Int32new Student({ id: 15 }).id; // 15 作为 Int32 new Student({ id: true }).id; // 1 作为 Int32 new Student({ id: false }).id; // 0 作为 Int32 new Student({ id: { valueOf: () 83 } }).id; // 83 作为 Int32 new Student({ id: }).id; // null 作为 Int32如果传入的对象带有返回 Number 的valueOf()函数Mongoose 会调用它并把返回值赋给该 path。null和undefined不会被 cast。以下输入会在验证阶段导致 CastError初始化时不抛错验证时才抛错NaN能 cast 成 NaN 的字符串没有valueOf()函数的对象必须四舍五入才能成为整数的小数超出 32 位整数范围的值从 lib/schema/int32.js 源码可以看到SchemaInt32的默认严格 caster 用INT32_MAX 0x7FFFFFFF、INT32_MIN -0x80000000做边界检查v ! (v | 0)还会校验是否为整数并且它的$conditionalHandlers额外注册了$bitsAllClear、$bitsAnyClear、$bitsAllSet、$bitsAnySet四个位运算操作符见 lib/schema/operators/bitwise.js。UnionUnionSchemaType 允许一个 path 接受多种类型。Mongoose 会尝试把值 cast 为其中一种指定类型const schema new Schema({ value: { type: Schema.Types.Union, of: [String, Number] } }); const Model mongoose.model(Model, schema); // 两种都有效 —— Mongoose 接受任意一种类型 const doc1 new Model({ value: hello }); const doc2 new Model({ value: 42 });Casting 行为当你给 Union path 赋值时Mongoose 按顺序尝试把它 cast 为of数组中的每种类型。如果值与其中某个类型精确匹配使用Mongoose 直接使用该值否则使用第一个能成功 cast 该值的类型const schema new Schema({ flexibleField: { type: Schema.Types.Union, of: [Number, Date] } }); const Model mongoose.model(Model, schema); // Number 类型 const doc1 new Model({ flexibleField: 42 }); doc1.flexibleField; // 42 (number) // 字符串 42 被 cast 为 Number第一个成功的类型 const doc2 new Model({ flexibleField: 42 }); doc2.flexibleField; // 42 (number) // Date 类型 const doc3 new Model({ flexibleField: new Date(2025-06-01) }); doc3.flexibleField; // Date 对象 // 字符串日期被 cast 为 Date const doc4 new Model({ flexibleField: 2025-06-01 }); doc4.flexibleField; // Date 对象Union 的 cast 逻辑实现于 lib/schema/union.js构造函数会校验options.of必须是非空数组否则直接抛出Union schema type requires an array of types并把每个成员通过parentSchema.interpretAsType()解释成真实的 SchemaType 实例存进this.schemaTypescast()方法依次调用每个子类型的 cast命中 val立即返回原值避免 Number 被错误 cast 成 String/Date 等否则取第一个成功 cast 的结果全部失败则抛出最后一个类型的 cast 错误。错误处理如果 Mongoose 无法把值 cast 为任何指定类型它会抛出 union 中最后一个类型产生的错误const schema new Schema({ value: { type: Schema.Types.Union, of: [Number, Boolean] } }); const Model mongoose.model(Model, schema); const doc new Model({ value: not a number or boolean }); // 抛出: Cast to Boolean failed for value not a number or booleanUnion 带选项你可以为 union 中的单个类型指定选项例如为字符串指定trimconst schema new Schema({ value: { type: Schema.Types.Union, of: [ Number, { type: String, trim: true } ] } }); const Model mongoose.model(Model, schema); const doc new Model({ value: hello }); doc.value; // hello (已 trim)查询与更新Union 类型同样适用于查询和更新。Mongoose 会根据 union 类型来 cast 查询过滤条件和更新操作const schema new Schema({ value: { type: Schema.Types.Union, of: [Number, Date] } }); const Model mongoose.model(Model, schema); await Model.create({ value: 42 }); // 用字符串查询 —— 会被 cast 为数字 const doc await Model.findOne({ value: 42 }); doc.value; // 42 // 更新 await Model.findOneAndUpdate( { value: 42 }, { value: new Date(2025-06-01) } );此外Union.prototype.toJSONSchema()见 lib/schema/union.js会把 union 输出为 JSON Schema 的anyOf结构可用于 MongoDB 的$jsonSchema校验或加密 schema 配置applySetters()也会为 union 中的每个成员依次应用其 setter 后再 cast。仓库 test/schema.union.test.js 提供了覆盖上述 cast、错误处理、选项与查询更新场景的测试。GettersGetter 之于 path 就像 virtuals 之于整个文档。例如你想把用户头像存为相对路径再在应用层拼接主机名可以这样组织userSchemaconst root https://s3.amazonaws.com/mybucket; const userSchema new Schema({ name: String, picture: { type: String, get: v ${root}${v} } }); const User mongoose.model(User, userSchema); const doc new User({ name: Val, picture: /123.png }); doc.picture; // https://s3.amazonaws.com/mybucket/123.png doc.toObject({ getters: false }).picture; // /123.png通常只在基础类型 path上使用 getter而不是数组或子文档。因为 getter 会覆盖访问 Mongoose path 时返回的内容在对象上声明 getter 可能会移除 Mongoose 对该 path 的变更追踪const schema new Schema({ arr: [{ url: String }] }); const root https://s3.amazonaws.com/mybucket; // 不好不要这样做 schema.path(arr).get(v { return v.map(el Object.assign(el, { url: root el.url })); }); // 之后 doc.arr.push({ key: String }); doc.arr[0]; // undefined因为每次访问 doc.arr 都会创建新数组不要像上面那样在数组上声明 getter而应该在url字符串上声明 getter。如果确实需要在嵌套文档或数组上声明 getter请格外小心const schema new Schema({ arr: [{ url: String }] }); const root https://s3.amazonaws.com/mybucket; // 正确替代在 arr 上声明 getter 的做法 schema.path(arr.0.url).get(v ${root}${v});用 Schema 作为路径类型要把某个 path 声明为另一个 schema将type设置为子 schema 的实例即可。要基于子 schema 的形状设置默认值直接设置一个默认值文档创建期间该值会先按子 schema 定义被 cast 再设置const subSchema new mongoose.Schema({ // 这里放一些 schema 定义 }); const schema new mongoose.Schema({ data: { type: subSchema, default: {} } });创建自定义 SchemaTypesMongoose 可以通过自定义 SchemaTypes 进行扩展完整指南见 Custom SchemaTypes 文档。你也可以在插件站点搜索兼容的类型例如 mongoose-long、mongoose-int32、mongoose-function 等。所有自定义类型最终都要像内置类型一样提供schemaName、cast 逻辑与OptionsConstructor可参考 lib/schema/index.js 中内置类型的注册方式。schema.path()函数schema.path()返回指定 path 实例化后的 schema typeconst sampleSchema new Schema({ name: { type: String, required: true } }); console.log(sampleSchema.path(name)); // 输出类似: /** * SchemaString { * enumValues: [], * regExp: null, * path: name, * instance: String, * validators: ... * } */可以用这个函数检查某个 path 的 schema type包括它有哪些校验器以及类型是什么。例如前文验证schema.path(name) instanceof mongoose.Schema.Types.String就是通过它实现的。进一步阅读与下一步围绕 SchemaTypes 体系建议按以下顺序继续深入本仓库校验Validationrequired、enum、min/max、match等内置校验器的完整说明与 CastError 行为Schema 指南schema 定义、type关键字与 virtuals 的更多细节Populatepopulate选项与 Map 的$*populate 语法Subdocs子文档document array 与子文档变更追踪Dates 教程Date 类型的更多使用细节自定义 SchemaTypes 指南编写插件级自定义类型Connections在掌握 SchemaTypes 后学习连接管理也是官方文档的下一步章节掌握 SchemaTypes 之后就可以继续学习 Mongoose 的 Connections搭建完整的异步数据访问链路了。【免费下载链接】mongooseMongoDB object modeling designed to work in an asynchronous environment.项目地址: https://gitcode.com/GitHub_Trending/mo/mongoose创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考