MongoDB学习心得

  • MongoDB学习心得
    • 初识MongoDB
      • HelloWorld 快速运行MongoDB
    • 详解MongoDB的CRUD操作
      • MongoDB 的数据模型
      • 创建数据库的语法
      • 创建与删除集合
      • MongoDB 的数据类型
      • MongoDB 插入文档
      • 查询文档
        • 带条件的查询 等值查询
        • AND 语法
        • or 语法
      • 更新文档
      • 删除文档
      • MongoDB 的投影
      • Limite 限制记录Limite方法
      • 排序文档sort
      • skip 跳过文档
      • 索引
      • 聚合aggregate
    • MongoDB 集群
      • 复制操作
      • 分片操作
      • 数据库备份与恢复 mongodump mongorestore
      • 数据库监控mongostate mongotop
    • 高级部分
      • 数据库引用
      • MongoDB 索引查询
      • MongoDB 的查询分析
      • MongoDB 的原子操作
    • Mongoose 学习
      • Mongoose 链接数据库
      • Mongoose 三大基本概念
        • Schema
        • schema中的数据类型 在SchemaTypes里面定义
        • Model
        • Entity
        • Queries in mongoose
        • Validation
        • Middleware 中间件
        • population 集合引用

MongoDB学习心得

主要是写一些mongodb的学习心得

初识MongoDB

mongodb 是一个非关系型的文档数据库。数据库中有集合这一概念,集合由一组文档构成,这一个集合相当于RDBMS中的一张表。而一个集合中的文档是动态的键值对,统一集合下的文档不必具有相同的文档字段以及结构,不同文件中的相同字段数据类型可以不同。

HelloWorld 快速运行MongoDB

从基本的创建数据库开始:
use DATABASE_NAME创建新的数据库,如果数据库存在返回当前数据库

createdatabase.JPG-11.3kB

use DBNAME是用于切换数据库,或者创建数据库。而db操作是查看有哪些数据库show dbs查看数据库的列表。
但是看不见当前的Mydb数据库因为里面没有内容,需要往数据库中插入一个文件可以查询到当前的数据库。

insertafile.JPG-16.1kB

本来正常的数据写入是db.COLLECTION_NAME.insert().如果COLLECTION_NAME不存在则会自动创建。
然后发现新建的数据库存在了。
删除数据库db.dropDatabase():

dropdatabase.JPG-13.9kB

创建集合使用db.createCollection(name,option)实现

MongoDB学习心得_第1张图片

其中option有多个选项:
capped:表示是否设定集合上限大小,如果为true那么option中的size有效了;autoIndexID 表示是否自动创建名称为_id索引字段。而size表示的是集合的上限大小;max表示的是集合上限文件数量

createcollections2.JPG-16.8kB

{
    capped:表示是否限制集合的上限大小。取值true or false
    autoIndexID:true | false表示是否自动创建文档的_id字段,_id其实是主键
    size:集合大小上限,当capped为true时有效单位是字节。
    max:集合文件数量上限。为数字
}

集合删除使用db.COLLECTION_NAME.drop()查看集合使用show collections
往集合中插入数据,也就是文档。MongoDB的基本操作(CRUD)其实都是比较固定的操作db.collection_name.dosomething(options)
如一个插入文档的操作是dp.COLLECTION_NAME.insert(document)
这个document是一个BSON数据。写入的是BSON格式的数据,它是JSON数据的扩展。
参考一个实例,往mc的collection中插入一个文档的操作

insertthefile.JPG-18.9kB

然后使用查询,查询可以分结构化查询与非结构化查询,普通查询是
db.COLLECTION_NAME.find() 查询这个集合中的所有的cocument。最后都需要添加一个pretty()让显示内容更加美观。
或者使用结构化查询显示这张表的全部信息
db.COLLECTION_NAME.find().pretty()
代码如下

MongoDB学习心得_第2张图片

查询也可以带一些参数,这一部分可以对比DBMS型数据库中的查询语句,有明显的不同

MongoDB学习心得_第3张图片

查询中find参数给定的是一个BSON数据,关键是取值部分key:value。value部分可以是简单的BSON数据类型中的一种,也可是更加复杂的类型。小于,小于等于,大于,大于等于,不等,都还是写成BSON格式,如{$lt:value} {$lte:value} {$gt:value} {$gte:value} {$ne:value} 这种格式写在find({key:Value})里面
实验例子使用AND查询
db.COLLECTION_NAME.find({key1:value1,key2:value2})AND查询就是一次性写两个及以上的键值对完成
find({key1:value1,key2:value2,key3:value3...}) 这种类型的查询语句。比如要查询数值在100到200的一个field。可以这样写
db.COLLECTION_NAME.find({field:{\$gte:100},field:{\$lte:200}})这种语法
如在上面例子查询title为MongoDB以及by是alex的文件语句是
db.mc.find({'title':'MongoDB','by':'alex'}).pretty()
使用or的查询使用$or的语法

MongoDB学习心得_第4张图片

这种查询与AND不一样,查询内容放在value这个列表里面,假设要查询一个field中大于100 或者小于50的值用or查询是
db.COLLECTION_NAME.find({\$or:[{field:{\$gt:100}},{field:{\$lt:50}}]})

文档的更新使用save或者update方法,两者的区别在于update是在现有的文档上面进行跟新,而save是用新的文档替换覆盖掉现有的文档。
使用update更新文档
db.COLLECTION_NAME.update(changeitem,newitem)

比如更改数据库中的title使用save 方法,使用新的id以及新的数据替换掉原有的文件数据。但是id一定要是集合中存在的文件
id保持一致,然后写入新的内容

MongoDB学习心得_第5张图片

删除集合中的文件
db.COLLECTION_NAME.remove()
如果为空,那么就是删除集合中的内容
如果是接某一个属性,那么删除这个文件,比如说写入title:alex那么含有这个条目的文件会被删除。

详解MongoDB的CRUD操作

MongoDB是一种NoSQL的数据库,是面向对象的数据库。与一般的RDBMS不同,MongoDB没有表与表之间的关系的概念,MongoDB 的操作语法非常适用于JavaScript的语法糖。BSON的格式可以看做是JSON的扩展,因此JavaScript环境非常适合作为运行使用MongoDB的理想的宿主环境。

MongoDB 的数据模型

相比于RDBMS中的数据,MongoDB的数据模型。比如说在RDBMS中要建立多对多的关系。比如老师,课程这一个一对多的关系。采用RDBMS数据库会设计两张表,一张老师信息表,一张课程表。但是在MongoDB中只需要一个集合然后集合中文档采用如下数据模型实现。

{
    '_id':ObjectId(),
    'name':'alex',
    'course':[{
        'name':'english',
        'describ':'blabla'
    },
    {
        'name':'math',
        'describ':'blabla'
    } 
    ]
}

可以将一对多的关系采用一个数据结构的文档表示,非常简单。集合是没有固定的模式的。

创建数据库的语法

use DB_NAME
这个语法有两个作用:如果数据库本身不存在那么使用这个指令会创建一个数据库,如果数据库存在那么使用这个指令会切换到选择的数据库
当然要查询当前数据库的名字,使用db指令,如果要看当前系统中所有的数据库那么使用show dbs查看,但是在创建数据库后,如果并未往数据库中添加数据,使用show dbs是查询不到数据库的,必须往数据库中写入至少一个文件才行。所以需要创建集合(collections),插入数据。一般情况下创建了collection就可以使用show dbs查询到数据库了。

创建与删除集合

创建集合使用指令db.createCollection(name,option)操作,其中name是创建的集合的名称,而option是创建集合时的重要的选项{capped,autoIndexID,size,max}。option可选的属性,主要用于指定创建集合的封顶大小,是否自动生成id,文档数量上限等信息。当然如果不创建集合的情况下使用插入数据,那么系统会自动创建集合。
这个option的选项有更加深入的影响:
删除集合的指令是db.COLLECTION_NAME.drop()实现。删除数据库为db.dropDatabase()

MongoDB 的数据类型

MongoDB学习心得_第6张图片

分析MongoDB的数据类型,可以看到与JavaScript的数据类型有联系也有较大的区别。MongoDB的文档存储格式类型为BSON(Binary JSON)。是对json的扩展(一般json只是支持6中数据类型null,number,string,boolean,array,object),加入了更多的在数据库中大量使用的数据类型。MongoDB支持的整数有32位以及64位的有符号整数类型。由于Mongo的shell是JavaScript的shell。因此处理数字的时候会出问题。js的shell只有一种数字类型。因此在用shell进行数据存储时,可能造成更改原有数据类型的情况。

MongoDB 插入文档

插入文档使用db.COLLECTION_NAME.insert(document)其中的document是一个bson对象,里面存放的多个键表示要存储的字段,值即是数据。
实例代码:

db.COLLECTION_NAME.insert({
    '_id':ObjectId(),
    'title':'First field',
    'name':'alex',
    'tag':['tag1','tag2','tag3'],
    'describtion':'this is a document'
})

使用插入时,若果没有创建集合那么系统会自动创建这个名字的集合,如果插入的document中没有指定_id字段那么系统会自动创建一个唯一的id与这个document对应。其中这个id字段对应的是一个12个字节的16进制整数在ObjectId里面。每个文档这个id是唯一的。
当然可以一次插入多个文件,批量操作db.COLLCTION_NAME.insert([list])一次性插入多个文件,批量操作。
也可以使用`db.collection_name.save(document)’方法插入文件,实例代码:

MongoDB学习心得_第7张图片

save本质上属于更新数据库中的文档。用新文档替换旧的文档。

查询文档

最基本的语法即是db.COLLECTION_NAME.find()实现查询
一般find()后面接一个pretty()让整个显示更加结构化。

带条件的查询 等值查询

equal.JPG-16.9kB

默认的是equal的等值查询。
实例:

equalex.JPG-27.1kB

小于查询:

lessthan.JPG-14.3kB

实例:

MongoDB学习心得_第8张图片

MongoDB学习心得_第9张图片

主要就是find({‘key’:{$type:value}})这样一种语法结构。$type 可以是$lt $lte $gt $gte $ne

AND 语法

语法find({key1:value1,key2:value2}) 将多个查询条件放置在一起查询。比如实现查询数值在100与200之间的数据。使用的时候$lt的条件放置于$gt的前面

andmore.JPG-27.2kB

or 语法

db.collection.find({$or:[{key1:value1},{key2:value2}]})
实例代码:

or.JPG-22.7kB

find查询都需要给定一个BSON格式的查询数据。只不过and查询是{key1:{$type:value1}[,key2:{$type:value2}]}这样的查询模式。而or查询中需要将$or 作为查询的BSON的key,后面紧跟一个list表示查询内容。
{\$or:[{key:value},{key:value}]}

更新文档

MongoDB文档更新使用两个方法update以及save方法。update是在原有的文件基础上更新,而save方法是覆盖原文件,使用与更新前的文档的_id在此基础上进行更新。
update语法:
db.COLLECTION_NAME.update({olditem},{$set:{newitem}},{multi:true|false})
实例代码:

MongoDB学习心得_第10张图片

该方法有一个参数可以设置为true实现更新所有满足更新条件的文档的内容
db.COLLECTION_NAME.update(olditem,{$set:{newitem}},{multi:true})
save 方法略微有点不同,更新文档直接根据现有ID重新写文档。相当于insert只不过这个id要根据要更改的文档确定,感觉实用性不如update。
实例代码:

MongoDB学习心得_第11张图片

更新可以结合and和or的语法进行高级的更新操作。比如更新age在50到60的人(包含)的名字改为A。

MongoDB学习心得_第12张图片

数据库中插入了三个人的数据,现在使用更新语句实现将年龄在50到60的人改名
注意当$lte 和$gte 一起作为查询条件用时,$lte写在前面,否则出错
最终效果:

MongoDB学习心得_第13张图片
完整了这个过程。

删除文档

语法db.COLLECTION_NAME.remove(par1,justone)
remove可以接受两个参数,第一个参数是删除条件,满足条件的删除,第二个参数决定是否只是删除一个,为1表示删除一个满足条件的。当remove不接参数时,表示删除集合中的所有内容。所以千万不要盲目的不给参数。
接着上一个update的集合中完成删除年龄在50到60的人的信息

MongoDB学习心得_第14张图片

完成将50到60的人的信息删除。

MongoDB 的投影

有时候不需要吧所有的字段显示,那么需要对文档中的有效字段进行投影,投影同样使用find函数
find函数的第二个参数可以设置需要显示的查询字段列表,1表示显示0表示不显示
实例代码:

MongoDB学习心得_第15张图片

这是find函数第二个参数而已。1,0都能够用true|false替换。而且_id字段一定会显示。

Limite 限制记录Limite方法。

db.COLLECTION_NAME.find().limit(number)接收数字的参数,表示限制查询的返回结果。这个非常有用,比如需要统计年龄由高到低的前10个人可以先进行排序查询出结果然后使用limite实现。
实例代码:

limit.JPG-18.3kB

排序文档sort()

sort接收一个字段列表,类表中表明排序的基准字段,其中相应字段的值1用于升序,-1用于降序
实例代码:

MongoDB学习心得_第16张图片

数据库中插入三个文件,有一个age字段,上面代码实现从数据库中读出文件进行升序排列。
sort不带值时是默认的升序排列。

skip 跳过文档

索引

建立索引 ensureIndex(Obj)
建立索引的作用,使得查询更加的高效,快捷。升序降序根据字段决定。1升序 -1降序。索引存储在一个方便遍历的数据之中。使用时ensureIndex({key1:1,key2:-1})指定升序降序。在集合层面上的操作
实例代码建立索引

MongoDB学习心得_第17张图片

聚合aggregate

MongoDB中的聚合操作
MySQL中的分组操作在mongodb里面即是聚合操作。
比如有如下的一个集合:

operation description example
$sum 分组求和操作求出分组后指定属性的和 [{$group:{_id:’ XXX,value:{$sum: XXX’}}}]
$avg 计算分组后指定属性的平均值 [{$group:{_id:’ XXX,value:{$avg: XXX’}}}]
$max 计算分组后指定属性的最大值 [{$group:{_id:’ XXX,value:{$min: XXX’}}}]
$min 计算分组后指定属性的最小值 [{$group:{_id:’ XXX,value:{$max: XXX’}}}]
$push 将分组的结果以一个数组返回 [{$group:{_id:’ XXX,value:{$push: XXX’}}}]
$addToSet 同样是将结果以数组返回但是不会出现重复的值 [{$group:{_id:’ XXX,value:{$addToSet: XXX’}}}]
$first 返回分组文档中的第一个 [{ group:{id: XXX’,value:{$first:’$XXX’}}}]
$last 返回分组中最后一个文件 [{$group:{_id:’ XXX,value:{$last: XXX’}}}]

实例代码,如下所示的一个集合:

MongoDB学习心得_第18张图片

进行求和操作:

aggadd.JPG-17.7kB

求平均值操作:

aggavg.JPG-17.1kB

最大值操作:

aggmax.JPG-17.9kB

将分组结果生成列表:

aggpush.JPG-18.4kB

聚合框架中的管道操作
整个mongodb的aggregate操作不仅仅包含 group,json group阶段前端加上一个$match作为一个过滤操作:

aggmatch.JPG-17.4kB

先过滤掉年龄小于50的数据然后进行分组,计算每一组的和。

MongoDB 集群

复制操作

复制就是实现数据同步在多个服务器中,实现数据的冗余备份,将数据存储于多个服务器可以提高数据的可用性和安全性,允许从硬件故障与服务中断中恢复数据。mongodb使用副本集达到复制的功能。副本集是一组mongod实例,具有唯一一个主节点,用于接收所有写的操作。其他节点与主节点的数据同步。

MongoDB学习心得_第19张图片

mongodb的复制依赖于副本集合。副本集是两个或者多个数据节点构成的集合。其中一个为主节点负责处理并记录客户端的操作。其他的节点为从节点,定期轮询主节点获取主节点的操作,然后再本数据节点上执行这些操作,最终达到与主节点数据同步的效果。副本集特点是本身是具有多个数据节点的集群。任何客户端的操作都是操作主节点,从节点只是负责同步并且在故障发生时可以完成自动的故障转移与恢复。MongoDB 的副本集主节点宕机后从节点会接替主节点成为新的主节点。因此整个副本集不会因为主节点宕机而全部停止工作。

实例:
启动mongod服务的时候需要增加--replSet 'name'的指令启动一个命名的mongodb服务。然后启动mongdb客户端。

mongod --path=/XXXXXX/ --replSet a --port=28010

创建一个名字为a占用端口为28010的mongodb服务。启动客户端,使用rs.initiate()方法初始化副本集。之后就可以以当前节点为主节点添加其他从节点到副本集里面,添加方法为:

rs.add('hostname:port')

这样一个简单的副本集构建完成。

分片操作

MongoDB中的另外一种集群操作就是分片。使得MongoDB系统能够处理大量数据增长的需求。提高数据库系统的吞吐量,将数据分割在不同的服务器上,使其能够处理更多的数据。

MongoDB学习心得_第20张图片

数据库备份与恢复 mongodump mongorestore

导出数据到指定的目录中去
完整指令

mongodump -h hostname -d dbname -o dbdirectory

数据库恢复,从备份数据中恢复数据库

mongorestore -h hostname -d dbname --directoryperdb db

最后一个参数是备份文件的位置。

数据库监控mongostate mongotop

mongostate 自带命令,每隔一段时间输出当前数据库状态

mongotop 自带工具,可以查看MongoDB实例中的数据库读写的时间。

高级部分

数据库引用

存在如下的应用场景,一个集合中的文档数据需要引用其他文档集合中的数据。此时需要使用数据库的引用。使用方法是在需要执行引用的文档的字段中插入如下内容:

{$ref:引用集合名称,$id:引用的文档的id,$db:引用的数据库名称(可选)}

MongoDB 索引查询

索引查询即是查询的内容以及返回的内容都是索引的一部分。由于索引内容都是在内存里面,所以这种查询可以提高查询的速度。索引构建本身可以构建复合索引,但是查询时需要注意,只有查询与定义复合索引相同的前N个字段才可能用到索引查询,不然无效。

MongoDB 的查询分析

使用explain命令。

MongoDB 的原子操作

Mongoose 学习

MongoDB的shell是JavaScript的运行环境,shell环境中操作MongoDB的语法无法在实际的软件运行场景中使用,实际工程使用中需要使用MongoDB的驱动程序完成数据库的操作,这一点与关系型数据库一样。此处在NodeJs运行环境中使用Mongoose驱动完成MongoDB的操作。Mongoose可直接通过npm进行安装。

Mongoose 链接数据库

使用mongoose连接mongodb可以用一下的两种方式实现。

    mongoose.connect("mongodb://hostname/dbname");
    var db = mongoose.connection;
    var db = mongoose.createConnection('hostname','dbname');

两种方法都能够得到数据库的实例。推荐使用第一种方法构建数据库的实例。实例创建成功时可以执行数据库的操作。

Mongoose 三大基本概念

Schema

Schema 故名思意是模式。此处schema最终会映射到MongoDB中的一个集合。Schema主要定义了集合中的键的名称以及类型。同时schema可以定义文档(模型的实例)的方法以及模型(model)的方法。总之schema是数据的一个高度抽象的概念,是MongoDB中集合的骨架。
构建schema
定义文档的方法

    schema.methods.fn = function(){};

构建的实例方法可以在model的实例上面执行一些操作。
定义模型的静态方法

    schema.statics.fn = function(){};

将model作为一个类看待,那么这个静态方法就是类的方法。
使用schema扩展的实例方法与类方法可以在原有的数据库操作提供的基本方法之上添加一些新的特性。可以利用这些方法将返回的数据做进一步处理。
使用virtual虚拟属性。
虚拟属性是一种不存在的属性,它是原有schema中的数据的重新组合。其实虚拟属性可以用实例方法替代。只不过一个作为属性的返回,一个作为函数的返回值返回。
schema定义索引
schema具有两种定义索引的方式。在定义键的时候可以使用index属性给值为true那么就将这个键设置为索引。使用index({})函数将制定的键作为索引。
schema中的option操作。
option操作提供了定义schema的一些额外的选项。操作option有两种方式,第一在定义schema的时候,调用构造函数时将option的json作为第二个参数传递,或者使用set(option,value)实现。autoIndex属性是否自动创建索引。构建schema中会有在驱动层面创建索引的行为。默认情况下,程序启动时,数据库会调用ensureIndex在服务端后台运行数据库索引的创建。实现这个过程手动创建,需要将option中的autoIndex设置为false然后在需要调用的地方使用model.ensureIndexes(cb)实现数据库索引的创建。capped 顾名思义。限定数据库的大小,使用语法

schema.set('capped',{size:1000,max:100,autoIndexId:true});

collection:name 指定集合的名字。一般情况下,集合的名字是在model定义的时候绑定的,然而option中使用这个属性可以直接指定数据库中集合的名字。此时不会受到model指定名字的干扰,model指定的名字的复数才是mongodb中的集合。在构建schema的时候使用了collection选项可以保证指定的名称不会受到model的干扰。

id:true|false 这个可选属性决定了model的实例的id属性的行为。在定义schema的时候如果这个id属性为true。那么在model的实例中调用id会返回这个实例的_id的字符串形式,一般情况下_id为ObjectId 所以一般返回的是16进制的24位字符串。ObjectId的string形式。

_id:true|false 如果说id字段只是为了控制model实例中使用id属性是否可以访问到实例的_id。那么这个字段就是决定了js环境中的model实例是否真的具有这个字段。这个option比较特殊,只能够在构建mongodb的时候指定,不能够用set的方法指定。而且当数据被插入到数据库中是,数据库会自动为当前实例创建一个默认的id字段。所以这个功能感觉没有意义。
read 属性,貌似与构建数据库集群相关。

safe也是和数据库的集群相关的。
shardKey 这个属性和shard mongodb相关,也就是和mongodb的分片相关。属于集群中的一部分。

strict 严格配置。默认开启的,存入数据库的实例在放入数据库之前必须进行验证,如果不满足schema定义的键那么无法存储。当然在构建实例的时候如果第二个参数使用了false也可以覆盖这个strict,实现不需检查就可以将数据放入数据库。

versionKey 版本锁。默认的使用mongoose创建的实例会有一个_V的键。这个就是版本锁,默认名称为_V
使用versionKey可以使用自定义的名称作为版本锁的名称。

vk.JPG-18.2kB

使用versionKey 后这个键名称替换为指定名称,当然可以设置为false那么就没有这个键。

schema中的数据类型 在Schema.Types里面定义

String Number Date Boolean ObjectId Mixed Array Buffer
同时在schema中定义一个键时可以采用直接方式{name:schematype}
或者是使用详细的定义{type:schematype,other}来定义一个键的类型。特别注意定义array的时候用的是一个[]里面指定数据类型即可。array是比较特殊的一种类型定义。还有一种比较特殊的类型Mixed类型。
mixed类型指的是任何一种类型,如果schema中的一个键类型是mixed。代表它的值是任意类型的.Mixed的定义有两种,一种是指定类型Schema.Types.Mixed 另外一种是直接给一个空的Object–{}.下面两种定义方式最终效果一样的。

    var schema = mongoose.Schema({any:Schema.Types.Mixed});
    var schema = mongoose.Schema({any:{}});

使用Mixed最重要的一点是数据更改了需要通知Mongoose所以在存储含有Mixed的属性先要使用

    doc.markModified(attr);

通知Mongoose含有Mixed的属性被更改了,然后执行后续的CRUD操作。

Schema.Types.ObjectId 这种类型与Schema.Types.Mixed属于特殊类型,因为他们不是Js的类型。完整的使用Schema.Types.ObjectId作为类型定义。
Date属性不能够更改了后自动通知Mongoose需要使用markModified进行通知。

完整的一个用到所有的schema.types的定义如下:

MongoDB学习心得_第21张图片

从数据库中find得到的结果:

MongoDB学习心得_第22张图片

Model

model可以看成一个构造函数,它的实例代表着数据库中的document。model接收两个参数,一个是数据库中集合名称,另外一个是schema.创建的语法如下。

    var model = mongoose.model('collectionname(s)',schema);

将model实例化就是可操作的文档对象。一般基本的数据库操作都是在model层面上完成。

Entity

Entity是model的一个实例,代表着数据库中的文档。一些数据库的基本操作可以在这个实例上进行操作,映射到数据库中。也可以在model上进行数据库操作。要保证实例操作的正确性,需要保证数据库处于正常的连接状态。

    var doc = new model(data);
    doc.save(callback);
    model.find(function(err,data){});//完成查询操作

包含子模式作为subdoc。 子文档其实是Mixed类型的一种规范化。将模式进行拆分。当存在处理内嵌文档的情景时,子模式就对应于内嵌的文档类型。但是两者还是有本质区别。使用内嵌文档如果使用定义Schema时指定内嵌类型而不是使用子schema 方式,那么是无法使用子schema的一些特性。比如根据id搜索以及使用中间件等。更重要一点传统的内嵌定义法内嵌内容只是一个Object而使用子模式定义的则是一个含有id的子文档。

Queries in mongoose

mongoose中的查询主要有两种方式,第一种是一次性写完查询所需的所有参数,传递回调函数,查询结果以数组形式出现在回调函数的参数中。另外一种方法是分布的查询。不直接给出查询最后的回调,每一次给定查询的一部分参数,这样查询返回的是一个query。query支持链式调用,添加查询条件最终执行exec的回调的函数里面,回调的结果则是查询的结果。

Model.find(query,fields,options,callback);
Model.find(query).where().select().limit().exec(callback);

第二种情况可以将完整的query进行拆分,最后exec中的callback获取返回的结果。两种情况是等价的,只不过第二种更加灵活的添加查询相关的条件。
find外扩展count返回满足条件的文档数目,update回调中得到被影响的文件数量。

大方向上的query包含三种查询find findById findOne

Validation

数据类型验证。在数据存入数据库的时候会执行。每一种Schema.Type都具有内置的数据验证。认证最强大的功能在于它可以是用户定制的,可以定制自己的一些认证条件。

Middleware 中间件

Mongoose的中间件是一种在document level的控制流函数,在document进行初始化,验证,存储和删除的时候起作用。与文档的声明周期非常相关。中间件的定义在Schema的基础上实现的。大致存在两种Middleware。pre和post两种Middleware前置中间件与后置中间件。顾名思义,前置中间件在生命周期函数之前执行,后置中间件在声明周期函数之后执行.pre前置中间件也分为两种一种串行中间件serial和并行的中间件parallel.
serial型中间件串行传递数据流。传递的终点是生命周期函数,所以每一个middle函数必须使用next不然声明周期函数无法执行。中间价中传递了error会直接到声明周期函数的err参数中。
语法

schema.pre('verb',function(next){
    //todo
    next();//将控制流交于下一个中间件,必须有
});

并行中间件

schema.pre('verb',true,function(next,done){
    next();//并行触发其他中间件执行。
    done();//每一个中间件任务执行完毕必须调用,当所有并行中间件都调用done之后才执行钩子函数
});

后置post中间件。在钩子函数执行之后触发,比前置中间件简单,只是作为钩子函数的监听器。

schema.post('verb',function(doc){
    console.log('doc name os %s',doc.name);
});

非常重要的一点是中间件只是在document级别有效,如findAndUpdate等方法不会触发中间件,因为这不是document级别的操作。

population 集合引用

关系型数据库中没有join 那一种多表链接的操作,但是在某些应用场景需要使用多表链接的情况。此时就要使用到population。使用这个特性只需要在定义键的时候指定类型与引用的模型的名字即可。

sh1 = mongoose.Schema({name:String,refs:{type:mongoose.Schema.Types.ObjectId,ref:'md2'});
//注意使用的是model的名字
md1 = mongoose.model('md1',sh1);
md2 = mongoose.model('md2',sh2);

此时会引用md2这个模型。此处的类型匹配规则是引用部分的type必须与ref引用的模型的_id的类型一样,一般选择类型是ObjectId,所以绝大多数情况下这个地方使用ObjectId类型。通过以上几步建立了一个文档与另外一个文档的引用关系。在存储的时候必须要注意,先存储要被引用的文档,因为存储引用文档的时候需要使用被引用文档。

var arg2 = new md2({});
arg2.save((err,data){
    //首先存储被引用文档
    /..../
    var arg1 = new md1({
        refs:arg2._id//引用文档2的id
    });
    arg1.save();//完成含有引用文档的存储。
});

默认引用的字段为_id。因为可以通过这个_id完成对引用文档的获取,这个主要就是使用了population 的查询方法。

你可能感兴趣的:(MongoDB)