记录 mongose 使用过程中遇到的应用场景

1.mongoose 预定义模式修饰符

对增加的数据进行预格式化。

  • lowercase
  • uppercase
  • trim 去掉左右两边的空格
    1
    2
    3
    4
    5
    6
    var TestSchema = mongoose.Schema({
    name:{
    type:String,
    trim:true //定义 mongoose 修饰符,去掉空格
    }
    })
    ## 2.mongoose 自定义修饰符 setter 和 getter 通过 set 在添加数据的时候对数据进行格式化,或者在实例获取数据的时候对数据进行格式化。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var TestSchema = mongoose.Schema({
    url:{
    type:String,
    set(params){ //定义 mongoose 修饰符,去掉空格
    if(!params){
    return ''
    }else{
    if(params.indexOf('http://')!=0&&params.indexOf('https://')!=0){
    return 'http://' + params;
    }
    }
    }
    }
    })
    get 同理,但不建议使用 get 。

3.mongoose 数据校验

简单的有required 也可以自定义 更多

4.索引

设置索引的目的是为了优化查询的速度。 1. 基础索引 可以在定义 Schema 的时候指定创建索引

1
2
3
4
5
6
7
8
9
10
var TestSchema = mongoose.Schema({
name:{
type:String,
unique:true //唯一索引
},
sex: {
type:String
index:true //普通索引
}
})
2. MongoDB shell创建索引
1
db.factories.ensureIndex( { addr : 1 } );

5.扩展 mongoose 的静态方法和实例方法

在定义Schema的时候扩展数据库操作方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 // 静态方法
TestSchema.statics.findBySex = function(sex,cb){
//通过 find 方法获取 sex 的数据
this.find({"sex":sex},function(err,data){
cb(err,data)
})
}
// 静态方法调用,model直接调用
var Test = mongoose.model('Test',TestSchema);
test.findBySex('female')
// 实例方法
var animalSchema = new Schema({ name: String, type: String });
animalSchema.methods.findSimilarTypes = function(cb) {
return this.model('Animal').find({ type: this.type }, cb);
};
// 实例方法调用,
var Animal = mongoose.model('Animal', animalSchema);
var dog = new Animal({ type: 'dog' });
// 必须先初始化一个实例
dog.findSimilarTypes(function(err, dogs) {
console.log(dogs); // woof
});

6.ObjectId()

_id: new mongoose.Types.ObjectId()

7.对文档中数组属性添加记录

相关方法: | 方法名 | 描述 | | ---- | --- | | \(pull | 从数组field内删除一个等于value的值 | | pullAll | 用法同\)pull一样,可以一次删除数组内的多个值。 | | $push | 把value追加到field里 | | \(pushAll | 用法同\)push一样,只是$pushAll一次可以追加多个值到一个数组字段内。 | | $addToSet | 加一个值到数组内,而且只有当这个值不在数组内才增加。 | | \(pop | 删除数组内的一个值 | 常用的有\)addToSet和\(push使用如下:

1
2
TestModel.updateOne({'_id':_id},{$addToSet:{fass:'wys'}})
TestModel.updateOne({'_id':_id},{$push:{fass:'wys'}})
两者区别,\)addToSet 不会添加重复的记录,$push 在数组尾部添加记录,可能重复。

8.Populaton 填充

将一个文档中的一部分字段,填充到另一个文档一个字段中 或者填充到另一个文档的多个字段中

以下代码定义了两个Schema ,注意 Story 的author 字段类型为 ref 指向的Person 文档 的_id .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var personSchema = db.Schema({
_id: db.Schema.Types.ObjectId,
name: String,
age: Number,
stories: [{ type: db.Schema.Types.ObjectId, ref: 'Story' }]
});

var storySchema = db.Schema({
author: { type: db.Schema.Types.ObjectId, ref: 'Person' },
title: String,
fans: [{ type: db.Schema.Types.ObjectId, ref: 'Person' }]
});

var Story = db.model('Story', storySchema);
var Person = db.model('Person', personSchema);
下面插入一条记录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var author = new Person({
_id: new mongoose.Types.ObjectId(),
name: 'Ian Fleming',
age: 50
});

author.save(function (err) {
if (err) return handleError(err);

var story1 = new Story({
title: 'Casino Royale',
author: author._id // assign the _id from the person
});

story1.save(function (err) {
if (err) return handleError(err);
// 保存成功
});
});
现在有了数据,来使用 population 填充查询
1
2
3
4
5
6
7
Story.
findOne({ title: 'Casino Royale' }).
populate('author').
exec(function (err, story) {
if (err) return handleError(err);
console.log(story);
});
查询结果如下
1
2
3
4
5
6
7
8
9
10
{ fans: [],
_id: 5dc3881b53c06d363aa816bb,
title: 'Casino Royale',
author:
{ stories: [],
_id: 5dc3881b53c06d363aa816ba,
name: 'Ian Fleming',
age: 50,
__v: 0 },
__v: 0 }
注意:author 字段的 _id 属性被根据该 _id 属性查询出来的 author 文档所填充。 如果我们不需要填充整个文档,而是只需要部分字段,可以在 population 中写入第二个参数指定填充字段:
1
2
3
4
5
6
7
Story.
findOne({ title: /casino royale/i }).
populate('author', ['name','age']). // 设置只填充 name、age 字段
exec(function (err, story) {
if (err) return handleError(err);
console.log(story);
});
此时的填充结果为:
1
2
3
4
5
6
7
8
{ fans: 
[ 3dc3881b53c06d363aa816bb,
5dc3903d6b4365368f3c4074,
5dc3903d6b4365368f3c4074 ],
_id: 5dc3881b53c06d363aa816bb,
title: 'Casino Royale',
author: { _id: 5dc3881b53c06d363aa816ba, name: 'Ian Fleming', age: 50 },
__v: 0 }
上面的示例都是只对 author 一个字段进行填充,如果想对多个字段填充,可以这样写:
1
2
3
4
5
Story.
find(...).
populate('fans').
populate('author').
exec();
注意:如果对同一路径 populate() 两次,只有最后一次生效。
1
2
3
4
5
6
7
// 第二个 `populate()` 覆盖了第一个,因为它们都填充 fans
Story.
find().
populate({ path: 'fans', select: 'name' }).
populate({ path: 'fans', select: 'email' });
// 以上代码等同于:
Story.find().populate({ path: 'fans', select: 'email' });
加入 Query 条件与其他选项
1
2
3
4
5
6
7
8
9
10
Story.
find(...).
populate({
path: 'fans',
match: { age: { $gte: 20 }}, // age >= 21
// 指定需要 name,age 字段而不要_id字段
select: 'name age -_id',
options: { limit: 5 } // 限制数量5条
}).
exec();
执行结果:
1
2
3
4
5
{ fans: [ { name: 'zhu', age: 20 }, { name: 'zhu', age: 20 } ],
_id: 5dc3881b53c06d363aa816bb,
title: 'Casino Royale',
author: 5dc3881b53c06d363aa816ba,
__v: 0 }

9.mongoose 中使用聚合管道连表查询

1
2
3
4
5
6
7
8
9
10
11
12
13
TestModel.aggregate([
{
$lookup:
{
from: <collection to join>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
as: <output array field>
}
}
],(err,doc)=>{

})