转自:http://www.w3c.com.cn/mongoose-guide

Queries

文件可以通过一些静态辅助模型的方法检索。

任何涉及 指定 查询 条件的模型方法,有两种执行的方式:

当一个回调函数:

  • 被传递,将立即执行的操作结果传递给回调。

  • 未被传递,返回一个查询的实例,它为您提供了一个特殊的QueryBuilder接口。

    让我们来看看在传递一个回调时会发生什么:

    var Person = mongoose.model('Person', yourSchema);
    // find each person with a last name matching 'Ghost', selecting the `name` and `occupation` fields
    Person.findOne({ 'name.last': 'Ghost' }, 'name occupation', function (err, person) {
    if (err) return handleError(err);
    console.log('%s %s is a %s.', person.name.first, person.name.last, person.occupation) // Space Ghost is a talk show host.
    })

在这里,我们可以看到,查询立即执行,并将结果传递给我们的回调。mongoose的所有回调使用模式:callback(error, result) 。如果执行查询时发生错误,错误参数将包含一个错误文件,result将是null。如果查询成功,error参数将是空的,result就会包含查询的结果。

  1. 任何地方被传递给一个函数,回调如下格式 callback(error, results)。

现在,让我们看看没有传递回调时,会发生什么:

// find each person with a last name matching 'Ghost'
var query = Person.findOne({ 'name.last': 'Ghost' });
// selecting the `name` and `occupation` fields
query.select('name occupation');
// execute the query at a later time
query.exec(function (err, person) {
if (err) return handleError(err);
console.log('%s %s is a %s.', person.name.first, person.name.last, person.occupation) // Space Ghost is a talk show host.
})

这使我们能够通过返回的query实例建立查询。进一步考虑这个例子:

Person
.find({ occupation: /host/ })
.where('name.last').equals('Ghost')
.where('age').gt(17).lt(66)
.where('likes').in(['vaporizing', 'talking'])
.limit(10)
.sort('-occupation')
.select('name occupation')
.exec(callback);

References to other documents

MongoDB是非关联型的,但有时我们还是想引用其他集合中的文档。这是population的用武之地。

Streaming

查询可以从MongoDB的传输到你的应用程序。只需调用查询的数据流的方法,而不是EXEC返回一个实例QueryStream。注:QueryStreams 是 Node.js的0.8风格,不是Node.js的0.10风格。

Next Up

现在,我们已经覆盖查询,让我们来看看验证。

Validation

在我们进入具体的Validation语法,请记住以下规则:

  • Validation是SchemaType中定义的

  • Validation是middleware的一部分

  • Validation发生在document试图在默认值应用之后保存的时候

  • Validation是异步递归的:当你调用 Model#save时,子文档执行Validation。如果发生了错 ​​误,你的 Model#save回调接收错误

  • Mongoose并不关心复杂的错误消息建设。错误有类型标识符。例如,“min”是数未达到minimum value时触发错误的标识符。引发错误的路径和值的ValidationError对象可以访问

Built in validators

Mongoose有几个内置的验证器。

Custom validators

通过传递一个验证function和一个error tyoe给你的SchemaTypes 验证方法.更多自定义验证器和异步验证器的细节请查阅API

Validation错误

Validation失败后返回的Errors包含一个持有实际ValidatorErrors的errors对象。每个ValidatorError有type, path, 和 value属性,为我们提供了多一点的错误处理的灵活性。

var toySchema = new Schema({
color: String,
name: String
});
var Toy = mongoose.model('Toy', toySchema);
Toy.schema.path('color').validate(function (value) {
return /blue|green|white|red|orange|periwinkel/i.test(value);
}, 'Invalid color');
var toy = new Toy({ color: 'grease'});
toy.save(function (err) {
// err.errors.color is a ValidatorError object
console.log(err.errors.color.message) // prints 'Validator "Invalid color" failed for path color with value `grease`'
console.log(String(err.errors.color)) // prints 'Validator "Invalid color" failed for path color with value `grease`'
console.log(err.errors.color.type) // prints "Invalid color"
console.log(err.errors.color.path) // prints "color"
console.log(err.errors.color.value) // prints "grease"
console.log(err.name) // prints "ValidationError"
console.log(err.message) // prints "Validation failed"
});;

Validation错误后,该文件也将有相同的errprs属性:

toy.errors.color.message === err.errors.color.message

Next up

现在,我们已经覆盖了Validation,让我们一起来看看,你可能会如何处理先进的Validation与Mongoose中间件。

Middleware

Middleware是一些函数,在执行init,validate,save和remove的时候传递控制权。有两种类型的中间件,pre和post。让我们开始pre。

pre

有两种类型的预中间件,串行和并行。

串行

串口中间件执行此起彼伏,每个中间件调用next

var schema = new Schema(..);
schema.pre('save', function (next) {
// do stuff
next();
});

并行

并行中间件提供更细粒度的流量控制。

var schema = new Schema(..);
schema.pre('save', true, function (next, done) {
// calling next kicks off the next middleware in parallel
next();
doAsync(done);
});

像例子一样,方法将不被执行,直到操作完每个中间件。

使用案例

中间件对于细化模型的逻辑,避免嵌套太多的异步代码。这里有一些其他的用途:

  • 复杂的验证
  • 消除依赖文档
    • (删除用户删除他的所有部落格文章)
  • 异步默认
  • 异步任务触发某个动作
    • 触发自定义事件
    • 通知

错误处理

如果任何中间件调用next,处理错误实例,流程将被打断,错误会传递给回调。

schema.pre('save', function (next) {
var err = new Error('something went wrong');
next(err);
});
// later...
myModel.save(function (err) {
console.log(err.message) // something went wrong
});

post

post在勾连方法和它们所有的pre中间件完成之后。pre中间件不直接接收流量控制,例如,没有next或callback被传递给它。post钩为这些方法注册传统的事件监听器。

schema.post('init', function (doc) {
console.log('%s has been initialized from the db', doc._id);
})
schema.post('validate', function (doc) {
console.log('%s has been validated (but not saved yet)', doc._id);
})
schema.post('save', function (doc) {
console.log('%s has been saved', doc._id);
})
schema.post('remove', function (doc) {
console.log('%s has been removed', doc._id);
})

Next up

现在,我们已经覆盖了中间件,让我们来看看mongoose方法伪造关联查询的population助手。

population

MongoDB是非关联数据库。但是有时候我们还是想引用其它的文档。这就是population的用武之地。

Population是从其它文档替换文档中的特定路径。我们可以迁移一个单一的文件,多个文件,普通对象,多个普通的对象,或从查询中返回的所有对象。让我们来看看一些例子。

var mongoose = require('mongoose')
, Schema = mongoose.Schema
var personSchema = Schema({
_id : Number,
name : String,
age : Number,
stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
var storySchema = Schema({
_creator : { type: Number, ref: 'Person' },
title : String,
fans : [{ type: Number, ref: 'Person' }]
});
var Story = mongoose.model('Story', storySchema);
var Person = mongoose.model('Person', personSchema);

到目前为止,我们已经创造了两个Models。我们的person model有它的stories字段设置到一个ObjectIds数组。ref选项是告诉Mongoose在Story Model的例子中应该在population的时候使用哪个Model。所有_id我们这里存储必须是来自Story Model的document _id s。我们同时还声明了 Story _creator 属性,和在personSchema里的_id一样的类型。匹配这两者非常重要要。

注意: ObjectId, Number, String, 和 Buffer也可以用来作为 refs.

Saving refs

保存对于其他文件的ref的工作方式和通常保存属性的相同,只是分配_id值:

var aaron = new Person({ _id: 0, name: 'Aaron', age: 100 });
aaron.save(function (err) {
if (err) return handleError(err);
var story1 = new Story({
title: "Once upon a timex.",
_creator: aaron._id // assign the _id from the person
});
story1.save(function (err) {
if (err) return handleError(err);
// thats it!
});
})

Population

到目前为止,我们还没有做什么太大的不同。我们只是创造了一个Person,一个Story。现在,让我们来看看使用查询生成器扩展我们的story's_creator:

Story
.findOne({ title: 'Once upon a timex.' })
.populate('_creator')
.exec(function (err, story) {
if (err) return handleError(err);
console.log('The creator is %s', story._creator.name);
// prints "The creator is Aaron"
})

扩展的路径不再设置到原来的_id,在返回结果之前通过分离查询它们的值将会被从数据库返回的mongoose document取代。

refs数组以相同的方式工作。在query上调用populate方法就会返回一个document数组取代原来的_ids。

注意: mongoose >= 3.6 exposes the original _ids used during population through the document#populated() method.

Field Selection

如果我们只想要一些扩展文档的特定的字段?这可以通过传递populate方法的第二个参数一般的字段名语法来完成:

Story
.findOne({ title: /timex/i })
.populate('_creator', 'name') // only return the Persons name
.exec(function (err, story) {
if (err) return handleError(err);
console.log('The creator is %s', story._creator.name);
// prints "The creator is Aaron"
console.log('The creators age is %s', story._creator.age);
// prints "The creators age is null'
})

Populating multiple paths

如果我们想同时扩展多个路径怎么办?

Story
.find(...)
.populate('fans author') // space delimited path names
.exec()

In mongoose >= 3.6, 我们能用空格分界的字符串异性搞定,但是之前你要调用很多次

Story
.find(...)
.populate('fans')
.populate('author')
.exec()

Query conditions and other options

如果我们想要扩展基于年龄的粉丝数组,选择他们的名字,返回最多我个人呢?

Story
.find(...)
.populate({
path: 'fans',
match: { age: { $gte: 21 }},
select: 'name -_id',
options: { limit: 5 }
})
.exec()

Refs to children

然而,我们使用 aaron 对象,但我们不能够获得stories的清单。这是因为没有story对象‘压栈’到 aaron.stories。

这里有两种观点。首先,aaron知道哪个故事是他的就好了。

aaron.stories.push(story1);
aaron.save(callback);

这使我们能够执行查找并扩展组合:

Person
.findOne({ name: 'Aaron' })
.populate('stories') // only works if we pushed refs to children
.exec(function (err, person) {
if (err) return handleError(err);
console.log(person);
})

我们是否真的要两套指针,因为它们可能走出同步。相反,我们可以跳过扩展,直接find()我们感兴趣的story

Story
.find({ _creator: aaron._id })
.exec(function (err, stories) {
if (err) return handleError(err);
console.log('The stories are an array: ', stories);
})

Updating refs

现在,我们有一个story的_creator不正确。我们可以更新refs就像其它mongoose属性设置一样:

var guille = new Person({ name: 'Guillermo' });
guille.save(function (err) {
if (err) return handleError(err);
story._creator = guille;
console.log(story._creator.name);
// prints "Guillermo" in mongoose >= 3.6
// see https://github.com/LearnBoost/mongoose/wiki/3.6-release-notes
story.save(function (err) {
if (err) return handleError(err);
Story
.findOne({ title: /timex/i })
.populate({ path: '_creator', select: 'name' })
.exec(function (err, story) {
if (err) return handleError(err);
console.log('The creator is %s', story._creator.name)
// prints "The creator is Guillermo"
})
})
})

query population返回的documents 是完全功能化的,可删除的,可保存的文档,除非 lean option 被指定了。不要将它们和子文档搞混了。当你要调用remove方法的时候要注意,因为你将会把它从数据库里删除,不仅仅是数组。

Populating an existing document

如果我们有一个已有的document,想要扩展路径, mongoose >= 3.6 支持document#populate() 方法。

Populating multiple existing documents

如果我们想扩展多个document(like mapReduce output),我们需要通过 Model.populate() 方法,mongoose >= 3.6有效.document#populate() 和 query#populate() 就是用这个方法更新documents.

Field selection difference from v2 to v3

字段选择在v3 v2的比略有不同。数组的领域不再被接受。请参阅迁移指南和下面的例子中更多的细节。

// this works in v3
Story.findOne(..).populate('_creator', 'name age').exec(..);
// this doesn't
Story.findOne(..).populate('_creator', ['name', 'age']).exec(..);

Next Up

现在,我们已经覆盖扩展查询,让我们来看看连接。

Connections

我们可以连接到MongoDB的通过mongoose.connect()方法。

mongoose.connect('mongodb://localhost/myapp');

只是最起码的链接本地运行在默认端口(27017)的myapp数据库。我们也可以指定一些参数在uri中。这取决于你的环境:

mongoose.connect('mongodb://username:password@host:port/database?options...');

更多详细信息,请参阅MongoDB的mongodb connection string说明。

Options

connect方法也接受传递给底层驱动的options对象。所有选项在这里包括的优先于传递到connection 字符串的选项。

mongoose.connect(uri, options);

下列选项键可供选择:

  • db – passed to the connection db instance
    server – passed to the connection server instance(s)
    replset – passed to the connection ReplSet instance
    user – username for authentication (if not specified in uri)
    pass – password for authentication (if not specified in uri)
    auth – options for authentication
    mongos – Boolean – if true, enables High Availability support for mongos

例如:

var options = {
db: { native_parser: true },
server: { poolSize: 5 },
replset: { rs_name: 'myReplicaSetName' },
user: 'myUserName',
pass: 'myPassword'
}
mongoose.connect(uri, options);

注: 该服务器选项auto_reconnect的是默认为true时,它可以被覆盖。forceServerObjectId db选项设置为false,不能被覆盖。

有关可用选项的更多信息,请参阅驱动的有关信息。

有关的keepalive的注意事项

对于长时间运行的applictions的,它往往是谨慎启用KEEPALIVE。没有它,一段时间后,你可能会开始看没有理由的“connection closed”的错误。如果是这样的话,看这个后,你可能会决定启用KEEPALIVE:

options.server.socketOptions = options.replset.socketOptions = { keepAlive: 1 };
mongoose.connect(uri, options);

ReplicaSet Connections

使用同样的方法,而不是通过一个单一的URI连接到一个副本集,但我们通过逗号分隔URIs。

`mongoose.connect('mongodb://username:password@host:port/database,mongodb://username:password@host:port,mongodb://username:password@host:port?options...' [, options]);`

注:数据库,只需要在一个指定的的uri。

Multi-mongos support

在多个Mongos的实例也支持高可用性。通过一个连接字符串Mongos的实例,并设置为true Mongos的选项:

mongoose.connect('mongodb://mongosA:27501,mongosB:27501', { mongos: true }, cb);

Multiple connections

到目前为止,我们已经看到了如何连接到MongoDB。有时我们可能需要mongo打开多个连接,每个有不同的读/写设置,或者只是为了不同的数据库,例如。在这些情况下我们可以利用mongoose.createConnection()接受已经讨论过的所有参数,并返回一个新的连接。

`var conn = mongoose.createConnection('uri,uri,uri...', options);`

然后,可以使用此连接对象,用于创建和检索,范围只在这一特定的连接的模型。

Connection pools

每个连接,无论是mongoose.connect还是mongoose.createConnection都会用一个内置可配置链接池备份,默认大小为5。调节池的大小,使用的连接选项:

// single server
var uri = 'mongodb://localhost/test';
mongoose.createConnection(uri, { server: { poolSize: 4 }});
// for a replica set
mongoose.createConnection(uri, { replset: { poolSize: 4 }});
// passing the option in the URI works with single or replica sets
var uri = 'mongodb://localhost/test?poolSize=4';
mongoose.createConnection(uri);

Next Up

现在,我们已经覆盖了连接,让我们来看看我们如何能够打破我们的功能件为可重用和共享插件。

Plugins

Schemas是可插入的,即,它们可以应用预先包装的能力,从而延长其功能。这是一个非常强大的功能。

假设我们有几个collection在我们的资料库中,要添加的最后一次修改给每一个的功能。用插件,这很容易。只需创建一个插件,并把它应用到每个Schema:

// lastMod.js
module.exports = exports = function lastModifiedPlugin (schema, options) {
schema.add({ lastMod: Date })
schema.pre('save', function (next) {
this.lastMod = new Date
next()
})
if (options && options.index) {
schema.path('lastMod').index(options.index)
}
}
// game-schema.js
var lastMod = require('./lastMod');
var Game = new Schema({ ... });
Game.plugin(lastMod, { index: true });
// player-schema.js
var lastMod = require('./lastMod');
var Player = new Schema({ ... });
Player.plugin(lastMod);

我们刚添加的last-modified的行为–我们的Game和Player Schemas,并补充添加声明我们的Games的对于lastMOd的index。不坏的几行代码!

社区!

您不仅可以重新使用模式功能在自己的项目,但你也获益mongoose社区。发表任何插件NPM和带有mongoose标签的,会显示在我们的搜索结果页面

Next Up

现在,我们已经覆盖插件以及如何涉足生长在他们周围的伟大的社区,让我们来看看如何可以帮助mongoose本身的持续发展做出贡献

Mongoose Guide(转)的更多相关文章

  1. node.js操作数据库之MongoDB+mongoose篇

    前言 node.js的出现,使得用前端语法(javascript)开发后台服务成为可能,越来越多的前端因此因此接触后端,甚至转向全栈发展.后端开发少不了数据库的操作.MongoDB是一个基于分布式文件 ...

  2. Mongoose Schemas定义中timestamps选项的妙用

    在Node.js中使用MongoDB少不了Mongoose. 假设有如下Mongoose Schemas的定义: var ItemSchema = new mongoose.Schema({ biz: ...

  3. NodeJS实战:Express+Mongoose+ejs

    元宵还没到,先向所有朋友拜一个晚年~~~ 文章目录: 1.组件版本号 -- --node -- --express -- --Mongoose 2.初始化项目 firstblood -- --用 ex ...

  4. Node.js使用Mongoose包操作MongoDB数据库

    1. 安装Mongoose npm install mongoose 2. 使用 2.1 创建连接 var mongoose = require('mongoose'); mongoose.conne ...

  5. Nodejs学习笔记(十四)— Mongoose介绍和入门

    目录 简介 mongoose安装 连接字符串 Schema Model 常用数据库操作 插入 更新 删除 条件查询 数量查询 根据_id查询 模糊查询 分页查询 其它操作 写在之后... 简介 Mon ...

  6. Mongoose Schemas中定义日期以及timestamps选项的妙用

    本文转自:http://www.cnblogs.com/jaxu/p/5595451.html 在Node.js中使用MongoDB少不了Mongoose.假设有如下Mongoose Schemas的 ...

  7. mongoose一看就会的基本操作

    Mongoose是在node.js异步环境下对mongodb进行便捷操作的对象模型工具 那么要使用它,首先你得装上node.js和mongodb,关于mongodb的安装和操作介绍可以参考:http: ...

  8. [转] mongoose学习笔记(超详细)

    名词解释 Schema: 一种以文件形式存储的数据库模型骨架,不具备数据库的操作能力 Model: 由Schema编译而成的假想(fancy)构造器,具有抽象属性和行为.Model的每一个实例(ins ...

  9. mongoose 入门基本操作

    简介 Mongoose是在node.js异步环境下对mongodb进行便捷操作的对象模型工具 那么要使用它,首先你得装上node.js和mongodb,关于mongodb的安装和操作介绍可以参考:ht ...

随机推荐

  1. FOV

    来源:https://blog.csdn.net/chepwavege/article/details/98876550 视场 (视图字段) 是指现场对面相机镜头的立体角.图如下图所示︰ 高频通气︰  ...

  2. @FeignClient注解详解

    Spring Cloud 是目前最火的微服务框架,Feign 作为基础组件之一,在 Spring Cloud 体系中发挥了重要的作用. 一.FeignClient注解 FeignClient注解被@T ...

  3. Oracle缓存表与Oracle缓存的区别

    一.Oracle缓存表 与 Oracle缓存 的概念 Oracle 缓存:是把Oracle近期查询的语句放置在Oracle设定的缓存当中. Oracle 缓存表:是把某个表放置在缓存当中,缓存是Ora ...

  4. 如何让程序像人一样的去批量下载歌曲?Python爬取付费歌曲

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 今天来教大家一个自动化爬虫的工具 selenium selenium Se ...

  5. ubuntu 19.10 中防火墙iptables配置

    $sudo which iptables   /usr/sbin/iptables说明有安装 如果没有安装,那么使用sudo apt-get install iptables 安装. 刚装机,是这个样 ...

  6. css做模糊处理

    -webkit-filter: blur(9px); filter: blur(9px);

  7. 极简 Node.js 入门 - 5.1 创建 HTTP 服务器

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

  8. day56 Pyhton 前端Jquery08

    前端 内容回顾: -BOM -jquery介绍 -jquery下载和引入方式 npm install jquery -jquery的选择器 -基本选择器 -通配符选择器 - id选择器 - 类选择器 ...

  9. <二分查找+双指针+前缀和>解决子数组和排序后的区间和

    <二分查找+双指针+前缀和>解决子数组和排序后的区间和 题目重现: 给你一个数组 nums ,它包含 n 个正整数.你需要计算所有非空连续子数组的和,并将它们按升序排序,得到一个新的包含 ...

  10. 怎样学习C语言(献给迷茫的C爱好者)!

    一 .怎样学习C语言 很多人对学习C语言感到无从下手,经常问我同一个问题:究竟怎样学习C语言?我是一个教师,已经开发了很多年的程序,和很多刚刚起步的人一样,学习的第一个计算机语言就是C语言. 经过这些 ...