Schema Type

  • String
  • Number
  • Date
  • Buffer
  • Boolean
  • Mixed
  • Decimal Type (在3.4中新增,最多支持34位小数,并且存储的是实际值而不是浮点数)
  • Object
  • Objectid
  • Array

Usage notes

Date

内建的 ```Date``` 方法没有被纳入 ```Mongoose``` 。所以如果使用像 ```setMonth``` 这样的方法是不会执行的,如果一定要使用需要通过 ```markModified``` 方法告诉 ```Mongoose``` 。
1
2


var Assignment = mongoose.model(‘Assignment’, { dueDate: Date });
Assignment.findOne(function (err, doc) {
doc.dueDate.setMonth(3);
doc.save(callback); // THIS DOES NOT SAVE YOUR CHANGE

doc.markModified(‘dueDate’);
doc.save(callback); // works
})

1
2
### Mixed
当改变了 Mixed 元素的结构类型时,需要通过 ```markModified``` 函数告诉 ```Mongoose``` 后才会自动生效。

person.anything = { x: [3, 4, { y: “changed” }] };
person.markModified(‘anything’);
person.save(); // anything will now get saved

1
2
3
4
5
6
7
8
### ObjectId
声明时需要使用 ```Schema.Types.ObjectId```。

### Arrays
当将元素声明为 ```Array``` 以后,默认值会是空数组 ```[]```,如果想修复这个问题,需要添加 ```default``` 属性。被声明为 ```Array``` 的元素如果被指定为 ```required``` ,那么至少需要一个元素在其中。

## Schema Type handle definition
在 ```Schmea``` 中对于每一个元素都有一些定义好的属性供开发者使用。可以直接定义也可以通过 ```path``` 定义。

// directly
var schema = new Schema({
test: {
type: String,
lowercase: true
}
});

// use path
var schema2 = new Schema({test: String});

schema2.path(test).lowercase(true);

1
2
3
4
5
6
7
8
9
10

### Common
+ **default(val)**

这个属性就是为元素添加默认值,只要在修改之前都会是这个值。
对于文档里提到的对于 ```mixed``` 属性,如果不设置特殊的函数返回默认值,那么多个实例会指向第一个实例。但是我在 ```4.11.7``` 版本测试,这个问题已经得到修复。

+ **validate(obj, [errorMsg], [type])**

为元素添加检验器,来检验输入的值是否符合要求。第一个参数是检验器,支持 ```RegExp```, ```Function```, ```Object```。正则表达式可以进行最简单的字面检验,函数可以进行较为复杂的逻辑检验,对象可以支持多个检验器的组合,并且可以携带错误信息。
var many = [
    { validator: validator, msg: 'uh oh' }
  , { validator: anotherValidator, msg: 'failed' }
]
new Schema({ name: { type: String, validate: many }});
1
2
3
4
5
更加细节的是,错误信息可以自定义,当然如果没有自定义,内部为准备了一些错误信息模板。自定义错误信息中可以使用模板来获得一些内部属性。

+ **{PATH} :** 非法的 document path
+ **{VALUE} :** 非法的值
+ **{TYPE} :** 检验器的类型,比如 ```Regexp```, ```min```, ```user defined
+ **{MIN} :** 检验器设定的最大值 + **{Max} :** 检验器设定的最小值 同时也提供了异步检验器,可以通过设置
属性来告诉 ```Mongoose``` 这是一个异步检验器。在回调中需要返回 ```true``` 或者 ```false``` 来告诉检验器是否成功。**(利用这个属性可以方便的写一个如果不存在则存入数据库的逻辑。)** 这个方法会在 ```save``` 动作之前执行,如果需要的话也可以自己进行[调用](http://mongoosejs.com/docs/api.html#document_Document-validate),```document.validate(function(err){})```。 如果在 ```save``` 之前检验器失败,但是没有错误处理的话,异常会被先抛到 ```Model``` 再到 ```db connection``` ,可以通过监听 ```error``` 捕获。
1
2
3
4

+ **get(fn)**

为元素添加返回转换器,可以对元素的内容进行转换以后再返回。转换函数中可以接受两个值,第一个是需要过滤的参数,第二个是这个元素对应的 ```SchemaType```,可以使用```SchemaType``` 来定制一些功能。官方文档中举了一个日期处理和信用卡卡号隐藏中间几位数字的例子,还是蛮实用的。

function inspector (val, schematype) {
if (schematype.options.required) {
return schematype.path + ‘ is required’;
} else {
return schematype.path + ‘ is not’;
}
}

var VirusSchema = new Schema({
  name: { type: String, required: true, get: inspector },
  taxonomy: { type: String, get: inspector }
})

var Virus = db.model('Virus', VirusSchema);

Virus.findById(id, function (err, virus) {
  console.log(virus.name);     // name is required
  console.log(virus.taxonomy); // taxonomy is not
})
1
2
3
4
5
6
7
+ **set(fn)**

为元素添加保存转换器,先将元素转换成相应格式以后再存入数据库。同样定义 ```get``` 方法, ```set``` 方法的转换函数也支持两个参数,第一个是需要过滤的参数,第二个是这个元素对应的 ```SchemaType```,可以使用```SchemaType``` 来定制一些功能。

+ **select(bool)**

用来决定该元素是否要包含在搜索结果当中,但是这个属性会被 ```query``` 级别的声明覆盖。
T = db.model('T', new Schema({ x: { type: String, select: true }})); T.find(..); // field x will always be selected .. // .. unless overridden; T.find().select('-x').exec(callback);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
     
+ **index(options)**

来定义是否为该元素添加索引,支持 ```Object```, ```Boolean```, ```String```, ```Object``` 用来定义复合索引。**(先留flag,后面会来填MongoDB index的相关问题)**

+ **required(required, [options.isRequired], [options.ErrorConstructor], [message])**

为元素添加存在检查器,算是检查器的一个特例,所以同样有错误信息等。并且 ```required``` 属性同样支持函数来自定义存在检查,如果没有进行自定义那么 ```Mongoose``` 会检查这个元素的值是否等于 ```null``` 或者 ```undefined``` 来判断是否存在。**(但是这边的自定义功能感觉和 ```validate``` 有点冗余)**

+ **sparse(bool)**

为元素添加稀疏索引。**作用是当该元素为空是不进入索引。**

+ **unique(bool)**

为元素添加唯一索引。**作用是只允许一条索引字段为空的记录存在,之后就不允许插入了。再次插入 记录时会报错。**

+ **text(bool)**

为元素添加全文索引。**(全文索引的坑下次和符合索引一起填。)**

### String

+ **checkRequired(value, doc)**

对于 ```String``` 属性特殊定制的 ```required``` 属性,```required``` 会直接调用字符串的 ```checkRequired``` 方法。会针对字符串,不仅检查是否为空,还会检查长度、类型和原型链,空字符串会被判 ```fail``` 。

+ **enum([args...])**

一种特殊的检查器,会检查输入的字符串是否在规定的串中,同样有错误信息。
var states = ['opening', 'open', 'closing', 'closed'] var s = new Schema({ state: { type: String, enum: states }}) var M = db.model('M', s) var m = new M({ state: 'invalid' }) m.save(function (err) { console.error(String(err)) // ValidationError: `invalid` is not a valid enum value for path `state`. m.state = 'open' m.save(callback) // success }) // or with custom error messages var enum = { values: ['opening', 'open', 'closing', 'closed'], message: 'enum validator failed for path `{PATH}` with value `{VALUE}`' } var s = new Schema({ state: { type: String, enum: enum }) var M = db.model('M', s) var m = new M({ state: 'invalid' }) m.save(function (err) { console.error(String(err)) // ValidationError: enum validator failed for path `state` with value `invalid` m.state = 'open' m.save(callback) // success })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
    
+ **lowercase(bool)**

一种特殊的 ```set``` 属性,会将输入的字符串全部转为小写之后再存入数据库。

+ **uppercase(bool)**

一种特殊的 ```set``` 属性,会将输入的字符串全部转为大写之后再存入数据库。

+ **match(regExp, [message])**

一种特殊的检查器,会检查输入的字符串是否满足正则表达式。空字符串和 ```null``` 会通过这个检查器,所以如果要过滤这些特殊情况还需要添加 ```required``` 属性。

+ **maxlength(value, [message])**

一种特殊的检查器,会检查输入的字符串的长度是否比阈值小。会对应错误信息模板中的 ```{MAXLENGTH}``` 。

+ **minlength(value, [message])**

一种特殊的检查器,会检查输入的字符串的长度是否比阈值大。会对应错误信息模板中的 ```{MINLENGTH}``` 。

+ **trim(bool)**

一种特殊的 ```set``` 属性,会将输入字符串先修剪两端的空格再存入数据库。

### Number

+ **checkRequired(value, doc)**

对于 ```Number``` 属性特殊定制的 ```required``` 属性,```required``` 会直接调用字符串的 ```checkRequired``` 方法。不仅检查是否为 ```null``` 还会检查类型和原型链。

+ **max(maximum, [message])**

一种特殊的检查器,会检查输入的数字是否比阈值小。会对应错误信息模板中的 ```{MAX}``` 。

+ **min(value, [message])**

一种特殊的检查器,会检查输入的数字是否比阈值大。会对应错误信息模板中的 ```{MIN}``` 。

### Date

+ **checkRequired(value, doc)**

对于 ```Date``` 属性特殊定制的 ```required``` 属性,```required``` 会直接调用字符串的 ```checkRequired``` 方法。不仅检查是否为 ```null``` 还会检查类型和原型链。

+ **expires(when)**

为元素添加 ```TTL Index```,这个属性是 ```Date``` 独有的。传入的属性可以是秒数,也可是比较友好的字符串形式。
// expire in 24 hours new Schema({ createdAt: { type: Date, expires: 60*60*24 }}); // expire in 24 hours new Schema({ createdAt: { type: Date, expires: '24h' }}); // expire in 1.5 hours new Schema({ createdAt: { type: Date, expires: '1.5h' }});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    
+ **max(maximum, [message])**

一种特殊的检查器,会检查输入的数字是否比阈值小。会对应错误信息模板中的 ```{MAX}``` 。输入的值必须是 ```Date``` 类型。

+ **min(maximum, [message])**

一种特殊的检查器,会检查输入的数字是否比阈值大。会对应错误信息模板中的 ```{MIN}``` 。输入的值必须是 ```Date``` 类型。

### Object

+ **auto(bool)**

如果打开会自动添加 ```ObjectId``` 到元素的值中。

## Custom
在 ```Mongoose 4.4.0``` 中,支持了自定义 ```Schema Type``` 的功能,新建的 ```Schema Type``` 需要继承自 ```mongoose.SchemaType``` ,与 ```mongoose.SchemaTypes``` 的原型链保持一致,并且需要实现 ```cast()``` 方法。

function Int8(key, options) {
mongoose.SchemaType.call(this, key, options, ‘Int8’);
}
Int8.prototype = Object.create(mongoose.SchemaType.prototype);

// cast() takes a parameter that can be anything. You need to
// validate the provided val and throw a CastError if you
// can’t convert it.
Int8.prototype.cast = function(val) {
var _val = Number(val);
if (isNaN(_val)) {
throw new Error(‘Int8: ‘ + val + ‘ is not a number’);
}
_val = Math.round(_val);
if (_val < -0x80 || _val > 0x7F) {
throw new Error(‘Int8: ‘ + val +
‘ is outside of the range of valid 8-bit ints’);
}
return _val;
};

// Don’t forget to add Int8 to the type registry
mongoose.Schema.Types.Int8 = Int8;

var testSchema = new Schema({ test: Int8 });
var Test = mongoose.model(‘Test’, testSchema);

var t = new Test();
t.test = ‘abc’;
assert.ok(t.validateSync());
assert.equal(t.validateSync().errors[‘test’].name, ‘CastError’);
assert.equal(t.validateSync().errors[‘test’].message,
‘Cast to Int8 failed for value “abc” at path “test”‘);
assert.equal(t.validateSync().errors[‘test’].reason.message,
‘Int8: abc is not a number’);

1
2
3
4
5

## Creating from ES6 Classes Using
```Mongoose``` 同样支持了 ```ES6``` 的 ```Class``` 特性,可以使用 ```Class``` 新建类之后使用 ```loadClass()``` 导入,

+ ```getter/setter``` 函数对应 ```get/set
  • 类方法对应
    的方法
    1
    + 静态方法对应 ```Model``` 静态方法。

const schema = new Schema({ firstName: String, lastName: String });

class PersonClass {
// fullName becomes a virtual
get fullName() {
return ${this.firstName} ${this.lastName};
}

set fullName(v) {
const firstSpace = v.indexOf(‘ ‘);
this.firstName = v.split(‘ ‘)[0];
this.lastName = firstSpace === -1 ? ‘’ : v.substr(firstSpace + 1);
}

// getFullName() becomes a document method
getFullName() {
return ${this.firstName} ${this.lastName};
}

// findByFullName() becomes a static
static findByFullName(name) {
const firstSpace = name.indexOf(‘ ‘);
const firstName = name.split(‘ ‘)[0];
const lastName = firstSpace === -1 ? ‘’ : name.substr(firstSpace + 1);
return this.findOne({ firstName, lastName });
}
}

schema.loadClass(PersonClass);
var Person = db.model(‘Person’, schema);

Person.create({ firstName: ‘Jon’, lastName: ‘Snow’ }).
then(doc => {
assert.equal(doc.fullName, ‘Jon Snow’);
doc.fullName = ‘Jon Stark’;
assert.equal(doc.firstName, ‘Jon’);
assert.equal(doc.lastName, ‘Stark’);
return Person.findByFullName(‘Jon Snow’);
}).
then(doc => {
assert.equal(doc.fullName, ‘Jon Snow’);
});
```

Comments

2017-08-16

⬆︎TOP