mongodb是面向文档的数据库,没有模式,文档的键不会事先定义,也不会固定不变。由于没有模式需要更该,通常不需要迁移大量的数据。
1、容易扩展
2、丰富的功能
- 索引
- 存储javascript
- 聚合
- 固定集合
- 文件存储
3、不牺牲速度
- 使用MongoDB传输协议
- 预分配数据文件
- 默认的存储引擎中使用了内存映射
- 动态查询优化器会“记住”执行查询最高效的方式
-尽可能将服务器端处理逻辑交由客户端
4、简便的管理
- 尽量让服务器自治来简化数据库的管理
启动mongodb
mongod -dbpath dbsource
启动shell
mongo
shell是功能完备的JavaScript解释器,可以运行任何JavaScript程序
MongoDB客户端
shell是一个独立的MongoDB客户端,开启的时候,shell会连到MongoDB服务器的test数据库,并将这个数据库连接赋值给全局变量db。这个变量是通过shell访问MongoDB的主要入口点。
shell还有些非JavaScript语法的扩展
> use local
switched to db local
shell的4个基本操作(CRUD)
创建
> db.shangquan.insert({"content": "fadffada" })
WriteResult({ "nInserted" : 1 })
读取
db.shangquan.findOne()
更新
> db.shangquan.update({"aa": "cc"},{"conment": []})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
用新版本的文档替换原来的文档
删除
> db.shangquan.remove({"content":"fadffada"})
WriteResult({
"nRemoved" : 0,
"writeError" : {
"code" : 20,
"errmsg" : "cannot remove from a capped collection: pxh.shangquan"
}
})
shell的帮助命令
> help
db.help() help on db methods
db.mycoll.help() help on collection methods
sh.help() sharding helpers
rs.help() replica set helpers
help admin administrative help
help connect connecting to a db help
help keys key shortcuts
help misc misc things to know
help mr mapreduce
show dbs show database names
show collections show collections in current database
show users show users in current database
show profile show most recent system.profile entries with time >= 1ms
show logs show the accessible logger names
show log [name] prints out the last segment of log in memory, 'global' is default
use set current database
db.foo.find() list objects in collection foo
db.foo.find( { a : 1 } ) list objects in foo where a == 1
it result of the last line evaluated; use to further iterate
DBQuery.shellBatchSize = x set default number of items to display on shell
exit quit the mongo shell
MongoDB的文档近似于JSON,在概念上和JavaScript中的对象神似,JSON只有null、布尔、数字、字符串、数组和对象几种类型。MongoDB在保留JSON基本的键/值对特性的基础上,添加了其他一些数据类型。
{
"_id": ObjectId("4c0beecfd096"),
"myInteger": {
"floatApprox": 3
}
}
插入的64位整数不能精确的作为双精度显示, shell会添加两个键,”top”和”bottom”,分别表示高32位和低
32位
- 64位浮点数,shell中的数字都是这种类型。{“x”: 3.14}
- 字符串 {“x”: “foobar”}
- 符号, shell将数据库里的符号转换成字符串。
- 对象id, 文档的12字节的唯一ID {“x”: ObjectId()}
- 日期 存储从标准纪元开始的毫秒数 {“x”: new Date()}
- 正则表达式 采用JavaScript的正则表达式语法 {“x”: /foobar/i}
- 代码 文档中可以包含JavaScript代码:{“x”: function() {/……………../}}
- 二进制数据 可以由任意字节的串组成,在shell中无法使用
- 最大值 BSON包含一个特殊类型,表示可能的最大值
- 最小值 BSON包含一个特殊类型,表示可能的最小值
- 未定义 {“x” : undefined}
- 数组 {“x”: [“a”, “b”, “c”]}
- 内嵌文档 {“x”: {“foo”: “bar”}}
MongoDB中存储的文档必须有个”_id”键。这个键可以是任何类型,默认是一个ObjectId对象。在一个集合里,每个文档都有唯一的”_id”值,来确保集合里面的每个文档都能被唯一标识。
ObjectId使用12字节的存储空间,每个字节是两位十六进制数字,是一个24位的字符串。
ObjectId的组成: 前4个字节是从标准纪元开始的时间戳,接下来的三个字节是所在主机的唯一标识,接下来的两个字节是产生ObjectId的进程标识符,最后三个字节是计数器;
插入并保存文档
MongoDb插入原理:使用驱动程序进行插入的时候,会将数据转换成BSON格式。数据库会解析BSON,并检验是否含有“_id”键,因为“_id”键在插入到数据库时MongoDb会自动生成。而且每次插入文档不能超过4M。这个应该是和MongoDb本身有关。
public void save(List shangquan_infos) {
mongoTemplate.save(shangquan_infos);
}
删除文档
> db.shangquan.remove({})
WriteResult({
"nRemoved" : 0,
"writeError" : {
"code" : 20,
"errmsg" : "cannot remove from a capped collection: pxh.shangquan"
}
})
更新文档
文档存入数据库以后,可以用update方法来修改它。update有两个参数,一个查询文档,一个修改器文档。更新操作是原子的,若是两个更新同时发生,先到的服务器先执行,接着执行另外一个。所以相互有冲突的更新可以火速传递,但不会相互干扰,最后的更新会取得”胜利“,
文档替换
db.trans.update({"money" : -1}, {"aa": [1, 3, "c"]})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
使用修改器
通常文档只会有一部分需要更新,利用原子的更新修改器,可以使得这种部分更新极为高效。跟新修改器是一种特殊的键,用来指定复杂的更新操作,比如调整、增加或者删除键。
> db.trans.update({"money" : -1}, {"$inc": {"money": 1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
“$inc”只能用于整数、长整数或双精度浮点数。
使用修改器的时候,”_id”的值不能改变。
“$set” 用来指定一个键的值,如果这个键不存在,则创建它。
> db.trans.update({"money" : -1}, {"$set": {"address": "strett"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.trans.update({"money" : -1}, {"$unset": {"address": "strett"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
“$push” 可用于操作数组,若数组不存在,可自动创建;
> db.trans.update({"money" : -1}, {"$push": {"address": {"city": "NewYork", "street": "yanggaonanlu"}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
经常有这种境况,如果一个值不在数组里面旧把它加进去。可用”$ne”实现
> db.trans.update({"address" : {"$ne": {"address": "Beijing"}}}, {"$push": {"address": {"city": "Beijing", "street": "yanggaonanlu"}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
“$addToSet” 可以完成同样的事,可以避免重复
> db.trans.update({ "_id" : ObjectId("564193d2e4b09a79f9d08b48")},{"$addToSet": {"address": {"city": "hannan"}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
将” addToSet"和" each”组合起来,可以添加多个不同的值;
> db.trans.update({ "_id" : ObjectId("564193d2e4b09a79f9d08b48")},{"$addToSet": {"address":{"$each": [{"city": "zhengzhou"}, {"city": "hangzhou"}]} }})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
“$pop” 可以从数组任何一端删除元素。
> db.trans.update({ "_id" : ObjectId("564193d2e4b09a79f9d08b48")}, {"$pop": {"address": 1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
{pop: {key: 1}}从数组末尾删除一个元素,{pop: {key: -1}}从数组头部删除
“ pull"可以基于特定的条件来删除数据,而不仅仅是位置," pull”会将所有匹配的部分删除掉
> db.trans.update({ "_id" : ObjectId("564193d2e4b09a79f9d08b48")},{"$pull": {"address": {"city": "hannan"}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
对数组中的一部分值进行操作,可以通过位置或定位符操作。单测试用定位符报错,但数据库中文档已更新。
> db.trans.update({"_id" : ObjectId("564193d2e4b09a79f9d08b48")}, {"$set": {"address.0.street": "madeng"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.trans.update({"_id" : ObjectId("564193d2e4b09a79f9d08b48")}, {"$set": {"address.$.street": "madeng"}})
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 16837,
"errmsg" : "The positional operator did not find the match needed from the query. Unexpanded update: address.$.street"
}
})
upsert是一种特殊的更新,要是没有文档符合更新条件,就会以这个条件和更新文档为基础创建一个新的文档。若找到匹配的文档,则正常更新。
> db.trans.update({"_id" : ObjectId("564193d2e4b09a79f9d08b48")}, {"$inc": {"card_num": 4}}, true)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
save是一个shell函数,可以在文档不存在时插入,存在时更新
当保存已存在的文档时报错,但查看数据库已更新。
> var card1 = db.trans.find({ "_id" : ObjectId("564193d2e4b09a79f9d08b48")})
> card1.num = 20
20
> db.trans.save(card1)
2016-08-04T20:50:48.519+0800 E QUERY Error: can't save a DBQuery object
at Error ()
at DBCollection._validateObject (src/mongo/shell/collection.js:145:15)
at DBCollection._validateForStorage (src/mongo/shell/collection.js:154:10)
at insert (src/mongo/shell/bulk_api.js:646:20)
at DBCollection.insert (src/mongo/shell/collection.js:243:18)
at DBCollection.save (src/mongo/shell/collection.js:493:21)
at (shell):1:10 at src/mongo/shell/collection.js:145
> db.trans.save({"aa","cc"})
2016-08-04T20:51:48.784+0800 E QUERY SyntaxError: Unexpected token ,
> db.trans.save({"aa":"cc"})
WriteResult({ "nInserted" : 1 })
> db.trans.find({ "_id" : ObjectId("564193d2e4b09a79f9d08b48")})
更新多个文档
默认情况下,更新只能对符合匹配条件的第一个文档执行操作。要是多个文档符合条件,其余的文档就没有变化。要使所有匹配到的文档都得到更新,可以设置update的第4个参数为true。
> db.trans.update({"money": -1}, {"$inc": {"money": 34}},true,true)
WriteResult({ "nMatched" : 81, "nUpserted" : 0, "nModified" : 81 })
返回已更新的文档
用getLastError仅能获得有限的信息,并不能返回已更新的文档。这个可以通过findAndModify命令来做到。对于操作查询以及其他需要取值和赋值风格的原子性操作十分方便。
> db.runCommand({"findAndModify": "trans","query":{"card1" : "489592"}, "update": {"$set":{"status": "RUNNING"}}}
... )
> db.trans.findAndModify({"query":{"card1" : "622150"},"update": {"$set":{"status": "RUNNING"}}})
这个命令有些限制,它一次只能处理一个文档,也不能执行upsert操作。
数据库会为每一个MongoDB数据库连接创建一个队列,存放这个连接的请求。当客户端发送一个请求,会被放到队列的末尾,只有队列中的请求都执行完毕,后续请求才会执行。单个连接可以了解整个数据库,总能读到自己写的东西。每个连接都有独立的队列,要是打开两个shell,就有两个数据库连接,在一个shell中执行插入,之后在另一个shell中查询不一定能得到插入的文档。在同一个shell中,插入后再进行查询是一定能查到的。
MongDB中使用find来进行查询。
find的第一个参数决定了要返回那些文档。
> db.trans.find({"card1" : "489592"})
指定返回的键
> db.trans.find({"card1" : "489592"}, {"money": 1})
“_id”这个键总是被返回,即便没有指定也一样。
若不希望结果含有某个键,可以指定为0
> db.trans.find({"card1" : "489592"}, {"card2":1, "_id": 0})
查询条件
“ lt"、" lte”、” gt"、" gte”等比较操作符
> db.trans.find({"money": {"$lt": 500}})
“ or"、" in” 查询
“ in”可以用来查询一个键的多个值“ or”更通用一些,用来完成多个键值的任意给定值
“$nin” 返回与数组中都不匹配的文档
> db.trans.find({"money": {"$in": [100, 2517]}})
> db.trans.find({"$or": [{"money": 2517},{"card2" : "8161"}]})
> db.trans.find({"money": {"$nin": [100, 2517]}})
“ not”是元条件语句,即可以用在任何其他条件之上。用来查找与特定模式不符的文档。“ mod"是取模运算符,"$mod”会将查询的值除以第一个给定值,若余数等于第二个给定值则返回结果
> db.trans.find({"money": {"$mod": [3,1]}})
> db.trans.find({"money":{"$not": {"$mod": [3,1]}}})
可对一个键应用多个条件,但一个键不能对应多个修改器
> db.trans.find({money: {$lt : 300, $gt : 30}})
特定类型查询
null不仅仅匹配自身,而且匹配“不存在”的。所以,这种匹配会返回缺少这个键的所有文档。
> db.trans.find({money: null})
若想要仅仅匹配键值为null的文档,可以通过一下命令判断
> db.trans.find({money: {$in : [null], $exists: true}})
正则表达式能够灵活有效的匹配字符串,也可以匹配自身
> db.trans.find({card1: /485/i})
查询数组
数组绝大多数情况可以这样理解:每个元素都是整个键的值。
> db.trans.find({aa: 1})
{ "_id" : ObjectId("564193d2e4b09a79f9d08b49"), "aa" : [ 1, 3, "c" ] }
“$all”用来匹配数组的多个元素。
> db.trans.find({"aa": {"$all": ["c", 3]}})
{ "_id" : ObjectId("564193d2e4b09a79f9d08b49"), "aa" : [ 1, 3, "c" ] }
{ "_id" : ObjectId("57a4023aac1c6b4c5f658698"), "aa" : [ "c", 3, 5 ] }
完整的数组精确匹配, 不会匹配有缺少或者冗余元素的文档,也不会匹配数组顺序不一样的文档
“$size” 可以用其查询指定长度的数组
> db.trans.find({aa: {$size: 3}})
{ "_id" : ObjectId("564193d2e4b09a79f9d08b49"), "aa" : [ 1, 3, "c" ] }
{ "_id" : ObjectId("57a40216ac1c6b4c5f658697"), "aa" : [ 3, 5, "e" ] }
{ "_id" : ObjectId("57a4023aac1c6b4c5f658698"), "aa" : [ "c", 3, 5 ] }
“$slice”操作符,可以返回数组的一个子集合
返回集合的前面几个元素
> db.trans.find({aa: {$size: 3}},{"aa": {"$slice": 2}})
{ "_id" : ObjectId("564193d2e4b09a79f9d08b49"), "aa" : [ 1, 3 ] }
{ "_id" : ObjectId("57a40216ac1c6b4c5f658697"), "aa" : [ 3, 5 ] }
{ "_id" : ObjectId("57a4023aac1c6b4c5f658698"), "aa" : [ "c", 3 ] }
返回集合尾部几个元素
> db.trans.find({aa: {$size: 3}},{"aa": {"$slice": -2}})
{ "_id" : ObjectId("564193d2e4b09a79f9d08b49"), "aa" : [ 3, "c" ] }
{ "_id" : ObjectId("57a40216ac1c6b4c5f658697"), "aa" : [ 5, "e" ] }
{ "_id" : ObjectId("57a4023aac1c6b4c5f658698"), "aa" : [ 3, 5 ] }
返回中间几个元素,接受偏移值和要返回的元素数量。
> db.trans.find({aa: {$size: 3}},{"aa": {"$slice": [1,3]}})
{ "_id" : ObjectId("564193d2e4b09a79f9d08b49"), "aa" : [ 3, "c" ] }
{ "_id" : ObjectId("57a40216ac1c6b4c5f658697"), "aa" : [ 5, "e" ] }
{ "_id" : ObjectId("57a4023aac1c6b4c5f658698"), "aa" : [ 3, 5 ] }
查询内嵌文档
有两种方法查询内嵌文档,查询整个文档,或只针对其键值对进行查询。
> db.trans.find({comments: {author: "joe", score: 3, comment: "nice post"}})
{ "_id" : ObjectId("57a408f7ac1c6b4c5f658699"), "comments" : [ { "author" : "joe", "score" : 3, "comment" : "nice post" }, { "author" : "mary", "score" : 6, "comment" : "good" } ] }
当文档添加新的字段,查询将无效
可以用点表示法查询内嵌的键
> db.trans.find({"comments.author": "joe", "comments.score": 3})
{ "_id" : ObjectId("57a408f7ac1c6b4c5f658699"), "comments" : [ { "author" : "joe", "score" : 3, "comment" : "nice post" }, { "author" : "mary", "score" : 6, "comment" : "good" } ] }
上面的查询可能不能查到”author”是”joe” 并且 “score” 是3的文档,因为符合”author”是”joe”和score” 是3条件的可能不是同一条评论。
要正确的指定一组条件,可以使用”$elemMatch”.这种模糊的命名条件句能用来部分匹配数组中的单个内嵌文档的限定条件。正确的写法应该是这样:
> db.trans.find({"comments.author": "joe", "comments.score": 3})
{ "_id" : ObjectId("57a408f7ac1c6b4c5f658699"), "comments" : [ { "author" : "joe", "score" : 3, "comment" : "nice post" }, { "author" : "mary", "score" : 6, "comment" : "good" } ] }
{ "_id" : ObjectId("57a4289eac1c6b4c5f65869b"), "comments" : [ { "author" : "joe", "score" : 8, "comment" : "nice post" }, { "author" : "xiaoze", "score" : 3, "comment" : "nice post" } ] }
> db.trans.find({comments: {"$elemMatch": {author: "joe", score: 3}}})
{ "_id" : ObjectId("57a408f7ac1c6b4c5f658699"), "comments" : [ { "author" : "joe", "score" : 3, "comment" : "nice post" }, { "author" : "mary", "score" : 6, "comment" : "good" } ] }
$Where
使用$where字句,可以执行任意JavaScript作为查询的一部分。
比较一个文档中,两个字段的值
> db.trans.find({"$where" : function(){
... for(var current in this) {
... ... for (var other in this) {
... ... if(current != other && this[current] == this[other])
... ... return true;
... ... }
... ... }
... ... return false;
... ... }
... }
... )
{ "_id" : ObjectId("57a43135ac1c6b4c5f65869c"), "apple" : 8, "spinach" : 4, "watermelon" : 4 }
要避免使用 where查询,因为他们在速度上比常规查询慢很多,每个文档都要从BSON转换为JavaScript对象,然后通过" where”的表达式来运行,同样还不能利用索引。
游标
数据库通过游标来返回find的执行结果。客户端对游标的实现通常能够对最终结果进行有效的控制。
var cursor = db.trans.find()
> cursor.next()
{ "_id" : ObjectId("564193d2e4b09a79f9d08b49"), "aa" : [ 1, 3, "c" ] }
当调用find的时候,shell并不立即查询数据库,而是等待真正开始要求获得结果的时候才发送查询,这样在执行之前可以给查询附加额外的选项。当查询被发往服务器。shell立刻获得100个结果或者4MB数据(两者中较小者), 这样下次调用next或者hasnext时就不必跑到服务器上去了。客户端用光了第一组数据,shell会再一次联系数据库,并要求更多的结果。
> var cursor= db.trans.find().sort({money: 1}).skip(10).limit(34)
skip会略过前几个匹配的文档。
MongoDB处理不同类型的数据是有一个顺序的。
从小到大依次为:
分页查询
选取随机文档
高级查询选项
查询分为普通查询和包装两类:
普通查询:
> db.trans.find({comments: {"$elemMatch": {author: "joe", score: 3}}})
包装查询:
db.trans.find({author: "joe", score: 3}).sort({money: 1, card1: -1})
实际上不会将{author: “joe”, score: 3}作为查询直接发送到数据库,而是将查询包装在一个更大的文档。shell会将查询从{author: “joe”, score: 3}转换成
{“ query":author:"joe",score:3," orderby”:{money: 1, card1: -1}}
获取一致结果: 查询保存后,结果不一致。
解决方法: 对查询进行快照
在服务器端, 游标消耗内存和其他资源。游标遍历尽了以后,或者客户端发来消息要求终止,数据库会释放这些资源。释放的资源可以被数据库换做他用,这是非常有益的。
还有一些情况会导致游标终止,当游标在客户端已经不在作用域内,驱动会向服务器发送专门的消息,让其销毁游标。最后,即便用户也没有迭代完所有结果,并且游标也还在作用域内,10分钟不使用,数据库游标也自动销毁。