Mongoose是一个文档对象模型(ODM)库,它为MongoDB Node.js原生驱动程序提供更多的功能。在大多数情况下,它被用来把结构化的模式应用到一个MongoDB的集合,并提供了验证和类型转换的好处。
Mongoose通过实现构建器对象,让你把其他命令灌入查找,更新,保持,删除,聚合和其他数据库操作,这简化了构造数据库调用的一些负责性,并可用使代码更容易实现。
1,了解Mongoose
Mongoose是一种ODM库,它包含在MongoDB Node.js驱动程序外面。它提供了一种基于模式的解决方案,来存储在MongoDB数据库中的数据进行建模。
使用Mongoose的主要好处如下:
- 你可用为文档创建一个模式结构
- 可以对模型中的对象/文档进行验证
- 应用程序数据可以通过类型强制转换成对象模型
- 可以使用中间件来应用业务逻辑挂钩。
- 在某些方面,Mongoose比Mongo Node.js原生驱动程序的使用更容易一点。
不过,使用Mongoose也有一些缺点:
- 你必须提供一个模式,在MongoDB并不需要它的时候,这并不总是最好的选择
- 在特定的操作上,如数据存储,它似乎并不如原生驱动程序执行得那样好。
其他对象
Mongoose位于Mongo Node.js原生驱动程序之上,并用几个不同的方法扩展了其功能。首先,它增加了一些新的对象,Schema,Model和Document,它们提供必需的功能来实现ODM验证。
你可以使用Scheme对象来定义结构化的模式集合中的文档。它允许你定义包括的字段和类型,唯一性,索引和验证。Model对象作为集合中所有文档的表示。Document对象作为集合中单个文档的表示。
Mongoose还把用于实现查询和聚合参数的标准函数包装到新的对象Query和Aggregate中,它们让你能够在一系列方法的调用中应用这些数据库操作的参数,最后才执行这些函数。这可以使实现代码及复用这些对象的实例来执行多个数据库操作更容易。
2,利用Mongoose连接到MongoDB数据库
利用Mongoose连接到MongoDB数据库的方法与在Node.js 中连接到MongoDB的方法非常相似,具体如下:
connect(url,options,[callback]);
一个使用Mongoose连接到MongoDB数据库的例子:
var mongoose = require('mongoose');//从MongoDB根目录中请求mongoose模块
mongoose.connect('mongodb://localhost/test'); //连接数据库
mongoose.connection.on('open',function(){ //Connection对象发出open事件
console.log(mongoose.connection.collection);
mongoose.connection.db.collection("agger").find({}).toArray(function(err,res){
console.log(res);
mongoose.disconnect();
});
});
3,定义模式
在使用Mongoose时,你经常需要实现模式。模式为集合中的文档定义字段和字段类型。如果你的数据是被结构化成支持模式的,这是非常有用的,因为你可以对对象进行验证和类型转换以符合模式的要求。
对于在模式中的每个字段,你都需要定义一个特定的值类型。受支持的类型如下:
- String(字符串)。
- Number(数值)。
- Boolean 或 Boll(布尔)。
- Array(数组)
- Buffer(缓冲区)
- Date(日期)
- ObjectId或Oid
- Mixed(混合)。
需要为你计划使用的每个不同的文档类型都定义一个模式。此外,你应该只在每个集合中存储一个文档类型。
3.1,了解路径
Mongoose使用术语path定义访问主文档和子文档字段的路径。例如,如果文档中有一个名为name的字段,它是一个具有title,first和last属性的子文档,下面是所有路径:
name
name.title
name.first
name.last
3.2,创建一个模式定义
要为模型定义一个模式,你需要创建一个Schema对象的新实例。Schema对象接受一个描述模式的definition对象作为第一个参数和options对象作为第二个参数:
new Schema(definition,options)
options对象定义与MongoDB服务器上的集合交互。下表列出了大多数常用的选项。
选项 | 说明 |
autoIndex | 一个布尔值,如果为true,则表示对集合的自动索引功能已开启。默认值为true |
bufferCommands | 一个布尔值,如果为true,则表示由于连接问题而无法完成的命令被缓存。默认值为true |
capped | 指定在封顶集合中支持的最大文件数 |
collection | 指定用于此Schema模型的集合名称。Mongoose编译模式模型时,会自动连接到该集合 |
id | 一个布尔值,如果为true,则使模型中的文档有对应于该对象的_id值的id获取器。默认值是true |
_id | 一个布尔值,如果为true,则将导致Mongoose自动为你的文档分配_id字段。默认值为true |
read | 指定副本的读取首选项。值可以是primary,primaryPreferred,secondary,secondaryPreferred或nearest |
safe | 一个布尔值,如果为true,则将导致Mongoose应用一个写入关注到更新数据库的请求。默认值为true |
strict | 一个布尔值,如果为true,则表示没有出现在定义的模式中的对象传入属性不会被保存到数据库中。默认值为true |
例如,要为一个名称为students的集合创建模式,它有一个String类型name字段,一个Number类型的average字段和一个类型为Number数组的scores字段,你可以使用下面的语句:
var schema = new Schema({
name:String,
average:Number,
scores:[Number]
},{collection:'students'});
3.3,把索引添加到一个模式
你可能要给自己经常使用来查找文档的特定字段分配索引。你可以在定义模式时或使用index(fields)命令把索引应用到模式对象。例如,下面两种方法都能把索引添加到name字段,按升序排列:
var schema = new Schema({
name:{type:String,index:1}
};);
//或
var schema = new Schema({name:String});
schema.index({name:1});
可以使用indexes()方法获得一个模式对象的索引字段列表。例如:
schema.indexes();
3.4,实现字段的唯一性
你可以指定一个字段的值必须在一个集合中唯一,这意味着没有其他的文档可以有相同的该字段的值。你可以通过在Schema对象定义中添加unique属性实现这一点。例如,要为name字段添加index(索引),并使它在集合中唯一,可以使用下面的语句:
var schema = new Schema({
name:{type:String,index:1,unique:true}
});
3.5,强制字段的必须性
当你为模型创建Document对象的一个新实例时,可以指定某个字段必须被包括在内。默认情况下,如果你创建一个Document实例时不指定字段,则创建的对象就没有字段。对于必须存在于模型中的字段,需要在定义模式时添加所需的属性。例如,要为name字段添加一个索引,确保唯一性,并迫使在集合中包含它,你可以使用下面的语句:
var schema = new Schema({
name:{type:String,index:1,unique:true,required:true}
});
你可以利用requiredPaths()方法来获得一个Schema对象必需的字段列表。例如:
Schema.requiredPaths()
3.6,添加Schema对象的方法
Mongoose模式使你能够在模型的Schema对象中添加在文档对象中自动可用的方法。这使你可用利用Document对象调用这些方法。定义一个方法如下所示:
var schema = new Schema({
first:String,
last:String
});
scheam.methods.fullName = function(){
return this.first + " " + this.last;
};
3.7,在数据库上实现一个模式
如下是一个定义一个模式的例子:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var wordSchema = new Schema({
word:{type:String,index:1,required:true},
first:{type:String,index:1},
last:String,
size:Number,
letters:[String],
stats:{
vowels:Number,consonants:Number
},
charsets:[{type:String,chars:[String]}]
},{collection:'word_stats'});
wordSchema.methods.startsWith = function(letter){
return this.first === letter;
};
exports.wordSchema = wordSchema;
console.log("required Paths:");
console.log(wordSchema.requiredPaths());
console.log("Indexes:");
console.log(wordSchema.indexes());
结果:
required Paths:
[ 'word' ]
Indexes:
[ [ { word: 1 }, { background: true } ],
[ { first: 1 }, { background: true } ] ]
4,编译模型
一旦你定义了Schema对象模型,就需要把它编译成一个Model对象。当Mongoose编译模型时,它使用由mongoose.connect()建立的到MongoDB数据库的连接,以确保在应用更改时,集合已创建并具有适当的索引,且设置了必需性和唯一性。
编译后的Model对象的行为方式与MongeDB中的Collection对象大致相同,它提供了在模型中并随后在MongoDB集合中访问,更新和移除对象的功能。
要编译模型,应使用mongoose模块中的model()方法。model()方法的语法如下:
model(name,[schema],[collection],[skipInit])
name参数是以后用model(name)发现该模型可以使用的字符串。schema参数是上一节中所讨论的Schema对象。collection参数是要连接的集合名(如果Schema对象中没有指定一个集合)。skipInit选项是一个布尔值,默认为false。如果为true,则初始化过程被跳过,并创建了没有连接到数据库的简单的Model对象。
以下是编译模型的一个例子
var Words = mongoose.model('Words',wordSchema);
然后,你就可以在任何时间通过以下语句访问编译后的Model对象:
mongoose.model('Words')
5,了解Query对象
一旦把一个Schema对象编译成一个Model对象,你就完全准备好开始在模型中访问,添加,更新和删除文档了,这使得对底层的MongoDB数据库执行修改。然而,在你转到这方面之前,你需要了解Mongoose提供的Query对象的性质。
许多在Model对象中的方法,与在MongoDB Node.js中的Collection对象的方法匹配。例如,有find(),remove(),update(),count(),distinct()和aggregate()方法。这些方法的参数,在大多数情况下,与Collection对象的参数完全相同,但有一个主要的区别:callback参数。
使用Mongoose Model对象时,你既可以传入callback函数,也可以在方法的参数中忽略它。如果传入callback函数,该方法的行为会与你的期望一致。将请求发送到MongoDB,并且在callback函数中返回结果。
但是,如果你没有传入一个callback函数,MongoDB请求不会发送;相反,会返回一个Query对象,允许你在执行它之前添加额外的功能要求。然后,当你准备好执行数据库调用时,可以在Query对象上使用exec(callback)方法。
为了更好地理解这一点,下面的Mongoose find()请求例子中使用了与原生驱动程序相同的语法:
model.find({{value:{$gt:5}},{sort:{[['value',-1]]},fields:{name:1,title:1,value:1}}}
function(err,res){}
);
不过,当你使用Mongoose时,你可以使用下面的代码单独定义所有的查询选项:
var query = model.find({});
query.where('value').gt(5);
query.sort('-value');
query.select('name title value');
query.exec(function(err,results){});
该model.find()调用返回一个Query对象,而不是执行find()方法,因为没有指定回调函数。请注意,query属性和options属性被分解到随后在Query对象上的方法调用中。然后,一旦Query对象被完全建立,则exec()方法被调用,且callback函数被传递进去。
还可以把Query对象的方法串在一起,如下面的例子所示:
var query = model.find({}).where('value').gt(5).sort('-value').select('name title value').exec(function(err,results){});
当exec()被调用时,Mongoose库建立必要的query和options参数,然后对MongoDB执行原生调用。回调函数返回结果。
5.1,设置查询数据库操作
每个query对象都必须有一个与之关联的数据库操作。对数据库的操作决定了连接到数据库时采取的行动——从发现文档到存放文档。有两种方法可以将数据库操作分配给一个Query对象。一种是从Model对象调用操作,而不是指定一个回调函数。返回的Query对象拥有分配给它的操作。例如:
var query = model.find();
一旦你有了一个Model对象,就可以通过调用Query对象上的方法更改应用的操作。例如,下面的代码创建了一个Query对象,它首先应用count()操作,然后是一个find()操作。where()子句同时被应用于两者:
var query = model.count();
query.where('value').lt(5);
query.exec(function(){});
query.find();
query.exec(function(){});
这使得你可以动态地重用相同的Query查询对象执行多个数据库操作。下表列出了可以在Query对象上调用的方法。你也可以在编译后的Model对象上使用这些方法,这可以通过省略回调函数来返回一个Query对象。请记住,如果你对任意方法传递一个回调函数,操作将被执行并在完成时调用回调函数。
方法 | 说明 |
create(objects,[callback]) | 把objects参数中指定的对象插入到MongoDB数据库。onjects参数可以是一个JavaScript对象或JavaScript对象数组 。为每个对象都创建一个针对模型的Document对象实例。回调函数接受一个错误对象作为第一个参数,而被保存的 文档作为其他对象。例如:function(err,doc1,doc2,doc3,....) |
count([query],[callback]) | 设置操作为count。当回调函数被执行时,返回的结果是匹配query的项数 |
distinct([query],[field],[callback]) | 设置操作为distinct,这将结果限制为执行回调时指定字段的不同值的数组 |
find([query],[options],[callback]) | 设置操作为find,这将返回匹配query的Document对象的数组 |
findOne([query],[options],[callback]) | 设置操作为findOne,这将返回匹配query的第一个Document对象的数组 |
findOneAndRemove([query],[options],[callback]) | 设置操作为findAndRemove,这将删除集合中第一个匹配query的文档 |
findOneAndUpdate([query],[update],[options],[callback]) | 设置操作为findOneAndUpdate,这将更新集合中第一个匹配query的文档,并且更新该文档 |
remove([query],[options],[callback]) | 设置操作为remove,这将删除集合中匹配query的所有文档 |
update([query],[update],[options],[callback]) | 设置操作为update,这将更新集合中匹配query的所有文档 |
ggregate(operators,[callback]) | 对集合应用一个或多个集合operators。回调函数接受一个错误作为第一个参数和表示聚合结果的JavaScript对象数组作为第二个参数 |
5.2,设置查询数据库操作选项
有些query对象的方法也允许你设置选项,如定义请求如何在服务器上进行吃的limit,skip和select。你可以options参数中设置这些选项,或通过调用下表的Query对象的方法来设置。
方法 | 说明 |
setOptions(options) | 设置数据库请求时,用于与MongoDB交互的选项 |
limit(number) | 设置在结果中包含的文档的最大数量 |
select(fields) | 指定应包含在结果集中每个文档中的字段:如 select({name:1,title:1,value:0}) |
sort(fields) | 以字符串形式或对象形式指定进行排序的字段,例如: sort({name:1,value:-1}) |
skip(number) | 指定要在结果集的开头跳过的文档的数量 |
read(preference) | 允许你设置读取首选项为primary,primaryPreferred,secondary,secondaryPreferred或nearest |
snapshot(Boolean) | 为true时吧查询设置为快照查询 |
safe(Boolean) | 设置为true时,数据库对更新操作使用写入关注 |
hint(hints) | 指定查找文档时要使用或排除的索引。使用1表示包含,使用-1表示排除。 |
comment(string) | 将string连同查询添加到MongoDB的日志中。这对于识别在日志文件中的查询是有用的。 |
5.3,设置查询运算符
Query对象允许你设置用于查找你想要应用数据库操作的文档的运算符和值。这些运算符把这定义为类似"字段值超过指定的数量"。所有运算符都工作于字段的路径;你可以利用where()方法或将其包括在运算符方法中来指定这个路径。如果没有指定运算符方法,则使用最后被传递到where()方法的路径。例如:
query.where('value').gt(5);
query.where('value').gt(5).lt('score',10);
下表列出了可以应用到Query对象的常用的方法:
方法 | 说明 |
where(path,[value]) | 为运算符设置当前字段路径。如果value也包括在内,那么只有其中该字段值等于value的文档才包括在内。例如:where('name','myName') |
gt([path],value) | 匹配大于在查询中指定的设定value的值。例如:gt('value',5) gt(5) |
gte([path],value) | 匹配大于或等于 |
lt([path],value) | 匹配小于 |
lte([path],value) | 匹配小于或等于 |
ne([path],value) | 匹配不等于 |
in([path],value) | 匹配存在于查询中指定的一个设定array中的值。例如:in('name',['item1','item2']) |
nin([path],value) | 匹配不存在 |
or(conditions) | 用逻辑OR连接查询子句, or{{size:{$lt:5}},{size:{$gt:10}}} |
and(conditions) | 用逻辑and连接查询子句 |
nor(conditions) | 用逻辑NOR连接查询子句 |
exists([path],Boolean) | 匹配具有指定字段的文档。例如,查找有name字段的文档: exists('name',true) |
mod([path],value,remainder) | 对某个字段的值执行除数为value的取模运算,并选择余数与remainder匹配的文档。例如:mod('size',2.0) |
regex([path],expression) | 选择其值匹配指定正则表达式的文档。例如:regex('myField','some.*exp') |
all([path],array) | 匹配包含在array参数中指定的所有元素的数组字段。例如: all('myArr',['one','two','three']) |
elemMatch([path],criteria) | 如果子文档的数组中的元素具有匹配所有指定的$elemMatch标准的字段,选择此文档。criteria可以是对象或函数。 |
size([path],value) | 选择数组字段为指定大小的文档。例如size('myArr',5) |
6,了解Document对象
当使用Model对象来检索数据库中的文档时,这些文档都在回调函数以Mongoose Document对象来表示。Document对象继承自Model类,代表一个集合中的文档。Document对象允许你通过提供一些支持验证,修改等的方法和额外属性,与从你的模式模型的角度看到的一个文档交互。
下表列出了Document对象上常用的方法和属性。
方法/属性 | 说明 |
equals(doc) | 如果这个Document对象与doc参数指定的文档相匹配,则返回true |
id | 包含文档的_id值 |
get(path,[type]) | 返回指定的路径的值,type参数可以让你强制转换返回值的类型 |
set(path,value,[type]) | 设置指定路径的字段值。type参数可以让你强制转换设置的值的类型 |
update(update,[options],[callback]) | 更新MongoDB数据库中的文档。update参数指定应用到该文档的uodate运算符。 |
save([callback]) | 把已经对Document对象做出的更改保存到MongoDB的数据库。回调函数接受一个错误对象作为唯一的参数 |
remove([callback]) | 伤处MongoDB数据库中的Document对象。回调函数接受一个错误对象作为唯一的参数 |
isNew | 一个布尔值,如果为true,则表示一个孩没有被存储在MongoDB中的模型的新对象 |
isInit(path) | 如果在这个路径的字段已经被初始化,则返回true |
isSelected(path) | 如果在这个路径的字段是从MongoDB返回的结果集中选择的,则返回true |
isModified(path) | 如果这个路径的字段已被修改,但尚未被保存到MongoDB,则返回true |
markModified(path) | 把路径标记为正在被修改,使得它会被保存/更新到MongoDB |
modifiedPaths() | 返回已被修改的对象中的路径的数组 |
toJSON() | 返回Document对象的字符串表示形式 |
toObject() | 返回一个普通的JavaScript对象,它无Document对象额外的属性和方法 |
toString() | 返回Document对象的字符串表示形式 |
validate(callaback) | 在文档上执行验证。回调函数只接受一个err参数 |
invalidate(path,msg,value) | 把路径标志为无效,从而导致验证失败。msg和value参数指定错误信息和value |
errors | 包含在文档中的错误的列表 |
schema | 链接到定义了Document对象的模型的Schema对象 |
7 利用Mongoose添加文档
你可以利用Model对象的create()方法或新创建的Document对象的save()方法吧文档添加到MongoDB库。具体例子如下:
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/words');
var wordSchema = require('./word_schema.js').wordSchema;
var Words = mongoose.model('words',wordSchema);
mongoose.connection.once('open',function(){
var newWord1 = new Words({
word:'gratifaction',
first:'g',last:'n',size:12,
letters:['g','r','a','t','i','f','c','o','n'],
stats:{vowels:5,consonants:7}
});
console.log("Is Document New? " + newWord1.isNew);
newWord1.save(function(err,doc){
console.log("\nSaved documtn: " + doc);
});
var newWord2 = {
word:'googled',
first:'g',last:'d',size:7,
letters:['g','o','l','e','d'],
stats:{vowels:3,consonants:4}
};
var newWord3 = {
word:'selfie',
first:'s',last:'e',size:6,
letters:['s','e','l','f','i'],
stats:{vowels:3,consonants:3}
};
Words.create([newWord2,newWord3],function(err){
for(var i =1;i<arguments.length;i++){
console.log("\nCreated documtn: " + arguments[i]);
}
mongoose.disconnect();
});
});
结果:
required Paths:
[ 'word' ]
Indexes:
[ [ { word: 1 }, { background: true } ],
[ { first: 1 }, { background: true } ] ]
Is Document New? true
Saved documtn: { stats: { vowels: 5, consonants: 7 },
letters: [ 'g', 'r', 'a', 't', 'i', 'f', 'c', 'o', 'n' ],
charsets: [],
_id: 5b095e2600d58120111ff24d,
word: 'gratifaction',
first: 'g',
last: 'n',
size: 12,
__v: 0 }
(node:73744) [DEP0079] DeprecationWarning: Custom inspection function on Objects via .inspect() is deprecated
Created documtn: { stats: { vowels: 3, consonants: 4 },
letters: [ 'g', 'o', 'l', 'e', 'd' ],
charsets: [],
_id: 5b095e2600d58120111ff24e,
word: 'googled',
first: 'g',
last: 'd',
size: 7,
__v: 0 },{ stats: { vowels: 3, consonants: 3 },
letters: [ 's', 'e', 'l', 'f', 'i' ],
charsets: [],
_id: 5b095e2600d58120111ff24f,
word: 'selfie',
first: 's',
last: 'e',
size: 6,
__v: 0 }
8,利用Mongoose查找文档
程序代码
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/words');
var wordSchema = require('./word_schema.js').wordSchema;
var Words = mongoose.model('Words',wordSchema);
setTimeout(function(){
mongoose.disconnect();
},3000);
mongoose.connection.once('open',function(){
var query = Words.count().where('first').in(['a','e','i','o','s']);
query.where('last').in(['a','e','i','o','u']);
query.exec(function(err,count){
if(err)throw err;
console.log("Count: " + count);
});
});
运行结果:
9,利用Mongoose更新文档
9.1 保存文档更改
代码:
var mongoose = require("mongoose");
var db = mongoose.connect('mongodb://localhost/words');
var wordSchema = require('./word_schema.js').wordSchema;
var Words = mongoose.model('Words',wordSchema);
mongoose.connection.once('open',function(){
var query = Words.findOne().where('word','googled');
query.exec(function(err,doc){
console.log("Is Document New? " + doc.isNew);
console.log("\nBefore Save:");
console.log(doc.toJSON());
doc.set('word','Book');
doc.set('first','B');
console.log("\nModified Fields: ");
console.log(doc.modifiedPaths());
doc.save(function(err){
Words.findOne({word:'Book'},function(err,doc){
console.log("\nAfter Save: ");
console.log(doc.toJSON());
mongoose.disconnect();
});
});
});
});
结果:
Is Document New? false
Before Save:
{ stats: { vowels: 3, consonants: 4 },
letters: [ 'g', 'o', 'l', 'e', 'd' ],
charsets: [],
_id: 5b095e2600d58120111ff24e,
word: 'googled',
first: 'g',
last: 'd',
size: 7,
__v: 0 }
Modified Fields:
[ 'word', 'first' ]
(node:62120) [DEP0079] DeprecationWarning: Custom inspection function on Objects via .inspect() is deprecated
After Save:
{ stats: { vowels: 3, consonants: 4 },
letters: [ 'g', 'o', 'l', 'e', 'd' ],
charsets: [],
_id: 5b095e2600d58120111ff24e,
word: 'Book',
first: 'B',
last: 'd',
size: 7,
__v: 0 }
9.2,更新单个文档
Document对象提供了update()方法,它允许你利用update运算符来更新一个文档,语法如下:
update(update,[options],[callback])
update参数定义对文档执行的更新操作。options参数指定写入的首选项,而callback参数接受err和doc.
具体代码如下:
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/words');
var wordSchema = require('./word_schema.js').wordSchema;
var Words = mongoose.model('Words',wordSchema);
mongoose.connection.once('open',function(){
var query = Words.findOne().where({'word':'Book'});
query.exec(function(err,doc){
console.log("update Before:");
console.log(doc.toString());
var query = doc.update({$set:{word:'new book'}});
query.exec(function(err,results){
console.log("\n%d Documen updated",results);
Words.findOne({word:'new book'},function(err,doc){
console.log("\nAfter Update: ");
console.log(doc.toString());
mongoose.disconnect();
});
});
});
});
更新后的结果如下:
update Before:
{ stats: { vowels: 3, consonants: 4 },
letters: [ 'g', 'o', 'l', 'e', 'd' ],
charsets: [],
_id: 5b095e2600d58120111ff24e,
word: 'Book',
first: 'B',
last: 'd',
size: 7,
__v: 0 }
(node:62164) [DEP0079] DeprecationWarning: Custom inspection function on Objects via .inspect() is deprecated
NaN Documen updated
After Update:
{ stats: { vowels: 3, consonants: 4 },
letters: [ 'g', 'o', 'l', 'e', 'd' ],
charsets: [],
_id: 5b095e2600d58120111ff24e,
word: 'new book',
first: 'B',
last: 'd',
size: 7,
__v: 0 }
9.3更新多个文档
使用Model对象提供的update()方法。
代码:
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/words');
var wordSchema = require('./word_schema.js').wordSchema;
var Words = mongoose.model('Words',wordSchema);
mongoose.connection.once('open',function(){
Words.find({word:/sel.*/},function(err,docs){
console.log("Before update: ");
for(var i in docs){
console.log(docs[i].word + " : " +docs[i].size);
}
var query = Words.update({},{$set:{size:0}});
query.setOptions({multi:true});
query.where('word').regex(/sel.*/);
query.exec(function(err,res){
Words.find({word:/sel.*/},function(err,docs){
console.log("\nAfter update:");
for(var i in docs){
console.log(docs[i].word + " : " + docs[i].size);
}
mongoose.disconnect();
});
});
});
});
结果
Before update:
selfie : 6
After update:
selfie : 0
10,利用Mongoose删除文档
利用Mongoose在集合中删除文档,可以使用remove()方法。
10.1,删除单个文档
Document对象提供remove()方法,它允许你删除模型中的文档
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/words');
var wordSchema = require('./word_schema.js').wordSchema;
var Words = mongoose.model('Words',wordSchema);
mongoose.connection.once('open',function(){
var query = Words.findOne().where('word','测试');
query.exec(function(err,doc){
if(err)throw err;
console.log("Before Delete: ");
console.log(doc);
doc.remove(function(err,deletedDoc){
console.log("here");
console.log(deletedDoc);
mongoose.disconnect();
});
});
});
结果:
Before Delete:
{ letters: [],
charsets: [],
_id: 5b0a666ff7f25ab151910d4c,
word: '测试' }
here
{ letters: [],
charsets: [],
_id: 5b0a666ff7f25ab151910d4c,
word: '测试' }
11,利用Mongoose聚合文档
Model对象提供了aggregate()方法,可以让你实现MongoDB中的聚合管道。它在这里的工作方式类似于它在MongoDB Node.js原生驱动程序中的工作方式。事实上,如果你愿意,就可以使用完全相同的语法。你也可以选择使用Mongoose Aggregate对象生成并执行聚合管道。
在如下方面,Aggregate对象的工作原理非常类似于Query对象,即如果你传入一个回调函数,则aggregate()被立即执行;如果没有传入一个回调函数,则返回一个Aggregate对象,并且你可以应用管道的方法。
12,使用验证框架
mongoose模块的最重要的一个方面是对已定义的模型执行验证。Mongoose提供了一个内置的验证框架,你只需要定义在需要验证的特定字段执行的验证函数。当你尝试创建一个文档的新实例,从数据库中读取文件或保存文件的时候,验证框架就会调用自定义的验证方法,如果验证失败则返回错误。
验证框架其实很容易实现。你在想要应用验证的Model对象的具体路径上调用validate()方法,并传入验证函数即可。验证函数接受该字段的值,然后使用该值返回true或false,这取决于该值是否有效。validate()方法的第二个参数是,如果验证失败,就被应用到错误对象的error字符串。例如:
Words.schema.path('word').validate(function(value){
return value.length <20;
},"Word is Too Big");
验证抛出的错误对象具有以下字段。
- error.errors.<field>.message:在加入验证函数时定义的字符串。
- error.errors.<field>.type:验证错误类型。
- error.errors.<field>.path:验证失败的对象路径。
- error.errors.<field>.value:验证失败的值。
- error.name:错误类型名称
- err.message:错误消息
下面的清单显示了一个简单的例子,它在word模型中添加单词长度为0或大于20则无效的验证。
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/words');
var wordSchema = require('./word_schema.js').wordSchema;
var Words = mongoose.model('Words',wordSchema);
Words.schema.path('word').validate(function(value){
return value.length > 0 ;
},"Word is too small");
Words.schema.path('word').validate(function(value){
return value.length < 20;
},"Word is too big");
mongoose.connection.once('open',function(){
var newWord = new Words({
word:'sablfjhuegblnakufguasfbkuahifhusjiah',
first:'s',
});
newWord.save(function(err){
console.log(err.errors.word.message);
console.log(String(err.errors.word));
console.log(err.errors.word.type);
console.log(err.name);
console.log(err.errors.word.value);
console.log(err.message);
mongoose.disconnect();
});
});
输出结果:
Word is too big
Word is too big
undefined
ValidationError
sablfjhuegblnakufguasfbkuahifhusjiah
Words validation failed: word: Word is too big
13,实现中间件函数
Mongoose提供一个中间件框架,它在Document对象的init(),validate(),save()和remove()方法前后调用pre和post函数。中间件框架可以让你实现应该在某个过程的特定步骤之前或之后应用的函数。例如,在使用本章前面定义的模型创建word文档时,你可能需要自动把size设置成word字段的长度,如下pre() save()中间件函数所示:
Words.schema.pre('save',function(next){
console.log('%s is about to be saved',this.word);
console.log('Setting size to %d',this.word.length);
this.size = this.word.length;
next();
});
有两种类型的中间件函数——pre和post函数,并且它们的处理方式有点不同。pre函数接收一个next参数,这是要执行的下一个中间件函数。pre函数可以被异步或同步的调用。在异步方法的情况下,一个额外的done参数被传递到pre函数,以便可以通知异步框架你已完成操作。如果你应用的操作应按其在中间件的顺序来完成,则应该使用同步的方法。
要同步应用中间件,只需要在中间件函数中调用next()。例如:
Words.schema.pre('save',function(next){
next();
});
要异步地应用中间件,则需要把一个true参数添加大pre()方法来表示异步行为,然后在中间间函数内部调用doAsyn(done)。例如:
Words.schema.pre('save',true,function(next){
next();
doAsync(done);
});
你在init,validate,save或remove操作已处理后调用post中间件函数。这允许你在应用操作的时候做任何必要的清理工作。例如,下面的代码实现了一个简单的post save方法来记录已经保存的对象:
schema.post('save',function(doc){
console.log("Document Saved: " + doc.toString());
});
下面的代码清单是一个使用pre()和post()的例子:
var wordSchema = require('./word_schema.js').wordSchema;
var Words = mongoose.model('Words',wordSchema);
Words.schema.pre('init',function(next){
console.log('a new word is about to be initialized from the db');
next();
});
Words.schema.pre('validate',function(next){
console.log('%s is about to be validated',this.word);
next();
});
Words.schema.pre('save',function(next){
console.log('%s is about to be saved',this.word);
console.log('Setting size to %d',this.word.length);
this.size = whis.word.length;
next();
});
Words.schema.pre('remove',function(next){
console.log('%s is about to be removed',this.word);
next();
});
Words.schema.post('init',function(doc){
console.log('%s is initialized from the db',doc.word);
});
Words.schema.post('validate',function(doc){
console.log('%s is validated ',doc.word);
});
Words.schema.post('save',function(doc){
console.log('%s is saved to db ',doc.word);
});
Words.schema.post('remove',function(doc){
console.log('%s is removed ',doc.word);
});
mongoose.connection.once('open',function(){
var newWord = new Words({
word:'newWord',
first:'t',
last:'d',
size:'newWord'.length,
});
console.log("\nSaving: ");
newWord.save(function(err){
console.log("\nFinding: ");
Words.findOne({word:'newWord'},function(err,doc){
console.log("\nRemoving: ");
newWord.remove(function(err){
mongoose.disconnect();
});
});
});
});
运行结果
Saving:
newWord is about to be saved
Setting size to 7
Finding:
Removing:
newWord is about to be removed
newWord is removed