mongoDB读书笔记

MongoDB

这几天编写程序,发现如果没有理论的支持,即使时间花的再多,效率也是不高的,所以每天在编程之前都应该先给自己充一下电。这次重新看了mongoDB权威开发指南的前四章,做了下面的读书笔记。

  • mongoDB 是面向文档的数据库,不是关系型数据库;
  • 文档是MongoDb中数据的基本单元;
  • 每一个文档都有一个特殊的键"_id",在文档所处的集合中是唯一的。可以作为文档的唯一标识符

文档

文档是MongoDB的核心概念。包括多个键和关联的值,有序地存放在一起;

{"greeting": "Hello world!"};
  • 键: greeting;
  • 值: hello world;

多个键值对:

{
    "greeting": "hello world",  //字符串
    "foo": "2"  //整形
}

//另外的一个键值对
{
    "foo": "2",
    "greeting": "hello world"
}

文档中的键/值对必须是有序的,上面两个是不同的键值对

字符串作为键的要求:

  1. 键不能含有\0空字符,这个字符用来保存键的结尾
  2. .$有特别的含义,也不可以
  3. _开头的键是保留的
  4. MongoDB 区分类型,也区分大小写,不能有重复的键

集合是多个键值对的集合


mongoDB的使用

  • mongoDB在没有参数的情况下会默认数据目录为/data/db
  • 默认情况下,mongoDB监听27017端口
  • mongod还会启动一个基本的http服务器,监听28017端口
  • 可以通过浏览器访问http://localhost:28017来获取数据库的管理信息
  • shell下可以输入ctrl+C来停止Mongd的运行

MongoDB中存储的文档必须有一个_id值。

  • 这个值可以是任意类型的,默认是ObjectId对象。
  • 在一个集合里面,每一个文档都有一个唯一的_id值,来确保集合里面的每一个文档都能被唯一标识
  • ObjectID_id的默认类型
  • 在插入文档的时候没有_id,系统会自动帮你创建一个。

强大的shell操作

  • insert添加一个文档到集合里面
db.数据库名.insert(自己定义好的一条数据)
  • find查找数据库,返回集合里面的所有文档
db.数据库名.find()
  • findOne(),返回数据库里面的一个文档
db.数据库名.findOne()
  • update()更改数据库,接受至少两个参数,一个是更新的文档限定条件,一个是新的文档
db.blog.update({title:"my blog"}, post)
  • remove()删除,没有参数会删除所有的文档,一般接受一个条件
db.blog.remove({title:"my blog"})
  • 移除blog中所有opt-out为true的人
db.blog.remove({"opt-out": true});
  • 删除数据是永久性的,不能撤销也不能恢复

更改器的使用

  • $inc 修改器增加pageviews的值
//将name为1对应的文档中的pageview增加1
db.analytics.update({"name":"1"}, {"$inc": {"pageview": 1}});

注意:使用修改器不能修改_id的值。

  • $set修改器用来指定一个键的值,如果这个键不存在,那么就创建它;
//users表中username为why的文档中的favoriteBook设置为c
db.users.update({"username": "why"}, {"$set": {"favoriteBook": "c"}});
  • $set甚至可以修改键的数据类型, $set还可以修改内嵌的属性
//将favoirteBook的键值设置为一个数组
db.users.update({"username": "why"},{"$set": {"favoriteBook": ["c","c++"]}});
  • $unset可以完全删除字段
db.users.update({"username": "why"},{"$unset": {"favoriteBook": "c"}});
  • $inc可以累计一个属性,如果不存在,那么会事先创建一个新的属性
//可以自己给它创建一个score: 50的属性
{"$inc": {"score": 50}}
//score+1
{"$inc": {"score": 1}}
//结果将变为score: 51;

$inc只能用来修改数字,如果想要改变其他类型的值,可以选择用$set


数组的操作

  • $push给已有的数组末尾添加一个元素,
  • 要是没有这个数组,会自动创建一个新的数组
  • 继续添加元素,只需要再次使用$push
  • 如果一个值不再数组里面,那么先用$ne来创建一个新的字段,再把它push到这个数组里面
db.user.update({"username": "{"$ne": "WHY"}"}, {$push:{"username": "WHY"}})
  • 如果$ne行不通,可以直接使用$addToSet,这样还可以避免重复
db.users.update({"username":"why"}, 
   {"$addToSet": {"emails":"qq.mail"}}
);

db.users.update({"username":"why"},
   {"$addToSet": {"emails":{"qq.mail","126.com"}} }
);
  • 将数组作为队列或者是栈,可以使用$pop这个修改器

  • 从数组的任何一端删除元素

  • {$pop: {key: 1}} 从数组末尾删除一个元素

  • {$pop: {key: -1}} 从数组头部删除

  • $pull 会将数组中匹配的部分删除掉

db.lists.insert({"todo": {"dishes": "dishes" ,"laundry","dry cleaning"}})

db.lists.update({}, {"$pull", "{"todo":"laundry"}");

db.list.find()
{
    "_id": ObjectId("XXXX"),
    "todo" : {
      "dishes",
      "dry cleaning"
    }
}

对于数组[1,1,2,1]执行pull 1 ,那么他会删掉重复的字段

  • 定位符$
//将原先author为tom的字段修改为why
db.blog.update({"comments.author": "tom"},
               {"$set": {"comments.$.author": "why"}});
  • $定位符之id那个匹配第一个匹配的元素。所以如果有多个评论人为tom的字段,只会修改第一个匹配的字段

  • $upsert

db.math.remove()
db.math.upsert({"count": 25}, {"$inc": {"count": 3}}, true);
db.math.findOne() {
    "id": ObjetcId(XXX);
    "count": 28
}

先清空了集合,然后里面就没有文档,
再用upsert创建一个count的值为25的文档
然后将这个值加3,最后得到count为28的文档。
如果没有开启upsert的选项,{"count" : 25}不会匹配到任何的文档,就不会有修改
再次运行,由于没有{"count": 25}的选项,那么他会再次创建一个count为25的字段,
然后再次+3为28

  • $save是保存
db.users.save();

更新多个文档

  • 默认情况下,更新只能对符合匹配条件的第一个文档执行操作。
  • 要是有多个文档符合条件,其余的文档就没有变化。
  • 要使得匹配到的文档都得到更新,那么可以设置update的第四个参数为true
db.users.update({"birthday": "10/13/2016"},
{$set: {gift: {"happy birthday"}}, false, true});

如果想知道文档到底更新了多少,可以运行getLastError命令

db.runCommand({getLastError: 1});

{
    "err": null,
    "updateExisting": true,
    "n": 5,
    "ok": true
}
  • 这里的n=5就说明有5个文档被更新了。
  • updateExisting: true说明是对已有的文档进行更新

getLastError只能获取更新的信息,不能返回已经更新的文档,

我们可以通过findAndModify获取更新好的文档,缺点是有点慢,需要等待数据库的响应

db.runCommand({
    "findAndModify": "processes",
    "query": {},
    "sort": {},
    "update": {}
})
  • findAndModify: 字符串,集合的名字
  • query: 查询文档,用来查询文档的条件
  • sort: 排序的条件
  • update: 修改器文档,对所有找到的文档执行更新
  • remove:布尔类型,表示是否删除文档
  • new: 布尔类型,表示返回的是更新前的文档还是更新后的文档,默认是更新前的文档。
  • update和remove必须有一个,也只能有一个,如果匹配不到文档,那么则这个命令会返回一个错误
  • 一次只能处理一个文档,也不能执行upsert操作,只能更新已有的文档
  • 对于普通的更新来说,findAndModify速度比较慢,时间相当一次查找,一次更新和一次getLastError

查询

  • find查询,查询返回一个集合中文档的子集,
    1. 子集的范围是从0个文档到整个集合
    2. find的第一个参数决定要返回哪些文档,其形式也是一个文档,说明要查询的细节
    3. 空的查询文档, 会返回集合的全部内容,如果不指定查询文档。默认就是空。
/这样会返回集合c中的全部内容
db.users.find{}
  • 当向查询文档中添加键值对时,就以限定了查找的条件
    查找方式是:1. 整数匹配整数,2. 布尔值匹配布尔值, 3. 字符串匹配字符串。
//查询所有年龄为27岁的用户
db.users.find({"age": 27});
//查询username为joe的字段
db.users.find({"username": "joe"});
//这样是多字段查询,会返回username为joe,年龄为27的所以字段
db.users.find({"username": "joe", "age":27})

指定返回的键

  • 有时不需要将文档中的所有键值对全部返回
  • 可以通过find()或者findOne()的第二个参数来指定想要的键
  • 这样可以节省传输的数据量,也可以节省客户端解码文档的时间和内存消耗
db.users.find({}, {"username": 1, "email": 1})
  • 返回找到字段中的usernameemail
  • 还有一个是_id,这个键总是被返回,即使没有指定_id显示也是一样
  • 也可以通过第二个参数来剔除查询结果中的某个键值对
db.users.find({}, {"password": 0});`

这样返回的字段中就不会出现password这个键值对

查询条件

  • $lt,$lte,$gt,$gte,是全部的比较操作符,分别对应<, <=,>, >=
  • 可以将它们组合起来查询一个范围的值
//查询年龄是18-30岁(含)的所有用户
db.users.find({"age": {"$gte": 18, "$lte": 30}})
//可以查询在现在这个时间之前注册过的用户
start = new Date();
db.users.find({"registerDate": {"$lt": start}})
  • $ne表示不等
//找到名字不是joe的用户
db.users.find({"username": {"$ne":"joe"}})

$ne可以用于所有类型的数据

OR查询

mongoDB有两种方式进行OR查询,$in可以查询一个键的多个值,
$or可以用来完成多个键值对的任意给定值(更加通用)

db.users.find({"username": {"$in":["why","joe"]}})

这回匹配usernamewhy的文档,也会匹配usernamejoe的文档

如果$in中对于的数组只有一个值,那么这和直接匹配这个值得效果是一样的

{ticket_no: {$in:[125]}}和{ticket_no: 125}是一样的

与$in相反的是$nin,将返回与数组中所有条件都不匹配的文档

db.users.find({"username": {"$nin":["why","joe"]}})

返回username既不是why,也不是joe的user

$in只能对单个键做OR查询,而$or可以查询包含所有可能条件的参数作为数组

db.raffie.find({"$or": [{"ticket_no": 125}, {"winner": true}]})

这样会返回ticket_no"是125,winner是true的所有字段

$or还可以含有其他条件语句

db.raffie.find({"$or": [
   {"ticket_no": ["$in":[123,124,125]]},
   {"winner": true}
]})

条件句的规则

在查询中,$lt在内层文档,而更新中$inc是外层文档的键
条件句是内层文档的键,而修改器是外层文档的键
一个键可以有多个条件,但是一个键不能对于多个更新更改器

//正确
db.users.find({"age": {"lt": 30, "$gt": 20})
//错误
db.user.find({"$inc": {"age":1}, "$set": {age:40}})

null比较特殊,不仅仅匹配自身,而且还匹配不存在,所以我们在匹配键值为null的文档的同时,还要检查该建筑是否存在

db.c.find({"z": {"$in":[null], "$exists": true}});

没有$eq操作符,我们使用$in操作符代替

正则表达式

匹配名为Joe或者joe的用户,可以用正则表达式匹配大小写

db.users.find({"name":/joe/i})

正则表达式还可以插入到数据库,自身也可以匹配

db.foo.insert({"bar": /baba/})
db.foo.find("bar": /baba/)
{
    "_id": ObjectId("XXXXX"),
    "bar": /baba/
}

查询数组

  • 插入一个数组
db.food.insert({"fruit":["apple", "banana"]})
db.food.find({"fruit":"banana"})
//找得到,但是比较低效
  • $all 如果需要多个元素来匹配数组,那么就需要用到$all,这样会匹配一组元素
db.find({"fruit": {$all: ["apple"]})
  • $size 可以用来查询指定长度的数组
db.food.({"fruit": {"$size":3}})
  • $slice find的第二个参数是可选的,可以返回那些键,"$slice"返回的是数组的一个子集合
//返回的是前10条评论
db.blog.posts.findOne(criteria, {"comments": {"$slice": 10}})

//-10表示的是后10条评论
db.blog.posts.findOne(criteria, {"comments": {"$slice": -10}})

//这个操作会跳过前面的前23个元素,返回第24个到第33个元素。
//如果数组不够33个元素,那么会返回第23个元素后面的全部元素
db.blog.posts.findOne(criteria, {"comments": {"$slice": [23,10]}})
  • limit()限制查询的结果数量
//只返回3个结果
如果返回的结果不足3个,那么返回匹配数量的结果。limit是上限而不是下线
db.c.find().limit(3);

//skip与limit类似
db.c.find().skip(3)
  • sort是用一个对象作为参数:
    一组键值对,键对应文档的键名,值代表查询的方向,排序方向可以是1升序-1降序 。 如果指定了多个键,那么按照键的顺序逐个进行进行排序
db.c.find().sort({username: 1, age: -1})

简单的分页, 按照date的降序显示文档

var page1 = db.foo.find(cirterial).limit(100)
var latest = null;
while(page1.hasNext()) {
    latest = page1.next();
    display(latest);
}
//get next page
var page2 = db.foo.find({"date": {"$gt": latest.date}});
page2.sort({"date": -1}).limit(100);

唯一索引

db.people.ensureIndex({"username": 1}, {"unique": true});

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,063评论 6 510
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,805评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,403评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,110评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,130评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,877评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,533评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,429评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,947评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,078评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,204评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,894评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,546评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,086评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,195评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,519评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,198评论 2 357

推荐阅读更多精彩内容