MongoDB权威指南—读书笔记(Part one)

MongoDB权威指南—读书笔记(Part One)

非常棒的官方网站!

MongoDB基础知识

  • 文档是MongoDB中数据的基本单元,非常类似于关系学数据库管理系统中的行,但更具表现力。
  • 类似地,集合(collection)可以看作是一个拥有动态模式(dynamic schema)的表。
  • MongoDB的一个实例可以拥有多个相互独立的数据库(database),每一个数据库都拥有自己的集合。
  • 每一个文档都由一个特殊的键”_id”,这个键在文档所属的集合中是唯一的。
  • MongoDB自带了一个简单但功能强大的JavaScript shell,可用于管理MongoDB的实例或数据操作。

文档

文档就是键值对的一个有序集。
例如:{"greeting":"Hello,world!","foo":3}
文档中的值可以是多种不同的数据类型(甚至可以是一个完整的内嵌文档)。

文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。
- 键不能含有\0(空字符)。这个字符用于表示键的结尾。
- .和$具有特殊意义,只能在特定环境下使用。通常,这两个字符是被保留的。

MongoDB不但区分类型,而且区分大小写,例:

下面两组均不相同:
{"foo":3}
{"foo":"3"}
/*-------------------------*/
{"foo":3}
{"Foo":3}

MongoDB的文档不能有重复的键。

集合

集合就是一组文档。

动态模式

集合是动态模式的。这意味着一个集合里面的文档可以是各式各样的。

“既然没有必要区分不同类型文档的模式,为什么还要使用多个集合呢?”:
- 如果把各种各样的文档不加区分地放在同一个集合里,无论对开发者还是对管理员来说都将是噩梦。
- 在一个集合里查询特定类型的文档在速度上页很不划算,分开查询多个集合要快得多。
- 把同种类型的文档放在一个集合里,数据会更加集中。
- 创建索引时,需要使用文档的附加结构(特别是创建唯一索引时)。索引是按照集合来定义的。在一个集合中只放入一种类型的文档,可以更有效地对集合进行索引。

命名

集合名可以是满足下列条件的任意UTF-8
- 集合名不能是空字符串(“”)。
- 集合名不能包含\0字符(空字符),这个字符表示集合名的结束。
- 集合名不能以”system.”开头,这是为系统集合保留的前缀。
- 用户创建的集合不能在集合名中包含保留字符”$”。引文某些系统生成的集合中包含$,很多驱动程序确实支持在集合名里包含该字符。除非你要访问这种系统创建的集合,否则不应该在集合名中包含$

子集合

组织集合的一种惯例是使用”.”分隔不同命名空间的集合。

数据库

在MongoDB中,多个文档组成集合,而多个集合可以组成数据库。一个MongoDB实例可以承载多个数据库,每个数据库拥有0个或者多个集合。每个数据库都由独立的权限,即便是在磁盘上,不同的数据库也放置在不同的文件中。按照经验,我们将有关一个应用程序的所有数据都存储在同一个数据库中。要想在同一个MongoDB服务器上存放多个应用程序或者用户的数据,就需要使用不同的数据库。

数据库通过名称来识别,数据库名可以是满足以下条件的任意UTF-8字符串:
- 不能是空字符串(“”)
- 不得含有/,\,.,",*,<,>,:,|,?,$ (一个空格),\0。基本上,只能使用ASCII中的字母和数字
- 数据库名区分大小写,即便是在不去放大小写的文件系统中也是如此。简单起见,数据库名应全部小写。
- 数据库名最多为64字节。

另外,有一些数据库名是保留的,可以直接访问这些有特殊语义的数据库。这些数据库如下所示。
- admin
从身份验证的角度来说,这是”root”数据库。如果将一个用户添加到admin数据库,这个用户将自动获得所有数据库的权限。再者,一些特殊的服务器端命令也只能从admin数据库运行,如列出所有数据库或关闭服务器。

  • local
  • config

启动MongoDB

MongoDB shell简介

这是JavaScript shell

运行shell

mongo

MongoDB客户端

启动时,shell会连到MongoDB服务器的test数据库,并将数据库连接赋值给全局变量db。

如果想要查看db当前指向哪个数据库,可以使用db命令:

> db
test

选择数据库:

> use foobar
switched to db foobar
shell中的基本操作
创建

insert函数可将一个文档添加到集合中。例:

{
    "title" : "My Blog Post",
    "content" : "Here's my blog post.",
    "date" : ISODate("2014-11-11T10:39:47.464Z")
}
> db.blog.insert(post)
WriteResult({ "nInserted" : 1 })

//结果
> db.blog.find()
{ "_id" : ObjectId("5461e78066f397ec9d6bf459"), "title" : "My Blog Post", "content" : "Here's my blog post.", "date" : ISODate("2014-11-11T10:39:47.464Z") }
读取

find和findOne方法可以用于查询集合中的文档。若只想查看一个文档,可用findOne:

> db.blog.findOne()
{
    "_id" : ObjectId("5461e78066f397ec9d6bf459"),
    "title" : "My Blog Post",
    "content" : "Here's my blog post.",
    "date" : ISODate("2014-11-11T10:39:47.464Z")
}

find和findOne可以接受一个查询文档作为限定条件。这样就可以查询滏阳河一定条件的文档。使用find时,shell会自动显示最多20个匹配的文档,也可获取更多文档。

更新

使用update。update接受(至少)两个参数:
1. 限定条件
2. 新的文档

> db.blog.update({title:"My Blog Post"},post)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.find()
{ "_id" : ObjectId("5461e78066f397ec9d6bf459"), "title" : "My Blog Post", "content" : "Here's my blog post.", "date" : ISODate("2014-11-11T10:39:47.464Z"), "comments" : [ ] }
删除

使用remove方法,如果需要全部删除则使用remove(“”)

> db.blog.remove({title:"My Blog Post"})
WriteResult({ "nRemoved" : 1 })
> db.blog.find()
/*---------------*/
> db.blog.remove("")
WriteResult({ "nRemoved" : 1 })
> db.blog.find()

数据类型

基本数据类型
  • null
  • boolean
  • number
  • string
  • date
  • 数组
  • 内嵌文档
  • 对象id
  • 二进制数据
  • 代码
    查询和文档中可以包含任意JavaScript代码
日期

使用new Date(...)

数组

这里的数组可以包含不同的数据类型的元素
{"things":["pie",3.14]}

内嵌文档
{
    "name":"John Doe",
    "address":{
        "city":"Anytown",
        "state":"NY"
    }
}
_id和ObjectId

是在一个集合中每个文档的唯一标识。

生成方式看书p20

使用MongoDB shell

可以连接到其他机器的MongoDB上(服务器)

shell小贴士

help

使用shell执行脚本

mongo script1.js script2.js
mongo shell会依次执行传入的脚本,然后退出。

也可以使用load()函数,从交互式shell中运行脚本:
load("script1.js")

在脚本中可以访问db变量,以及其他全局变量。然而,shell辅助函数(比如”use db”和”show collections”)不可以在文件中使用。这些辅助函数都有对应的JavaScript函数。

创建.mongorc.js文件

在用户主目录下创建一个名为.mongorc.js的文件,这是一个before文件,最常见的用途之一是移除那些比较”危险”的shell辅助函数。可以在这里集中重写这些方法。例如

var no = function() {
print("Not on my watch.");
};
// Prevent dropping databases
db.dropDatabase = DB.prototype.dropDatabase = no;
// Prevent dropping collections
DBCollection.prototype.drop = no;
// Prevent dropping indexes
DBCollection.prototype.dropIndex = no;

改变数据库函数时,要确保同时对db变量和DB原型进行改变(如上例)。如果只改变了其中一个,那么db变量可能没有改变,或者这些改变在新使用的所有数据库(运行use anotherDB命令)中都不会生效。

如果在启动shell时指定–norc参数,就可以禁止加载.mongorc.js。

定制shell提示

将prompt变量设为一个字符串或者函数,就可以重写默认的shell提示。
例如,显示当前使用的数据库:

prompt = function() {
if (typeof db == 'undefined') {
return '(nodb)> ';
}
// Check the last db operation
try {
db.runCommand({getLastError:1});
}
catch (e) {
print(e);
}
return db+"> ";
};

可以在.mongorc.js中定制自己想要的提示。

编辑复合变量
集合命名注意事项

创建,更新和删除文档

插入并保存文档

db.mycollection.insert({"key":"value"})
这个操作会给文档自动增加一个”_id”键(要是原来没有的话),然后将其保存到MongoDB中。

批量插入

暂时找不到2.6批量插入的方法。

插入效验

文档大小必须小于16MB

删除文档

db.mycollection.remove({})/remove{"key":"value"}

删除速度

如果要清空整个集合,使用drop直接删除集合会更快(然后在这个空集合上重建各项索引)。

更新文档

文档存入数据库以后,就可以使用update方法来更新它。update有两个参数:
1. 查询文档,用于定位需要更新的目标文档
2. 修改器(modifier)文档,用于说明要对找到的文档进行哪些修改。

更新操作是不可分割的:若是两个更新同时发生,先到达服务器的先执行,接着执行另外一个。

文档替换

最简单的更新就是用一个新文档完全替换匹配的文档。这适用于进行大规模模式迁移的情况。

> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing" }
> var name = db.blog.findOne()
> name
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing" }
> name.age=22
22
> db.blog.update({"name":"Bing"},name);
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "age" : 22 }
> 

使用”_id”作为查询条件比使用随机字段速度更快,因为是通过”_id”建立的索引。

使用修改器

通常文档只会有一部分要更新。可以使用原子性的更新修改器(update modifier),指定对文档中的某些字段进行更新。

  • $inc 修改器增加某个键的值,若该键不存在则创建一个只能用于整型,长整型或双精度浮点型的值
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "age" : 22 }
> db.blog.update({"name":"Bing"},
... {"$inc":{"age":-1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "age" : 21 }
  • $set 指定一个字段的值。如果这个字段不存在,则创建它。
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "age" : 21 }
> db.blog.update({"name":"Bing"},
... {"$set":{"favorite moive in the year":"星际穿越"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "age" : 21, "favorite moive in the year" : "星际穿越" }
  • $unset 将一个键完全删除
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "age" : 21, "favorite moive in the year" : "星际穿越" }
> db.blog.update({"name":"Bing"},
... {"$unset":{"age":1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越" }
  • $push 向已有的数组末尾加入一个元素,要是没有就创建一个新的数组。
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越" }
> db.blog.update({"name":"Bing"},
... {"$push":{"comments":{"content":"getArray"}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越", "comments" : [ { "content" : "getArray" } ] }
> db.blog.update({"name":"Bing"}, {"$push":{"comments":{"content":"2"}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越", "comments" : [ { "content" : "getArray" }, { "content" : "2" } ] }
  • $each $push的简单使用形式,可以通过一次”$push”操作添加多个值
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越", "comments" : [ { "content" : "getArray" }, { "content" : "2" } ] }
> db.blog.update({"name":"Bing"},
... {"$push":{"hourly":{"$each":[563.321,532.0,234]}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越", "comments" : [ { "content" : "getArray" }, { "content" : "2" } ], "hourly" : [ 563.321, 532, 234 ] }

$slice 和$push组合使用,可以保证数组不会超出设定号的最大长度,这实际上就得到了一个最多包含N个元素的数组。$slice的值必须是负整数。不能智将#slice或者$sort与$push配合使用,且必须使用$each。

> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越", "comments" : [ { "content" : "getArray" }, { "content" : "2" } ], "hourly" : [ 563.321, 532, 234, 32, 43, 53, 1, 2345, 322, 0.23, 324 ] }
> db.blog.update(
... {name:"Bing"},
... {
...     $push:{
...         hourly:{
...         $each:[3,4,5],
...         $slice:-5
...         }
...     }
... }
... )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越", "comments" : [ { "content" : "getArray" }, { "content" : "2" } ], "hourly" : [ 324, 22.2, 3, 4, 5 ] }
/*---------------------------------------------------------*/
> db.blog.update( {name:"Bing"},
...{ $push:{ hourly:{ $each:[3,4,5], $slice:-5,$sort:-1 } } } )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越", "comments" : [ { "content" : "getArray" }, { "content" : "2" } ], "hourly" : [ 5, 4, 4, 3, 3 ] }
  • $ne 保证数组内的元素不重复
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越", "comments" : [ { "content" : "getArray" }, { "content" : "2" } ], "hourly" : [ 5, 4, 4, 3, 3 ] }
> db.blog.update({name:{"$ne":"Bing"}}, 
...{$push:{name:"Bing"}})
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越", "comments" : [ { "content" : "getArray" }, { "content" : "2" } ], "hourly" : [ 5, 4, 4, 3, 3 ] }
  • $addToSet 作用与$ne一致,但是它可以与$each组合使用
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越", "comments" : [ { "content" : "getArray" }, { "content" : "2" } ], "hourly" : [ 5, 4, 4, 3, 3 ] }
> db.blog.update({name:"Bing"},
... {$addToSet:{
... hourly:{
... $each:[3,53,0,66]}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越", "comments" : [ { "content" : "getArray" }, { "content" : "2" } ], "hourly" : [ 5, 4, 4, 3, 3, 53, 0, 66 ] }
  • $pull 删除元素
    db.blog.update({name:"Bing"},{$pull:{comments:{}}})

  • $pop 删除元素
    {$pop:{"key":1}}从数组末尾删除,{$pop:{"key":-1}}从数组开头删除

  • 基于位置的数组修改器,数组下标都是以0开头的,可以将下标直接作为键来选择元素

> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越", "hourly" : [ 6, 4, 4, 3, 3, 53, 0, 66 ] }
> db.blog.update({name:"Bing"}, {$inc:{"hourly.0":1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越", "hourly" : [ 7, 4, 4, 3, 3, 53, 0, 66 ] }
upsert

upsert是一种特殊的更新。要是没有找到符合更新条件的文档,就会以这个条件和更新文档为基础创建一个新的文档。如果找到了匹配的文档,则正常更新。当update第三个参数为true时代表upsert。

更新多个文档

update第四个参数为true代表更新多个文档。

返回被更新的文档

通过findAndModify得到被更新的文档

API
db.collection.findAndModify({
    query: <document>,
    sort: <document>,
    remove: <boolean>,
    update: <document>,
    new: <boolean>,
    fields: <document>,
    upsert: <boolean>
});
> db.blog.findAndModify({query: { name: "Bing" },update: { $inc: { score: 1 } },upsert: true})
{
    "_id" : ObjectId("54641eeaa5fa343b3e09e590"),
    "name" : "Bing",
    "favorite moive in the year" : "星际穿越",
    "hourly" : [
        7,
        4,
        4,
        3,
        3,
        53,
        0,
        66
    ]
}
> db.blog.find()
{ "_id" : ObjectId("54641eeaa5fa343b3e09e590"), "name" : "Bing", "favorite moive in the year" : "星际穿越", "hourly" : [ 7, 4, 4, 3, 3, 53, 0, 66 ], "score" : 1 }

写入安全机制

写入安全(Write Concern)是一种客户端设置,用于控制写入的安全级别。


查询

find()间接

db.collection.find(<criteria>, <projection>)criteria相当于where语句,projection相当于投影
eg:

//criteria
db.bios.find()
db.bios.find( { _id: 5 } )
db.bios.find(
   {
      _id: { $in: [ 5,  ObjectId("507c35dd8fada716c89d0013") ] }
   }
)
db.collection.find( { field: { $gt: value1, $lt: value2 } } );
db.bios.find(
   {
      awards: {
                $elemMatch: {
                     award: "Turing Award",
                     year: { $gt: 1980 }
                }
      }
   }
)
//projection
db.products.find( { qty: { $gt: 25 } }, { item: 1, qty: 1 } )
db.bios.find( { }, { name: 1, contribs: 1 } )
db.products.find( { qty: { $gt: 25 } }, { _id: 0, qty: 0 } )
db.bios.find(
   { },
   {
     _id: 0,
     'name.last': 1,
     contribs: { $slice: 2 }
   }
)

现在介绍criteria有什么:
- $lt : <
- $lte: <=
- $gt : >
- $gte: >=
- $in : 相当于sql中的in,可以用来查询一个键的多个值
- $nin: 与in相反,not in
- $ne : !=
- $or : or
db.raffle.find({"$or" : [{"ticket_no" : 725}, {"winner" : true}]})
db.raffle.find({"$or" : [{"ticket_no" : {"$in" : [725, 542, 390]}},{"winner" : true}]})
- $and: and
db.users.find({"$and" : [{"x" : {"$lt" : 1}}, {"x" : 4}]})
匹配那些”x”字段的值小于1并且等于4的文档。但不如以下效率高:
db.users.find({"x" : {"$lt" : 1, "$in" : [4]}})
- 可以查询null
db.c.find({"y" : null})
- 可以查询正则表达式
db.users.find({"name" : /joe/i})
i是忽略大小写,/这里面是正则表达式内容/
- $exists
db.inventory.find( { qty: { $exists: true, $nin: [ 5, 15 ] } } )
选择不等于5和15的
- $all 通过多个元素来匹配数组

db.food.find({fruit : {$all : ["apple", "banana"]}})
{"_id" : 1, "fruit" : ["apple", "banana", "peach"]}
{"_id" : 3, "fruit" : ["cherry", "banana", "apple"]}
  • $size 查询特定长度的数组
  • 更多

现在介绍projection有什么:
最下面

where查询

游标

数据库使用游标返回find的执行结果。客户端对游标的实现通常能够对最终结果进行有效的控制。可以限制结果的数量,略过部分结果,根据任意键按任意顺序的组合对结果进行各种排序,或者是执行其他一些强大的操作。
创建一个游标(先创建文档吧):

> for(i=0; i<100; i++) {
...
db.collection.insert({x : i});
... }
> var cursor = db.collection.find();

这么做的好擦是可以一次查看一条结果。

> while(cursor.hasNext()){
... obj=cursor.next();
... print(obj.x);
... }

cursor.hasNext()检查是否有后续结果存在,然后用cursor.next()获取它
它还实现了迭代器接口:

cursor.forEach(
    function(x){
    print(x.x)
})

更多

limit,skip,sort
db.test.find().limit(3)
db.test.find().skip(3)//略过
db.test.find().sort({x:-1})//1正序,-1倒序
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章