本文隶属于专题系列: Mongodb入门系列

说到Mongodb的体系结构,免不了与关系型数据库做个对比。这里以MySQL为例,我们进行一些比较:

从逻辑结构上对比:

MySQL层次概念 MongoDB层次概念
数据库(database) 数据库(database)
表(table) 集合(collection)
记录(row) 文档(document)

在MongoDB中没有行、列、关系的概念,集合中的文档相当于一条记录,这体现了模式自由的特点。

从数据存储结构上对比:

MySQL的每个数据库存放在一个与数据库同名的文件夹中,MySQL如果使用MyISAM存储引擎,数据库文件类型就包括.frm、.MYD(存放数据,D为Data)、.MYI(存放索引,I为Index)。

MongoDB的默认数据目录时/data/db,它负责存储所有MongoDB的数据文件。在MongoDB内部,每个数据库都包含一个.ns文件和一些数据文件,而且这些数据文件会随着数据量的增加而变得越来越多,例如系统中有一个叫mydb的数据库,那么构成mydb这个数据库的文件就会有mydb.ns,mydb.0,mydb.1等等组成。

mydb.ns记录了数据库Json对象的命名空间(ns时namespace的缩写),也就是数据库集合里面的命名空间。mydb.0和mydb.1是存放数据库mydb的对象的空间,且大小按照2的n次方大小递增。如mydb.`0的大小是16M,当数据库mydb存满16M之后,就会形成生成mydb.1继续存储,mydb.1的大小为32M,以此类推,随着数据的增加,还会有mydb.2、mydb.3等文件出现,大小64M、128M。默认情况下,现在版本的Mongodb在数据库刚刚建立时就会预先分配好XXX.0和XXX.1共48M空间,之后再随着插入对象的增多而生成后续xxx.2等。

MongoDB客户段基本操作:

首先当然是要确认MongoDB的mongod服务是打开的。详细见我之前的博客。

打开MongoDB客户端的方法时运行MongoDB的bin目录下的mongo。

[neil@neilhost Downloads]$ pstree -p | grep mongod
           |-mongod(3556)-+-{mongod}(3557)
           |              |-{mongod}(3558)
           |              |-{mongod}(3559)
           |              |-{mongod}(3563)
           |              |-{mongod}(3564)
           |              |-{mongod}(3565)
           |              |-{mongod}(3566)
           |              |-{mongod}(3567)
           |              `-{mongod}(3568)
[neil@neilhost Downloads]$ cd /usr/local/mongodb/bin/mongo
bash: cd: /usr/local/mongodb/bin/mongo: 不是目录
[neil@neilhost Downloads]$ sudo  /usr/local/mongodb/bin/mongo
MongoDB shell version: 2.6.8
connecting to: test
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
	http://docs.mongodb.org/
Questions? Try the support group
	http://groups.google.com/group/mongodb-user

接下来介绍几个基本操作,以及一个细节。

show dbs的意思是显示mongodb中所有的数据库。刚安装好mongodb时,默认有两个数据库admin和local,不去管他们。

db指的是当前工作环境所在的数据库。当你每次进入mongo时。默认进入的数据库时test,它是一个隐式存储的数据库,如果需要进入特定的数据库,或是想建立一个新的数据库,只需要“use 数据库名称”就可以了。在mongodb中不需要create database这种操作,想用就用,mongodb会自动帮我们建立了数据库,就像一个服务周到的“黑执事”。这里,我use dt2建立了一个新的数据库dt2,客户端立即现实工作环境转入到dt2。但是,如果你show dbs,发现数据库并没有真正建立。是需要新建表并且插入一些数据才可以吗?不是,只需要你在当前数据库dt2输入任何一个细小的操作命令,如显示当前数据库的集合有哪些,这时候dt2就会被真正建立了。

connecting to: test
> show dbs
admin  (empty)
dt1    0.078GB
local  0.078GB
> db
test
> use dt2
switched to db dt2
> show dbs
admin  (empty)
dt1    0.078GB
local  0.078GB
> show collections
> show dbs
admin  (empty)
dt1    0.078GB
dt2    (empty)
local  0.078GB

在上面的命令中,show collections是显示当前数据库下的集合有哪些。因为现在还没有集合,所以什么都不显示。

接下来我们尝试建立集合和插入数据。

> db.student.find()
> show collections
>

mongodb建立集合(表),依然是一个不需要声明着建立表的过程。即不需要create collection或create table之类操作。

直接用即可。

这里我们依旧首先验证一个小细节。什么样子的表操作才会导致表的生成。通过上面的命令。我们在还没有student集合的时候对其进行查询(find()),但是,之后显示集合的命令可以看到并没有为dt2建立student集合。

于是我直接在student表中插入一个Json对象(存储在Mongodb的单元是Bson对象,在逻辑概念上是一个文档)

> db.student.insert({name:"Viper",age:20})
WriteResult({ "nInserted" : 1 })
> show collections
student
system.indexes
>

这时候,当show collections的时候可以看见,student集合已经有了。这说明在建立集合时,必须想新的集合中插入有效数据,才能真正建立集合。

总结上面的两个细节就是,在MongoDB中建立数据库时,只要use数据库,并且在数据库下执行任何看似不会有任何影响的命令,如查询集合,都会使得数据库建立起来;但是,如果在数据库下,对于新建集合的文档查询是不会导致集合建立的,必须有文档数据插入集合,才能使得集合真正建立起来。这样一对细节很多人未必知道!

另外当空数据库建立集合时,会生成一个索引表,system.indexes。该数据库下的所有集合的ObjectId的索引值全都存放在这里面。

那么下面我们来分别说说增删改查。

前面的例子中其实已经加入了一条文档,这里我们在增加一个文档到student集合。

> db.student.insert({name:"TA",age:18,phone:["1311234567","021-87658765"],GPA:{Math:88,English:99}})
WriteResult({ "nInserted" : 1 })
> db.student.find()
{ "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
{ "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA", "age" : 18, "phone" : [ "1311234567", "021-87658765" ], "GPA" : { "Math" : 88, "English" : 99 } }
>

这里可以看到,我向mongodb的student集合中插入的两个文档,它们的“表结构”是不一样的。这就是NoSQL数据库与关系型数据库的最重要区别之一(其他区别之前也提过,ACID特性(事务的支持),并发)。

其中第二条文档中我们看到key-value的value可以是一些数、字符串等基本类型外,还可以是数组(其实可以理解为栈,后面的博客文章会介绍value是数组情况下的push和pop,所以理解为一种类似于栈的列表更妥贴),如上面的phone键。更强大的地方是,文档,即插入的Json对象的keybalue的value可以是另一个Json对象,如上面的GPA键。

其实,这些数组、json对象更像是python语法中的列表和字典,哈哈哈哈。。。

这里,我将key-value的value数据类型做个整理:

null 表示空值或者不存在的值
布尔类型 true和false,如{male:true}
32位整数 Mongodb的控制台使用JS引擎进行输入,而JS仅仅支持64位浮点数,所以32位整数会被自动转义
64位整数 同上,会被自动转义成64位浮点数
64位浮点数 Mongodb的控制台数字的默认类型。如{salary:23871.12}
字符串 UTF-8字符串都可以表示为字符串类型的数据
符号 在MongoDB中不支持这种类型,将自动转义成字符串
ObjectId MongoDB独有,对象id时文档中唯一的12位16进制的id。(时间戳 | 机器 | PID | 计数器)
日期 注意:使用的时候要加上new。如{birthday:new Date()}
正则表达式 文档键值可以包含正则表达式,其正则表达式采用JS语法来表示。如:{key:/ho/i}
代码 文档中可以包含JS文档。如{key:function(){/*........*/}}(可以是没有函数名的匿名函数)
数组 文档中的key的value可以表示为数组,数组内还可以嵌套数组。
內嵌文档 文档可以包含别的文档,也可以作为value嵌入到父文档中。如{x:{name:"happyBKs",age:2}}

插入之后的Json对象我们可以看到,新插入的每个文档都被赋予了一个_id,这是可以理解为记录的主,是mongodb自动生存成的key,它的value是一个特定的ObjectId对象,是一个96位二进制数,由机器码、机器进程号、时间、当前命名空间的编号四个部分自动生成,独一无二。当然,如果你愿意,也可以在插入Json对象时自己指定_id的value,只要未发生主键冲突,都可以正常插入。

 增加一个记录除了用insert方法之外还有一种方法,那就是save。它的功能是当你指定Json的_id,并且_id在集合中已经存在,那么它会更新相应的文档;否则,则插入一个新的文档。请看下面这个例子,_id的为1的Json对象,第一次save是被添加,而第二次则是被更改。

> db.student.find()
{ "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
{ "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA", "age" : 18, "phone" : [ "1311234567", "021-87658765" ], "GPA" : { "Math" : 88, "English" : 99 } }
> db.student.save({_id:1,name:"happyBKs",age:0})
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : 1 })
> db.student.find()
{ "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
{ "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA", "age" : 18, "phone" : [ "1311234567", "021-87658765" ], "GPA" : { "Math" : 88, "English" : 99 } }
{ "_id" : 1, "name" : "happyBKs", "age" : 0 }
> db.student.save({_id:1,name:"hahaBKs",age:2})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.find()
{ "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
{ "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA", "age" : 18, "phone" : [ "1311234567", "021-87658765" ], "GPA" : { "Math" : 88, "English" : 99 } }
{ "_id" : 1, "name" : "hahaBKs", "age" : 2 }
>

本文是oschina博客作者的心血,转载请表明出处!

本文是oschina博客作者的心血,转载请表明出处!(http://my.oschina.net/u/1156339/blog/384073)

更改的操作用的是update方法。

用法为: db.collection.update({...},{...})

参数有两个,第一个指定要改谁,第二个指定改成什么。

但是事情没有这么简单,请看下面的例子。原本的打算是将名字为hahaBKs的记录增加一个性别字段,但是发现除了_id的所有字段都被覆盖掉了,只剩下了gender。这显然不是我们的预想的结果。

> db.student.find()
{ "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
{ "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA", "age" : 18, "phone" : [ "1311234567", "021-87658765" ], "GPA" : { "Math" : 88, "English" : 99 } }
{ "_id" : 1, "name" : "hahaBKs", "age" : 2 }
> 
> db.student.update({name:"hahaBKs"},{gender:"male"})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.find()
{ "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
{ "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA", "age" : 18, "phone" : [ "1311234567", "021-87658765" ], "GPA" : { "Math" : 88, "English" : 99 } }
{ "_id" : 1, "gender" : "male" }
>

那么问题出在哪里呢?这里我们需要用到的一个操作符$set,具体的用法以后的文章李会详细来说。

下面我直接给出一个解决办法的示例,如下:

> db.student.insert({name:"TB",age:11,gender:"male",room:"301"})
WriteResult({ "nInserted" : 1 })
> db.student.find()
{ "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
{ "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA", "age" : 18, "phone" : [ "1311234567", "021-87658765" ], "GPA" : { "Math" : 88, "English" : 99 } }
{ "_id" : 1, "gender" : "male" }
{ "_id" : ObjectId("54fc521d3fc8173ba3302e6e"), "name" : "TB", "age" : 11, "gender" : "male", "room" : "301" }
> db.student.update({name:"TB"},{$set:{age:22,classid:"1515"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.find()
{ "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
{ "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA", "age" : 18, "phone" : [ "1311234567", "021-87658765" ], "GPA" : { "Math" : 88, "English" : 99 } }
{ "_id" : 1, "gender" : "male" }
{ "_id" : ObjectId("54fc521d3fc8173ba3302e6e"), "name" : "TB", "age" : 22, "gender" : "male", "room" : "301", "classid" : "1515" }
>

上面的例子是,增加了一个name叫TB的对象,然后对其进行更改,更改的内容包括,将原有age键的值11改成22,并增加一个新的键classid赋值“1515”。这里需要用到的操作符就是$set,用于设置键的值。

删除的方法是remove。

用法是 db.collection.remove({...})

参数里写明想需要删除的对象的条件。

注意,当参数空着,或者空的Json对象。即db.collection.remove()和db.collection.remove({}),将把该集合中的所有文档全部删除!!!

> db.class.insert({classname:"English",teacher:"Mr A"})
WriteResult({ "nInserted" : 1 })
> db.class.insert({classname:"Math",teacher:"Mr B"})
WriteResult({ "nInserted" : 1 })
> db.class.find()
{ "_id" : ObjectId("54fc54773fc8173ba3302e6f"), "classname" : "English", "teacher" : "Mr A" }
{ "_id" : ObjectId("54fc54833fc8173ba3302e70"), "classname" : "Math", "teacher" : "Mr B" }
> db.class.remove({classname:"English"})
WriteResult({ "nRemoved" : 1 })
> db.class.find()
{ "_id" : ObjectId("54fc54833fc8173ba3302e70"), "classname" : "Math", "teacher" : "Mr B" }
> 
> db.class.remove({})
WriteResult({ "nRemoved" : 1 })
> db.class.find()
>

上面的例子中新建了一个class集合,并插入两个文档。其中种种,自己看吧。

用法两种

db.collection.find({....}),其中参数是查询对象的条件。如果find()或find({})就是全查,这和remove很类似。

db.collection.findOne({...})和find相似,但是它只会返回第一个查询到的符合条件的对象。

> db.class.insert({classname:"English",teacher:"Mr AAA"})
WriteResult({ "nInserted" : 1 })
> db.class.insert({classname:"English",teacher:"Mr ZZZ"})
WriteResult({ "nInserted" : 1 })
> db.class.insert({classname:"English",teacher:"Mr WWW"})
WriteResult({ "nInserted" : 1 })
> db.class.insert({classname:"English",teacher:"Mr SSS"})
WriteResult({ "nInserted" : 1 })
> db.class.insert({classname:"French",teacher:"Mr SSS"})
WriteResult({ "nInserted" : 1 })
> db.class.find({})
{ "_id" : ObjectId("54fc562e3fc8173ba3302e71"), "classname" : "English", "teacher" : "Mr AAA" }
{ "_id" : ObjectId("54fc56373fc8173ba3302e72"), "classname" : "English", "teacher" : "Mr ZZZ" }
{ "_id" : ObjectId("54fc56413fc8173ba3302e73"), "classname" : "English", "teacher" : "Mr WWW" }
{ "_id" : ObjectId("54fc564e3fc8173ba3302e74"), "classname" : "English", "teacher" : "Mr SSS" }
{ "_id" : ObjectId("54fc56603fc8173ba3302e75"), "classname" : "French", "teacher" : "Mr SSS" }
> db.class.findOne({classname:"English"})
{
	"_id" : ObjectId("54fc562e3fc8173ba3302e71"),
	"classname" : "English",
	"teacher" : "Mr AAA"
}
> db.class.find({classname:"English"})
{ "_id" : ObjectId("54fc562e3fc8173ba3302e71"), "classname" : "English", "teacher" : "Mr AAA" }
{ "_id" : ObjectId("54fc56373fc8173ba3302e72"), "classname" : "English", "teacher" : "Mr ZZZ" }
{ "_id" : ObjectId("54fc56413fc8173ba3302e73"), "classname" : "English", "teacher" : "Mr WWW" }
{ "_id" : ObjectId("54fc564e3fc8173ba3302e74"), "classname" : "English", "teacher" : "Mr SSS" }
>

上面例子自己看吧。

本文先就写到这里,增删改查的内容还有很多很多东西,我将放到后面的文章中写,哈哈!

你可能感兴趣的内容
0条评论

dexcoder

这家伙太懒了 <( ̄ ﹌  ̄)>
Owner