MongoDB教程
一、 MongoDB创建数据库
use DATABASE_NAME #如果数据库不存在,则创建数据库,否则切换到指定数据库
show dbs #查看所有数据库。如果新建的数据库中没有数据,则不会显示在列表中
MongoDB中默认的数据库是test, 如果没有创建新的数据库,集合将存放在test数据库中
二、 MongoDB删除数据库
db.dropDatabase() #删除数据库,默认为test.可使用db命令查看当前数据库名
db.collection.drop() #删除集合,其中collection为集合名
三、 MongoDB插入文档
MongoDB使用insert()或save()方法向集合中插入文档。
db.COLLECTION_NAME.insert(documet) #插入文档
db.COLLECTION_NAME.find() #查看已输入文档
当然,也可以将document定义成一个变量。例如:
>document=({"name":"xiongshiting"});
>db.doc.insert(document);
插入文档也可以使用db.doc.save(document)命令,如果不指定_id字段,该方法类似于insert();如果指定_id字段,则会更新该_id的数据。
四、 MongoDB更新文档
MongoDB使用update()和save()方法来更新集合中的文档。
Update() 用于更新已存在的集合。语法如下:
db.collection.update(
<query>,
<update>,
{
upsert:<boolean>,
multi:<boolean>,
writeConcern:<document>
}
)
参数说明如下:
l Query : update的查询条件,类似sql update查询内where后面的;
l Update : update的对象和一些更新的操作符(如: $,$inc)
l Upsert: 可选参数。如果不存在update的记录,是否插入objNew, true为插入,默认为false,不插入
l Multi: 可选,默认为false,只更新找到的第一条记录。如果这个参数为true,就把按条件查出来多条记录全部更新;
l writeConcern: 可选,抛出异常的级别;
例如:首先在集合中插入数据,然后更新数据。
> db.doc.insert({"title":"MongoDB教程"}); #插入数据
> db.doc.update({"title":"MongoDB教程"},{$set: {"title":"MongoDB"}}); #修改titile键的值,只会修改第一处匹配的
> db.doc.update({"title":"MongoDB教程"},{$set: {"title":"MongoDB"}}),{multi: true} #修改所有匹配的值
Save()方法通过传入的文档来替换已有文档,语法如下:
Db.collection.save(
<document>,
{
writeConcern:<document>
}
)
参数说明:
l document: 文档数据
l writeConcern: 可选,抛出异常的级别
更多实例
只更新第一条记录:
db.col.update({ "count" : { $gt : 1 } } , { $set : { "test2" :"OK"} } );
全部更新:
db.col.update({ "count" : { $gt : 3 } } , { $set : { "test2" :"OK"} },false,true );
只添加第一条:
db.col.update({ "count" : { $gt : 4 } } , { $set : { "test5" :"OK"} },true,false );
全部添加加进去:
db.col.update({ "count" : { $gt : 5 } } , { $set : { "test5" :"OK"} },true,true );
全部更新:
db.col.update({ "count" : { $gt : 15 } } , { $inc : { "count" : 1}},false,true );
只更新第一条记录:
db.col.update({ "count" : { $gt : 10 } } , { $inc : { "count" : 1}},false,false );
五、 MongoDB删除文档
MongoDB remove()函数用来移除集合中的数据。在执行remove()函数前先执行find()命令来判断执行的条件是否正确。
Remove()方法的语法如下:
Db.collection.remove(
<query>,
<justone>
)
2.6版本以后的格式:
Db.collection.remove(
<query>,
{
Justone: <boolean>,
writeConcern: <document>
}
)
参数说明:
l Query: 可选,删除的文档的条件
l JustOne: 可选,如果设为true或1,则只删除一个文档
l WriteConcern: 可选,抛出异常的级别
例如:删除title键为MongoDB的值
>db.collection.remove({“title”: “MongoDB”});
如果只想删除第一条找到的记录可以设置justOne为1:
>db.collection_name.remove(DELETION_CRITERIA,1)
删除所有记录
>db.collection_name.remove({})
六、 MongoDB查询文档
MongoDB查询数据语法如下:
>db.collection_name.find();
find()以非结构化的方法显示所有文档。如果以易读方式读取数据,使用pretty()方法,语法如下:
>db.collection_name.find().pretty();
findOne()方法,只返回一个文档。
MongoDB与RDBMS的where语句比较:
MongoDB AND 条件
MongoDB的find()方法可以传入多个键(key),每个键(key)以逗号隔开。语法如下:
>db.col.find({key1.value1,key2.value2}).pretty();
例如:通过by和title键来查询菜鸟教程中的MongoDB教程:
>db.col.find({“by”:”菜鸟教程”,”title”:”MongoDB 教程”}).pretty();
MongoDB OR 条件
MongoDB OR条件语句使用了关键字$or,语法如下:
>db.col.find(
{
$or: [
{key1:value1},{key2:value2}
]
}
)
例如:查询键by值为菜鸟教程或键title值为MongoDB教程的文档
>db.col.find({$or:[{“by”: “菜鸟教程”},{“title”: “MongoDB 教程”}]}).pretty();
AND 和OR 联合使用
>db.col.find({“likes”:{$gt: 50}, $or: [{“by”: “菜鸟教程”},{“title”: “MongoDB教程”}]}).pretty();
七、 MongoDB条件操作符
条件操作符用于比较两个表达式并从mongodb集合中获取数据。
MongoDB中条件操作符有:
Ø > 大于 $gt
Ø < 小于 $lt
Ø >= 大于等于 $gte
Ø <= 小于等于 $lte
1. MongoDB(>) 大于操作符-$gt
获取col集合中”likes”大于100的数据:
>db.col.find({“likes”: {$gt: 100}})
2. MongoDB(>=) 大于等于操作符-$gte
获取col集合中”likes”大于100的数据:
>db.col.find({"likes”: {$gte:100}})
3. MongoDB(<) 小于操作符-$lt
获取col集合中”likes”小于100的数据:
>db.col.find({“likes”: {$lt: 100}})
4. MongoDB(<=)小于等于操作符-$lte
获取col集合中”likes”小于等于100的数据:
>db.col.find(“likes”:{$lte: 100})
5. MongoDB使用(<)和(>)查询 -$lt和$gt
获取col接中”likes”大于100,小于200的数据:
>db.col.find({likes:{$lt: 200, $gt: 100}})
八、 MongoDB $type操作符
$type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果。
MongoDB中可以使用的类型如下:
类型 |
数字 |
备注 |
Double |
1 |
|
String |
2 |
|
Object |
3 |
|
Array |
4 |
|
Binary data |
5 |
|
Undefined |
6 |
已废弃 |
Object id |
7 |
|
Boolean |
8 |
|
Date |
9 |
|
Null |
10 |
|
Regular Expression |
11 |
|
JavaScript |
13 |
|
Symbol |
14 |
|
JavaScript(with scope) |
15 |
|
32-bit integer |
16 |
|
Timestamp |
17 |
|
64-bit integer |
18 |
|
Min key |
255 |
Query with -1 |
Max key |
127 |
|
例如: 如果想获取col集合中title为String的数据:
>db.col.find({“title”:{$type: 2}})
九、 MongoDB Limit与Skip方法
MongoDB Limit() 用来获取指定数量的数据记录,该方法接受一个数字参数,该参数指定从MongoDB中读取的记录条数。语法如下:
>db.collection_name.find().limit(number)
>db.col.find({},{“title”: 1,_id:0}).limit(2) #显示查询文档中的两条记录,如果没指定limit方法中的参数,默认显示所有数据
MongoDB Skip() 用来跳过指定数量的数据,该方法接受一个数字参数作为跳过的记录条数。语法如下:
>db.collection_name.find().limit(number).skip(number)
>db.col.find({},{“title”: 1,_id: 0}).limit(1).skip(1) #显示第二条文档数据。Skip()方法默认参数为0.
十、 MongoDB排序
MongoDB使用sort()方法对数据进行排序,sort()方法可以通过参数指定排序的字段,并使用1和-1来指定排序的方式。1为升序,-1为降序。
语法如下:
>db.collection_name.find().sort({KEY:1})
例如:col集合中的数据按字段likes降序排列:
>db.col.find({},{“title”: 1, _id: 0}).sort({“likes”: -1})
十一、 MongoDB索引
MongoDB使用ensureIndex()方法来创建索引。语法如下:
>db.collection_name.ensureIndex({KEY:1})
KEY值:表示要创建的索引字段
1:指定按升序创建索引 -1:指定按降序创建索引
例如:以title字段来创建索引
>db.col.ensureIndex({“title”:1})
#设置多个字段来创建索引
>db.col.ensureIndex({“title”: 1, “description”: -1})
ensureIndex()接收可选参数,可选参数如下:
Parameter |
Type |
Description |
backgroud |
Boolean |
建索引过程会阻塞其他数据库操作,backgroud可指定以后台方式创建索引,即增加“backgroud”可选参数,“backgroud”默认值为false. |
Unique |
Boolean |
建立的索引是否唯一。指定为true创建唯一索引。默认值为false. |
name |
String |
索引的名称。如果未指定,MongoDB通过连接索引的字段和排序顺序生成一个索引名称 |
dropDups |
Boolean |
在建立唯一索引时是否删除重复记录,指定true创建唯一索引。默认值为false. |
sparse |
Boolean |
对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档。默认值为false. |
expireAfterSeconds |
Integer |
指定一个以秒为单位的数值,完成TTL设定,设定集合的生存时间 |
v |
Index version |
索引的版本号。默认的索引版本取决于mogod创建索引时运行的版本 |
weights |
document |
索引权重值,数值在1到99999之间,表示该索引相对于其他索引字段的得分权重 |
default_language |
string |
对于文本索引,该参数决定了停用词及词干和词器的规则的方法,默认为英语 |
language_override |
string |
对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认language,默认值为language |
例如:在后台创建索引
db.values.ensureIndex({open:1, close: 1}, {background: true})
十二、 MongoDB聚合
MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。类似于count(*).
aggregate()方法的基本语法如下:
>db.collection_name.aggregate(AGGREGATE_OPERATION)
例如:计算集合中每个作者所写的文章数,使用aggregate()计算结果如下:
>db.mycol.aggregate({$group:{ _id : “$by_user”, num_tutorial: {$num: 1}})
该例中,我们通过字段by_user字段对数据进行分组,并计算by_user字段相同值的总和。
一些聚合的表达式:
表达式 |
描述 |
实例 |
$num |
计算总和 |
db.mycol.aggregate( {$group: {_id: “$by_user”, num_tutorial: {$num: “$likes”}}}) |
$avg |
计算平均值 |
db.mycol.aggregate( {$group: {_id : “$by_user”,num_tutorial: {$avg: “$likes”}}}) |
$min |
获取集合中所有文档对应值的最小值 |
db.mycol.aggregate( {$group: {_id: “by_user”,num_tutorial: {$min: “$likes”}}}) |
$max |
获取集合中所有文档对应值的最大值 |
db.mycol.aggregate({$group: {_id: “$by_user”, num_tutorial: {$max: “$likes”}}}) |
$push |
在结果文档中插入值到一个数组中 |
db.mycol.aggregate({$group: {_id: “$by_user”, url: {$push: “$url”}}}) |
$addToSet |
在结果文档中插入值到一个数组中,但不创建副本 |
db.mycol.aggregate( {$group: {_id : “$by_user”, url: {$addToSet: “$url”}}}) |
$first |
根据资源文档的排序获取第一个文档数据 |
db.mycol.aggregate( {$group: {_id: “$by_user”,first_url: {$first: “$url”}}}) |
$last |
根据资源文档的排序获取最后一个文档数据 |
db.mycol.aggregate( {$group: {_id: “$by_user”,last_url: {$last: “$url”}}}) |
管道的概念
MongoDB的聚合管道将MongoDB文档在一个管理处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。
表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。
聚合框架下常用的几个操作:
l $project: 修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档;
l $match: 用于过滤数据,只输出符合条件的文档,$match使用MongoDB的标准查询操作
l $limit: 用来限制MongoDB聚合管道返回的文档
l $skip: 在聚合查询中跳过指定数量的文档,并返回余下的文档
l $unwind: 将文档中某一个数组类型字段拆分成多条,每条包含数组中的一个值
l $group: 将集合中的文档分组,可用于统计结果
l $sort: 将输入文档排序后输出
l $geoNear: 输出接近某一地理位置的有序文档
管道操作符实例
1.$project
db.article.aggregate({$project: {title:1,author: 1}})
以上结果中就只有_id,title和author三个字段了。默认情况下_id字段是被包含的,如果不想包含,可以这样:
db.article.aggregate({$project:{_id: 0, title: 1,author: 1}})
2.$match
db.article.aggregate( [
{$match: { score : {$gt : 70,$lte: 90}}},
{$group: {_id:null, count:{$num: 1}}}
]
)
$match用于获取分数大于70或小于等于90的记录,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。
3.$skip
db.article.aggregate({$skip: 5 });
过滤掉前5个文档;
十三、 MongoDB复制(副本集)
MongoDB复制是将数据同步在多个服务器的过程。
复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性,并可以保证数据的安全性。
MongoDB的复制至少需要两个节点,其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。
MongoDB各个节点常见的搭配方式为:一主一从,一主多从。
主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。
MongoDB复制结构如下:
客户端总主节点读取数据,在客户端写入数据到主节点时,主节点与从节点进行数据交互保障数据的一致性。
副本集特征:
l N个节点的集群
l 任何节点可作为主节点
l 所有写入操作都在主节点上
l 自动故障转移
l 自动恢复
MongoDB副本集设置:
关闭正在运行的MongoDB服务器。
通过指定—replSet选项来启动MongoDB。 --replSet语法格式如下:
Mongod –port “PORT” --dbpath “YOUR_DB_DATA_PATH” --replSet “REPLICA_SET_INSTANCE_NAME”;
例如:
Mongod –port 27017–dbpath “D:set upmongodbdata” --replSet rs0
上述会启动一个名为rs0的MongoDB,端口号为27017.
在MongoDB客户端使用命令rs.initiate()来启动一个新的副本集。
使用rs.conf()来查看副本集的配置
使用rs.status()命令来查看副本集状态
副本集添加成员
添加副本集的成员,需要使用多条服务器来启动mongo服务。进入Mongo客户端,使用rs.add()方法来添加副本集的成员。
rs.add()命令语法如下:
>rs.add(HOST_NAME:PORT)
例如:已启动一个名为mongod1.net, 端口号为27017的mongo服务。在客户端命令使用rs.add()命令将其添加到副本集中。
>rs.add(“mongod1.net:27017”)
MongoDB中只能通过主节点将Mongo服务添加到副本集中。
使用命令db.isMaster() 可判断当前运行的Mongo服务是否为主节点。
MongoDB的主从在主机宕机后所有服务停止,而副本集在主机宕机后,副本会接管原来的主节点而成为主节点,不会出现宕机。
十四、 MongoDB分片
为什么使用分片
l 复制所有的写入操作到主节点
l 延迟的敏感数据会在主节点查询
l 单个副本集限制在12个字节
l 当请求量巨大时会出现内存不足
l 本地磁盘不足
l 垂直扩展价格昂贵
MongoDB分片集群结构分布:
Shard: 用于存储实际的数据块,实际生产环境中一个shard server角色可由几台机器组成一个replica set承担,防止主机单点故障
Config Server: mongod实例,存储了整个ClusterMetadata,其中包括chunk信息
Query Routers: 前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用。
十五、 MongoDB备份与恢复
使用mongodump命令来备份MongoDB数据。该命令可导出所有数据到指定目录中。还可通过参数指定导出的数据量级转存的服务器。语法如下:
>mongodump –h dbhost–d dbname –o dbdirectory
-h: MongoDB所在服务器地址,当然也能指定端口号
-d: 需要备份的数据库实例
-o: 备份的数据存放位置
实例:进入MongoDB安装目录bin目录下,输入mongodump即可备份,并备份数据到bin/dump目录下。
Mongodump命令可选参数列表如下所示:
语法 |
描述 |
实例 |
Mongodump --host HOST_NAME --port PORT_NUMBER |
将备份所有MongoDB数据 |
Mongodump –host w3cschool –port 27017 |
Mongodump --dbpath DB_PATH –out BACKUP_DIRECTORY |
|
Mongodump –dbpath /data/db –out /data/backup/ |
Mongodump --collection COLLECTION –db DB_NAME |
备份指定数据库的集合 |
Mongodump –collection mycol –db test |
MongoDB数据恢复
Mongodb使用mongorestore命令来恢复备份的数据。语法如下:
>mongorestore --h <hostname><:port> --d dbname $lt;path$gt;
--host<:port>,--h<:port>:MongoDB所在服务器地址,默认为localhost:27017
--db, -d: 需要恢复的数据库实例
--drop: 恢复的时候,先删除当前数据,然后恢复备份的数据。就是说,恢复后,备份后添加修改的数据都会被删除,慎用。
<path>:mongorestore 最后一个参数,设置备份数据所在位置。不可同时指定<path>和—dir选项。
--dir: 指定备份的目录。不能同时指定<path>和—dir选项
十六、 MongoDB监控
MongoDB提供了mongostat和mongotop两个命令来监控MongoDB的运行情况。
Mongostat:自带的MongoDB状态检测工具,命令行下使用,它会间隔固定时间获取mongodb的当前运行状态,并输出。
Mongotop: 用来跟踪一个MongoDB的实例,查看哪些大量时间花费在读取和写入数据。Mongotop提供每个集合的水平的统计数据。默认情况下,mongotop返回值的每一秒。
>mongotop 10 #10为sleeptime,单位为秒。
>mongotop –locks #报告每个数据库的锁的使用中
输出结果字段说明:
Ø ns: 包含数据库命名空间,后者结合了数据库名称和集合
Ø db: 包含数据库的名称。名为.的数据库针对全局锁定,而非特定数据
Ø total:mongod花费的时间工作在这个命名空间提供总额
Ø read: 提供了大量的时间,这mongod花费在执行读操作,在此命名空间
Ø write: 提供这个命名空间进行写操作,这mongod花了大量时间。
MongoDB高级教程
十七、 MongoDB关系
MongoDB的关系表示多个文档之间在逻辑上的相互联系。
文档间可以通过嵌入和引用来建立联系。
MongoDB中的关系可以是:1对1,1对多,多对1,多对多。
【嵌入式关系】
使用嵌入式方法,可以把用户地址嵌入到用户文档中。
这种结构的缺点是:如果用户和用户地址不断增加,数据量不断变大,会影响读写性能。
【引用式关系】
引用式关系是把用户数据文档和用户地址数据文档分开,通过引用文档的ID字段来建立关系。
{
"_id":ObjectId("52ffc33cd85242f436000001"),
"contact": "987654321",
"dob": "01-01-1991",
"name": "Tom Benzamin",
"address_ids": [
ObjectId("52ffc4a5d85242602e000000"),
ObjectId("52ffc4a5d85242602e000001")
]
}
该实例中用户文档的address_ids字段包含用户地址的对象ID数据。
可以读取这些用户地址的对象ID来获取用户的详细地址信息。
该方法需要两次查询,第一次查询用户地址的对象ID,第二次通过查询ID获取用户的详细地址信息。
>var result =db.users.findOne({"name":"TomBenzamin"},{"address_ids":1})
>var addresses =db.address.find({"_id":{"$in":result["address_ids"]}})
十八、 MongoDB数据库引用
MongoDB引用有两种:手动引用和DBRefs
在调用不同地址时,也需要指定集合,一个文档从多个集合引用文档,应该使用DBRefs.
DBRefs的形式:{ $ref : , $id: , $db: }
$ref 集合名称
$id 引用的id
$db 数据库名称,可选参数
以下用户数据文档使用了DBRef, 字段address
{
"_id":ObjectId("53402597d852426020000002"),
"address": {
"$ref": "address_home",
"$id": ObjectId("534009e4d852427820000002"),
"$db": "w3cschoolcc"},
"contact": "987654321",
"dob": "01-01-1991",
"name": "Tom Benzamin"
}
address DBRef字段指定了引用的地址文档是在address_home集合下的w3cschool数据库,id为534009e4d852427820000002
通过指定$ref参数(address_home集合)来查找集合中指定id的用户地址信息:
>var user =db.users.findOne({"name":"Tom Benzamin"})
>var dbRef = user.address
>db[dbRef.$ref].findOne({"_id":(dbRef.$id)})
返回结果:
{
"_id" : ObjectId("534009e4d852427820000002"),
"building" : "22 A, Indiana Apt",
"pincode" : 123456,
"city" : "Los Angeles",
"state" : "California"
}
十九、 MongoDB覆盖索引查询
覆盖索引查询是以下的查询:
l 所有的查询字段是索引的一部分
l 所有的查询返回字段在同一个索引中
使用覆盖索引查询:
使用users集合
{
"_id": ObjectId("53402597d852426020000002"),
"contact": "987654321",
"dob": "01-01-1991",
"gender": "M",
"name": "Tom Benzamin",
"user_name": "tombenzamin"
}
在users集合中创建联合索引,字段为gender和user_name:
>db.users.ensureIndex({gender:1 , user_name: 1})
该索引会覆盖以下查询:
>db.users.find({gender: “M”}, {user_name:1, _id:0 })
上述查询,MongoDB不会去数据库文件中查找。它会从索引中提取数据。
由于索引中不包括_id字段,_id在查询中会默认返回,我们可以在MongoDB的查询结果集中排除它。
如果实例没有排除_id,查询就不会被覆盖:
>db.users.find({gender: “M”}, {user_name: 1})
以下查询不能使用覆盖索引查询:
l 所有索引字段是一个数组
l 所有索引字段是一个子文档
二十、 MongoDB查询分析
MongoDB查询分析常用函数有:explain()和hint().
Explain操作提供了查询信息,使用索引及查询统计等。有利于对索引的优化。
在users集合中创建gender和user_name的索引:
>db.users.ensureIndex({gender:1 , user_name: 1})
在查询语句中使用explain:
>db.users.find({gender: “M” }, {user_name: 1,_id: 0}).explain()
explain()查询结果的字段:
l indexOnly 字段为true,表示使用了索引
l cursor 因为该查询使用了索引,MongoDB中索引存储在B树结构中,所以这是使用了BtreeCursor类型的游标。如果没有使用索引, 游标的类型是BasicCursor.这个键还会给出你所使用的索引的名称,你通过这个名称可以查看当前数据库下的system.indexes集合来得到索引的详细信息。
l n 当前查询返回的文档数量
l nscanned/nsannedObjects: 表明当前这次查询一共扫描了集合中多少个文档,我们目的是,让这个数值和返回文档的数量越接近越好。
l millis 当前查询所需时间,毫秒数
l indexBounds 当前查询具体使用的索引
使用hint()
使用hints来强迫MongoDB使用一个指定的索引。这种方法某些情形下会提升性能。一个有索引的collection并且执行一个多字段的查询。
如下查询实例指定了使用gender和user_name索引字段来查询:
>db.users.find( {gender: “M”},{user_name:1, _id: 0}).hint( {gender:1,user_name:1})
可以使用explain()函数来分析以上查询:
>db.users.find( {gender: “M”},{user_name:1,_id:0}).hint( {gender:1,user_name:1}).explain()
二十一、 MongoDB原子操作
MongoDB不支持事务。
所谓原子操作就是:要么这个文档保存到Mongodb,要么没有保存到MongoDB,不会出现查询到的文档没有保存完整的情况。
如下实例说明了在一个相同的文档中如何确保嵌入字段关联原子操作的字段是同步的。
book = {
_id: 123456789,
title: "MongoDB: The Definitive Guide",
author: [ "Kristina Chodorow", "Mike Dirolf" ],
published_date: ISODate("2010-09-24"),
pages: 216,
language: "English",
publisher_id: "oreilly",
available: 3,
checkout: [ { by: "joe", date: ISODate("2012-10-15")} ]
}
可以使用db.collection.findAndModify()方法来判断书籍是否可结算并更新新的结算信息。
db.book.findAndModify( {
query: {
_id: 123456789,
Available: { $gt : 0 }
},
update: {
$inc : { available : -1 },
$push: { checkout: { by: “abc”, date:new date () }}
}
} )
原子操作常用命令
$set 用来指定一个键并更新键值,若键不存在并创建
{ $set : { field : value } }
$unset 用来删除一个键
{ $unset : {field : 1}}
$inc 可以对文档的某个值为数字型(只能为满足要求的数字)的键进行增减的操作
{ $inc : {field : value }}
$push
{ $push : {field : value}}
把value追加到field里面去,field一定要是数组类型才行。如果field不存在,会新增一个数组类型加进去、
$pushAll 同$push,只是一次可以追加多个值到一个数组字段内。
{ $pushAll : { field : value_array }}
$pull 从数组field内删除一个等于value的值
{ $pull : { field : value }}
$addToSet 增加一个值到数组内,而且只有当这个值不在数组内才增加
$pop 删除数组的第一个或最后一个元素
{ $pop : {field : 1}}
$rename 修改字段名称
{ $rename : { old_field_name : new_field_name }}
$bit 位操作,integer类型
{ $bit : {field : {and : 5 }}}
二十二、 MongoDB高级索引
{
"address": {
"city": "Los Angeles",
"state": "California",
"pincode": "123"
},
"tags": [
"music",
"cricket",
"blogs"
],
"name": "Tom Benzamin"
}
假设基于标签来检索用户,需要对集合中的数目tags建立索引。
在数组中创建索引,需要对数组中的每个字段依次建立索引。所以在为数组tags创建索引时,会为标签中的所有值分别建立单独的索引。
【索引数组字段】
创建数组索引
>db.users.ensureIndex({“tags”: 1})
l 创建索引后,检索集合的tags字段
>db.users.find( {tags:“cricket”})
l 使用explain命令来验证是否使用了索引
>db.users.find( {tags: “cricker”}).explain()
以上命令执行结果中会显示“cursor”: “BtreeCursor tags_1”,则表示已经使用了索引
【索引子文档字段】
l 如果需要通过city、state、pincode字段来检索文档,由于这些字段是子文档的字段,需要对子文档建立索引,为子文档的是三个字段建立索引:
>db.users.ensureIndex({“address.city”:1, “address.state”: 1, “address.pincode” : 1 })
l 创建索引后,使用子文档的字段来检索数据
>db.users.find({“address.city” : “ Los Angeles”})
l 查询表达式必须遵循指定索引的顺序,上面创建的索引将支持如下查询:
>db.users.find({“address.city”: “Los Angeles”,”address.state”: “California”})
l 也支持如下查询
>db.users.find({“address.city”: “Los Angeles”,”address.state”: “California,”address.pincode”:“123”})
二十三、 MongoDB索引限制
l 每个索引占用一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。所以,如果很少对集合进行读取操作,建议不使用索引。
l 索引不能被以下的查询使用:
(1) 正则表达式及非操作符,如$nin,$not等
(2) 算术运算符,如$mod
(3) $where子句
l 从2.6版本开始,如果现有的索引字段的值超过索引键的限制,MongoDB中不会创建索引
l 最大范围
(1) 集合中索引不能超过64个
(2) 索引名的长度不能超过125个字符
(3) 一个复合索引最多可以有31个字段
二十四、 MongoDB Objectid
Objectid是一个12字节BSON类型数据,有如下格式:
l 前4个字节表示时间戳
l 接下来的3个字节是机器识别码
l 紧接的两个字节由进程id组成(PID)
l 最后三个字节是随机数
MongoDB中存储的文档必须有一个”_id”键。这个键的值可以是任何类型的,默认是个ObjectId对象。
在一个集合里面,每个文档都有唯一的”_id”值,来确保集合里面每个文档都能被唯一标识。
1. 创建新的ObjectId
l 生成新的ObjectId
>newObjectId=ObjectId()
l 使用生成的ObjectId取代MongoDB自动生成的ObjectId
>myObjectId=ObjectId(“5349b4ddd2781d08c09890f4”)
2. 创建文档的时间戳
ObjectId中存储了4个字节的时间戳,可通过getTimestamp函数来获取文档的创建时间
>ObjectId(“5349b4ddd2781d08c09890f4”).getTimestamp()
3. ObjectId转换为字符串
将ObjectId转换为字符串格式: >new ObjectId().str
二十五、 MongoDB Map Reduce
Map-Reduce是一种计算模型,就是将大批量的工作分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。
【MapReduce命令】
MapReduce命令语法:
>db.collection.mapReduce(
function() {emit (key,value);}, //map函数
function(key,values) {return reduceFunction}, //reduce函数
{
out:collection,
query: document,
sort: document,
limit: number
}
}
使用MapReduce要实现两个函数Map函数和Reduce函数,Map函数调用emit(key.value), 遍历collection中所有的记录,将key与value传递给Reduce函数进行处理。
Map函数必须调用emit(key,value)返回键值对。
参数说明:
l Map: 映射函数(生成键值对序列,作为reduce函数参数)
l Reduce统计函数,reduce函数的任务就是将key-values变成key-value,就是把values数组变成一个单一的值value
l out 统计结果存放集合(不指定则使用临时集合,在客户端断开后自动删除)
l query 一个筛选条件,只有满足条件的文档才会调用map函数,(query,limit,sort可以随意组合)
l sort 和limit结合的sort排序参数(也是在发往map函数前给文档排序),可以优化分组机制
l limit 发往map函数的文档数量的上限(要是没有limit,单独使用sort的用处不大)
【使用MapReduce】
以下文档存储了用户的user_name和文章的status字段:
>db.posts.insert({
"post_text": "菜鸟教程,最全的技术文档。",
"user_name": "mark",
"status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "菜鸟教程,最全的技术文档。",
"user_name": "mark",
"status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "菜鸟教程,最全的技术文档。",
"user_name": "mark",
"status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "菜鸟教程,最全的技术文档。",
"user_name": "mark",
"status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "菜鸟教程,最全的技术文档。",
"user_name": "mark",
"status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "菜鸟教程,最全的技术文档。",
"user_name": "runoob",
"status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "菜鸟教程,最全的技术文档。",
"user_name": "runoob",
"status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "菜鸟教程,最全的技术文档。",
"user_name": "runoob",
"status":"active"
})
WriteResult({ "nInserted" : 1 })
现在,我们将在 posts 集合中使用 mapReduce 函数来选取已发布的文章(status:"active"),并通过user_name分组,计算每个用户的文章数:
>db.posts.mapReduce(
function() { emit(this.user_name,1); },
function(key, values) {return Array.sum(values)},
{
query:{status:"active"},
out:"post_total"
}
)
以上 mapReduce 输出结果为:
{
"result" : "post_total",
"timeMillis" : 23,
"counts" : {
"input" : 5,
"emit" : 5,
"reduce" : 1,
"output" : 2
},
"ok" : 1
}
结果表明,共有4个符合查询条件(status: “active”)的文档,在map函数中生成了4个键值对文档,最后使用reduce函数将相同的键值分为两组。
具体参数说明:
l result :存储结果的collection的名字,这是个临时集合,MapReduce的连接关闭后自动就被删除了
l timeMillis : 执行花费的时间,毫秒为单位
l input : 满足条件被发送到map函数的文档个数
l emit : 在map函数中emit被调用的次数,也就是所有集合中的数据总量
l output : 结果集合中的文档个数
l ok : 是否成功,成功为1
l err : 如果失败,会显示失败原因
使用find操作符来查看mapreduce的查询结果:
db.posts.mapReduce(
function() { emit(this.user_name,1); },
function(key, values) {return Array.sum(values)},
{
query:{status:"active"},
out:"post_total"
}
).find()
查询结果如下,两个用户tom和mark有两个发布的文章:
{ "_id" : "mark","value" : 4 }
{ "_id" : "runoob","value" : 1 }
二十六、 MongoDB 全文检索
全文检索对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查询,并将查找的结果反馈给用户的检索方式。
【启用全文检索】
MongoDB2.6版本以后默认开启全文检索。而之前的版本需要手动开启全文检索:
>db.adminCommand({setParameter:true,textSearchEnabled: true})
或者
Mongod –setParametertextSearchEnabled=true
【创建全文索引】
例如:集合posts
{
"post_text": "enjoy the mongodb articles onw3cschool.cc",
"tags": [
"mongodb",
"w3cschool"
]
}
对post_text字段建立全文索引,来搜索文章内的内容:
>db.posts.ensureIndex({post_text:“text”})
【使用全文索引】
搜索文章中关键字w3cschool.cc :
>db.posts.find( {$text: {$search: “w3cschool.cc”}})
【删除全文索引】
删除已存在的全文索引,可使用find命令查找索引名:
>db.posts.getIndexes()
执行如下命令删除索引:
>db.posts.dropIndex(“post_text_text”)
二十七、 MongoDB 正则表达式
正则表达式是使用单个字符串来描述,匹配一系列符合某个句法规则的字符串。
MongoDB使用$regex操作符来设置匹配字符串的正则表达式。
MongoDB使用PCRE作为正则表达式的语言。
【使用正则表达式】
使用正则表达式查找包含w3cschool.cc字符串的文章:
>db.posts.find({post_text: {$regex: “w3cschool.cc”}})
或者
>db.posts.find({post_text:/w3cschool.cc/})
【不区分大小写的正则表达式】
如果检索需要区分大小写,可设置$option为$i
查找不区分大小写的字符串w3cschool.cc:
>db.posts.find({post_text: {$regex: “w3cschool.cc”, $option: “$i”}})
【数组元素使用正则表达式】
如果需要查找包含以tutorial开头的标签数据,可以使用:
>db.post.find( {tags:{$regex: “tutorial”}})
【优化正则表达式查询】
l 如果文档中的字段设置了索引,那么使用索引相比于正则表达式查找所有的数据查询速度更快
l 如果正则表达式是前缀表达式,所有匹配的数据将以指定的前缀字符串为开始。
正则表达式中使用变量。一定要使用eval将组合的字符串进行转换,不能直接将字符串拼接后传入给表达式。否则没有报错信息,只是结果为空。例如:
Var name=eval(“/” +变量值key + “/i”);
二十八、 MongoDB GridFS
GridFS 用于存储和恢复那些超过16M的文件
GridFS 也是文件的一种存储方式,但它是存储在MongoDB的集合中
GridFS 可以更好的存储大于16M的文件
GridFS 会将大文件对象分割成多个小的chunk,一般为256k/个,每个chunk将作为MongoDB的一个文档被存储在chunk集合中
GridFS 用两个集合来存储一个文件:fs.files与fs.chunks
每个文件的实际内容被存在chunks中,和文件有关的meta数据存在files集合中
简单的fs.files集合文档:
{
"filename":"test.txt",
"chunkSize":NumberInt(261120),
"uploadDate":ISODate("2014-04-13T11:32:33.557Z"),
"md5":"7b762939321e146569b07f72c62cca4f",
"length":NumberInt(646)
}
简单的fs.chunks集合文档:
{
"files_id":ObjectId("534a75d19f54bfec8a2fe44b"),
"n": NumberInt(0),
"data": "MongoBinary Data"
}
【GridFS添加文件】
使用GridFS的put命令来存储mp3文件.
>mongofiles.exe –dgridfs put song.mp3
GridFS是存储文件的数据名称。如果不存在该数据库,MongoDB会自动创建。使用如下命令查看数据库中文件的文档:
>db.fs.files.find()
二十九、 MongoDB 固定集合
MongoDB固定集合是性能出色有着固定大小的集合。类似于环形队列。当集合空间用完后,再插入的元素就会覆盖最初始的头部的元素。
【创建固定集合】
通过createCollection来创建一个固定集合,且capped选项设置为true.
>db.createCollection(“cappedLogCollection”, {capped: true, size: 10000})
指定文档个数,加上max:1000属性:
>db.createCollection(“cappedLogCollection”,{capped:true,size: 10000,max: 1000})
判断集合是否为固定集合:
>db.cappedLogCollection.isCapped()
将已存在的集合转换为固定集合:
>db.runCommand({“convertToCapped”: “posts”,size:10000})
【固定集合查询】
固定集合文档按照插入顺序存储的,默认情况下查询就是按照插入顺序返回的,可以使用$natural调整返回顺序:
>db.cappedLogCollection.find().sort({$natural: -1})
【固定集合的功能特点】
可以插入及更新,但更新不能超出collection的大小,否则更新失败,不允许删除。但是可以调用drop()删除集合中的所有行,但是drop后需要显式重建集合。
64位上的机器只受系统文件大小的限制。
【固定集合属性及用法】
1. 属性
l 对固定集合进行插入速度极快
l 按照插入顺序的查询输出速度极快
l 能够在插入最新数据时,淘汰最早的数据
2. 用法
l 存储日志信息
l 缓存一些少量的文档
三十、 MongoDB 自动增长
MongoDB的_id是系统自动生成的12字节唯一标识。
【使用counters集合】
希望实现_id字段实现从1,2,3,4到n的自动增长功能。
{
"_id":1,
"product_name": "Apple iPhone",
"category": "mobiles"
}
为此,创建counters集合,序列字段值可以实现自动长:
>db.createCollection(“counters”)
向counters集合中插入以下文档,使用productid作为key:
{
"_id":"productid",
"sequence_value": 0
}
Sequence_value字段是序列通过自动增长后的一个值。
使用以下命令插入counters集合的序列文档中:
>db.counters.insert({_id: “productid”,sequence_Value: 0})