《Mongodb权威指南》学习笔记 第二章 入门

文档是Mongodb中数据的基本单元,非常类似于关系数据库管理系统中的行(但是比行要复杂得多)。

类似地,集合可以被看做是没有模式的表

Mongodb单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限。

Mongodb自带简洁但功能强大的JavaScript Shell,这个工具对于管理Mongodb实例和操作数据作用非常大。

每一个文档都有一个特殊的键“_id”,它在文档所处的集合中是唯一的。

2.1 文档

文档是Mongodb的核心概念,多个键及其关联的值有序地放置在一起便是文档。

在JavaScript里面,文档表示为对象:

{"greeting","Hello,world!"}

这个文档只有一个键"greeting",其对应的值为"Hello,world"

文档中的键/值对是有序的。通常文档中键的顺序并不重要。

文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型。

文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符

键不能含有\0(空字符),这个字符用来表示键的结尾

.和$有特别的意义,只有在特定环境下才能使用。

以下划线“_”开头的键是保留的。

Mongodb不但区分类型,也区分大小写。例如,下面两个文档是不同的:

{"foo",3}
{"foo","3"}

以下的文档也是不同的

{"foo",3}
{"Foo",3}
Mongodb的文档不能有重复的键。例如,下面的文档是非法的:
{"greeting":"Hello,World!","greeting":"Hello,Mongodb!"}
2.2 集合 

集合就是一组文档,如果说Mongodb中的文档类似于关系型数据库中的行,那么集合就如同表

2.2.1 无模式

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

把同种类型的文档放入同一个集合里面,可以使索引更加有效。

2.2.2  命名

集合名可以是满足下列条件的任意UTF-8字符串

集合名不能是空字符串“”

集合名不能含有\0字符(空字符),这个字符表示集合名的结尾

集合名不能以“system.”开头,这是为系统集合保留的前缀。例如system.users这个集合保存着数据库的用户信息。

用户创建的集合名字不能含有保留字符$.

子集合

组织集合的一种惯例使用"."字符分开的按命名空间划分的子集合。例如,带有博客功能的应用可能包含两个集合,分别是blog.posts和blog.authors.

这样做的目的只是为了使组织结构更好些,也就是说blog这个集合(这里根本就不需要存在)及其子集合没有任何关系。

GridFS是一种存储大文件的协议,使用子集合来存储文件的元数据,这样就与内容块分开了。

Mongodb的web控制台通过子集合的方式将数据组织在DBTOP部分

在Mongodb中使用子集合来组织数据是很好的方法。

2.3 数据库

Mongodb中多个文档组成集合,同样多个集合可以组成数据库。一个Mongodb实例可以承载多个数据库。将一个应用的所有数据都存储在同一个数据库中的作法就很好。

和集合一样,数据库也通过名字来标识。

不能是空字符串("")

不能含有''(空格)、.、$、/、\和\0(空字符)

应全部小写

最多64个字节

要记住一点,数据库名最终会变成文件系统里的文件。

有一些数据库名是保留的

admin

从权限的角度来看,这是"root"数据库

local

这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合

config

当Mongodb用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。

把数据库的名字放到集合名前面,得到就是集合的全限定名,成为命名空间。命名空间的长度不得超过121字节,在实际使用当中应该小于100字节

2.4 启动Mongodb

Mongodb一般作为网络服务器来运行,客户端可以连接到服务器并执行操作。

要启动该服务器,需要运行mongodb可执行文件。

$ ./mongodb

[root@localhost bin]# ./mongod --help for help and startup options
Sun Aug 26 22:28:27 
Sun Aug 26 22:28:27 warning: 32-bit servers don't have journaling enabled by default. Please use --journal if you want durability.
Sun Aug 26 22:28:27 
Sun Aug 26 22:28:27 [initandlisten] MongoDB starting : pid=9692 port=27017 dbpath=/data/db/ 32-bit host=localhost.localdomain
Sun Aug 26 22:28:27 [initandlisten] 
Sun Aug 26 22:28:27 [initandlisten] ** NOTE: when using MongoDB 32 bit, you are limited to about 2 gigabytes of data
Sun Aug 26 22:28:27 [initandlisten] **       see http://blog.mongodb.org/post/137788967/32-bit-limitations
Sun Aug 26 22:28:27 [initandlisten] **       with --journal, the limit is lower
Sun Aug 26 22:28:27 [initandlisten] 
Sun Aug 26 22:28:27 [initandlisten] db version v2.0.2, pdfile version 4.5
Sun Aug 26 22:28:27 [initandlisten] git version: 514b122d308928517f5841888ceaa4246a7f18e3
Sun Aug 26 22:28:27 [initandlisten] build info: Linux domU-12-31-39-01-70-B4 2.6.21.7-2.fc8xen #1 SMP Fri Feb 15 12:39:36 EST 2008 i686 BOOST_LIB_VERSION=1_41
Sun Aug 26 22:28:27 [initandlisten] options: {}
Sun Aug 26 22:28:27 [initandlisten] waiting for connections on port 27017
Sun Aug 26 22:28:27 [websvr] admin web console waiting for connections on port 28017
Sun Aug 26 22:28:59 [initandlisten] connection accepted from 127.0.0.1:38368 #1
Sun Aug 26 22:29:27 [clientcursormon] mem (MB) res:12 virt:93 mapped:0


 

或者在windows下,这样操作:

$mongodb.exe

mongodb在没有参数的情况下会使用默认数据目录/data/db(在windows下是C:\data\db),并使用27017端口。在启动Mongodb前,创建数据目录(比如mkdir -p /data/db),并确保对该目录有写权限

默认情况下,Mongodb是监听27017端口。

mongodb还会启动一个非常基本的HTTP服务器,监听数据比主端口号高1000的端口,也就是28017端口。这也就意味着你可以通过浏览器访问http://localhost:28017来获取数据库的管理信息

在启动服务的shell下可以键入Crtl+C来完全停止Mongodb的运行

2.5 Mongodb Shell

MongoDB自带一个JavaScript Shell,可以从命令行与MongoDB实例交互

2.5.1 运行shell

运行mongo启动shell

[root@localhost bin]# ./mongo
MongoDB shell version: 2.0.2
connecting to: test

shell会在启动时自动连接MongoDB服务器,所以要确保在使用shell之前启动mongod。shell是功能完备的JavaScript解释器,可以运行任何JavaScript程序。为了证明这一点,我们运行几个简单的数学运算。

> x=200
200
> x/5
40
>
 还可以充分利用JavaScript的标准库> Math.sin(Math.PI/2);
1
> new Date("2010/1/1");
ISODate("2009-12-31T16:00:00Z")
> "Hello,World!".replace("World","MongoDB");
Hello,MongoDB!
也可以定义和调用JavaScript函数> function factorial(n){
... if(n<=1) return 1;
... return n*factorial(n-1);
... }
> factorial(5);
120
2.5.2 MongoDB客户端虽然能运行任意JavaScript程序很酷,但shell的真正威力还在于它是一个独立的MongoDB客户端。开启的时候,shell会连到MongoDB服务器的test数据库,并将这个数据库连接赋值给全局变量db。这个变量是通过shell访问MongoDB的主要入口点。shell还有些非JavaScript语法的扩展,是为了方便习惯于SQL Shell的用户而添加的。如:选择要使用的数据库> db
test
> use foobar
switched to db foobar
> db
foobar
>
因为这是一个JavaScript shell,所以键入一个变量会将变量的值转换为字符串(这里就是数据库名)并打印出来。可以通过db变量来访问其中的集合。例如db.baz返回当前数据库的baz集合。

2.5.3 shell中的基本操作

在shell查看操作数据会用到4个基本操作:创建、读取、更新和删除(CRUD).

1 创建insert函数添加一个文档到集合里面。例如,假设要存储一篇博客文章。

首先,创建一个局部变量post,内容是代表文档的JavaScript对象。这里会有“title”,“content”和“date”(发表日期)几个键

 > post = {"title":"My Blog Post",
... "content":"Here's my blog post.",
... "date":new Date()}
{
        "title" : "My Blog Post",
        "content" : "Here's my blog post.",
        "date" : ISODate("2012-08-26T14:50:26.687Z")
}
这个对象是有效的MongoDB文档,所以可以用insert方法将其保存到blog集合中

> db.blog.insert(post)
> db.blog.find()
{ "_id" : ObjectId("503a37d2ea5c5826dc1e84fa"), "title" : "My Blog Post", "content" : "Here's my blog post.", "date" : ISODate("2012-08-26T14:50:26.687Z") }
>
除了我们输入的键/值对都完整地被保存下来,还有一个额外添加的键“_id”

2 读取find会返回集合里面所有的文档。若只想查看一个文档,可以用findOne:

> db.blog.findOne()
{
        "_id" : ObjectId("503a37d2ea5c5826dc1e84fa"),
        "title" : "My Blog Post",
        "content" : "Here's my blog post.",
        "date" : ISODate("2012-08-26T14:50:26.687Z")
}

find和findOne可以接受查询文档形式的限定条件。这将通过查询限制匹配的文档。使用find时,shell自动显示最多20个匹配的文档,但可以获取更多的文档。

3 更新如果要更改博客文章,就要用到update了。update接受(至少)两个参数;第一个是要更新文档的限定条件,第二个是新的文档。假设决定给我们先前写的文章增加评论内容,则需要增加一个新的键,对应的值是存放评论的数组。

第一步修改变量post,增加“comments”键

> post.comments = []
[ ]
然后执行update操作,用新版本的文档替换标题为"My Blog Post"的文章:

> db.blog.update({title:"My Blog Post"},post)
> db.blog.find()
{ "_id" : ObjectId("503a37d2ea5c5826dc1e84fa"), "title" : "My Blog Post", "content" : "Here's my blog post.", "date" : ISODate("2012-08-26T14:50:26.687Z"), "comments" : [ ] }
> db.blog.findOne()
{
        "_id" : ObjectId("503a37d2ea5c5826dc1e84fa"),
        "title" : "My Blog Post",
        "content" : "Here's my blog post.",
        "date" : ISODate("2012-08-26T14:50:26.687Z"),
        "comments" : [ ]
}
文档已经有了"comments"键。再用find查看一下,可以看到新的键:如上图。

4 删除remove用来从数据库中永久性的删除文档。在不使用参数进行调用的情况下,它会删除一个集合内的所有文档。它也可以接受一个文档以指定限定条件。例如,下面的命令会删除我们刚刚创建的文章:

> db.blog.remove({title:"My Blog Post"})
> db.blog.findOne()
null
> db.blog.find()
2.5.4 使用shell窍门由于mongo是个JavaScript shell,通过在线查看JavaScript的文档能获得很多帮助。shell本身内置了帮助文档,可以通过help命令查看。

> help
        db.help()                    help on db methods
        db.mycoll.help()             help on collection methods
        rs.help()                    help on replica set methods
        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 <db_name>                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
>
使用db.help()可以查看数据库级别的命令的帮助

> db.help()
DB methods:
        db.addUser(username, password[, readOnly=false])
        db.auth(username, password)
        db.cloneDatabase(fromhost)
        db.commandHelp(name) returns the help for the command
        db.copyDatabase(fromdb, todb, fromhost)
        db.createCollection(name, { size : ..., capped : ..., max : ... } )
        db.currentOp() displays the current operation in the db
        db.dropDatabase()
        db.eval(func, args) run code server-side
        db.getCollection(cname) same as db['cname'] or db.cname
        db.getCollectionNames()
        db.getLastError() - just returns the err msg string
        db.getLastErrorObj() - return full status object
        db.getMongo() get the server connection object
        db.getMongo().setSlaveOk() allow this connection to read from the nonmaster member of a replica pair
        db.getName()
        db.getPrevError()
        db.getProfilingLevel() - deprecated
        db.getProfilingStatus() - returns if profiling is on and slow threshold
        db.getReplicationInfo()
        db.getSiblingDB(name) get the db at the same server as this one
        db.isMaster() check replica primary status
        db.killOp(opid) kills the current operation in the db
        db.listCommands() lists all the db commands
        db.logout()
        db.printCollectionStats()
        db.printReplicationInfo()
        db.printSlaveReplicationInfo()
        db.printShardingStatus()
        db.removeUser(username)
        db.repairDatabase()
        db.resetError()
        db.runCommand(cmdObj) run a database command.  if cmdObj is a string, turns it into { cmdObj : 1 }
        db.serverStatus()
        db.setProfilingLevel(level,<slowms>) 0=off 1=slow 2=all
        db.shutdownServer()
        db.stats()
        db.version() current version of the server
        db.getMongo().setSlaveOk() allow queries on a replication slave server
        db.fsyncLock() flush data to disk and lock server for backups
        db.fsyncUnock() unlocks server following a db.fsyncLock()
>
集合的相关帮助可以通过db.foo.help()来查看

> db.foo.help()
DBCollection help
        db.foo.find().help() - show DBCursor help
        db.foo.count()
        db.foo.dataSize()
        db.foo.distinct( key ) - eg. db.foo.distinct( 'x' )
        db.foo.drop() drop the collection
        db.foo.dropIndex(name)
        db.foo.dropIndexes()
        db.foo.ensureIndex(keypattern[,options]) - options is an object with these possible fields: name, unique, dropDups
        db.foo.reIndex()
        db.foo.find([query],[fields]) - query is an optional query filter. fields is optional set of fields to return.
                                                      e.g. db.foo.find( {x:77} , {name:1, x:1} )
        db.foo.find(...).count()
        db.foo.find(...).limit(n)
        db.foo.find(...).skip(n)
        db.foo.find(...).sort(...)
        db.foo.findOne([query])
        db.foo.findAndModify( { update : ... , remove : bool [, query: {}, sort: {}, 'new': false] } )
        db.foo.getDB() get DB object associated with collection
        db.foo.getIndexes()
        db.foo.group( { key : ..., initial: ..., reduce : ...[, cond: ...] } )
        db.foo.mapReduce( mapFunction , reduceFunction , <optional params> )
        db.foo.remove(query)
        db.foo.renameCollection( newName , <dropTarget> ) renames the collection.
        db.foo.runCommand( name , <options> ) runs a db command with the given name where the first param is the collection name
        db.foo.save(obj)
        db.foo.stats()
        db.foo.storageSize() - includes free space allocated to this collection
        db.foo.totalIndexSize() - size in bytes of all the indexes
        db.foo.totalSize() - storage allocated for all data and indexes
        db.foo.update(query, object[, upsert_bool, multi_bool])
        db.foo.validate( <full> ) - SLOW
        db.foo.getShardVersion() - only for use with sharding
        db.foo.getShardDistribution() - prints statistics about data distribution in the cluster
>
有个了解函数公用的技巧,就是在输入的时候不要输括号。这样就会显示该函数的JavaScript源代码

> db.foo.update
function (query, obj, upsert, multi) {
    assert(query, "need a query");
    assert(obj, "need an object");
    var firstKey = null;
    for (var k in obj) {
        firstKey = k;
        break;
    }
    if (firstKey != null && firstKey[0] == "$") {
        this._validateObject(obj);
    } else {
        this._validateForStorage(obj);
    }
    this._mongo.update(this._fullName, query, obj, upsert ? true : false, multi ? true : false);
}
>
要查看shell提供的所有自动生成的JavaScript函数API文档,可以访问http://api.mongodb.org/js蹩脚的集合名使用db.集合名的方式来访问集合一般不会有问题,但如果集合名恰好是数据库类的一个属性就有问题了,

例如,要访问version这个集合,使用db.version就不行,因为db.version是个数据库函数(它返回正在运行的MongoDB服务器的版本)

> db.version
function () {
    return this.serverBuildInfo().version;
}
 当JavaScript只有在db中找不到指定的属性时,才会将其作为集合返回。当有属性与目标集合同名时,可以使用getCollection函数

> db.getCollection("version");
foobar.version
 要查看名称中含有无效JavaScript字符的集合,这个函数也可以派上用场。比如foo-bar是个有效的集合名,但是在JavaScript中就变成了变量相减了。

通过db.getCollection("foo-bar")可以得到foo-bar集合在JavaScript中,x.y与x['y']完全等价。这就意味着不但可以直呼其名,也可以使用变量来访问子集合。也就是说,当需要对blog的每个子集合执行操作时,只要像下面这样迭代就好了。

 2.6 数据类型

2.6.1 基本数据类型MongoDB的文档类似于JSON,在概念上和JavaScript中的对象神似。JSON是一种简单的表示数据的方式,仅包含6种数据类型。只有null、布尔、数字、字符串、数组和对象几种类型

MongoDB在保留JSON基本的键/值对特性的基础上,添加了其他一些数据类型。

null

null用于表示空值或者不存在的字段{"x":null}

布尔

布尔类型有两个值'true'和'false'{"x",true}

32位整数

shell中这个类型不可用,前面提到,JavaScript仅支持64位浮点数,所以32位整数会被自动转换

64位整数

shell也不支持这个类型,shell会使用一个特殊的内嵌文档来显示64位整数

64位浮点数

shell中的数字都是这种类型。下面是一个浮点数{"x":3.14}这个也是浮点数:{"x":3}

字符串

UTF-8字符串都可以表示为字符串类型的数据

{"x":"foobar"}

符号

shell不支持这种类型。shell将数据库里面的符号类型转换成字符串

对象id

对象id是文档的12字节的唯一ID

{"x":ObjectId()}

日期

日期类型存储的是从标准纪元开始的毫秒数。不存储时区:

{“x”:new Date()}

正则表达式

文档中可以包含正则表达式,采用JavaScript的正则表达式语法:

{"x":/foobar/i}

代码

文档中还可以包含JavaScript代码:{"x":function(){/*...*/}}

二进制数据

二进制数据可以由任意字节的串组成。不过shell中服务使用。

最大值

BSON包括一个特殊类型,表示可能的最大值。shell中没有这个类型

最小值

BSON包括一个特殊类型,表示可能的最小值,shell中没有这个类型

未定义

文档中也可以使用未定义类型(JavaScript中null和undefined是不同的类型)

{"x":undefined}

数组:值的集合或者列表可以表示成数组

{"x":["a","b","c"]}

内嵌文档文档可以包含别动文档,也可以作为值嵌入到父文档中

{"x":{"foo":"bar"}}

2.6.2 数字

JavaScript中只有一种“数字”类型。因为MongoDB中有3种数字类型(32位整数、64位整数和64位浮点数),shell必须绕过JavaScript限制。

默认情况下shell中的数字都被MongoDB当做是双精度数

这就意味着如果你从数据库中获得的是一个32位整数,修改文档后,将文档存回数据库的时候,这个整数也被转换成了浮点数,及时保持这个整数原封不动也会这样。

所以明智的作法是尽量不要在shell下覆盖整个文档。

数字只能表示为双精度数(64位浮点数)

2.6.3 日期

在JavaScript中,Date对象用做MongoDB的日期类型,创建一个新的Date对象时,通常会调用new Date(...)而不只是Date(...)

shell中的日期显示时使用本地时区设置,但是,日期在数据中是以从标准纪元开始的毫秒数的形式存储的,没有与之相关的时区信息。

2.6.4 数组

数组是一组值,既可以作为有序对象(像列表、栈或队列)来操作,也可以作为无序对象(像集合)操作。

在下面的文档中,"things"这个键的值就是一个数组

数组可以包含不同数据类型的元素

2.6.5 内嵌文档

内嵌文档就是把整个MongoDB文档作为另一个文档中键的一个值。

例如,用文档来表示一个人,同时还要保存他的地址,可以将地址内嵌到"add-ress"文档中

{

     “name”:"John Doe",

     "address":{

          "street":"123 Park Street",

          "city":"Anytown",

          "state":"NY"

      }

}

在关系型数据库中,以上文档一般会被拆分成两个表"people"和“address”中的两个行。在MongoDB中,就可以将地址文档直接嵌入人员文档中。

 

2.6.6 _id和ObjectId

MongoDB中存储的文档必须有一个"_id"键,这个键的值可以是任何类型的,默认是个ObjectId对象。在一个集合里面,每一个文档都有唯一的"_id"值,来确保集合里面每个文档都能被唯一标示。

1 ObjectId是"_id"的默认类型

ObjectId使用12字节的存储空间,每个字节两位16进制数字,是一个24位的字符串。

前4个字节是从标准纪元开始的时间戳,单位为秒,

时间戳,与随后的5个字节组合起来,提供了秒级别的唯一性

由于时间戳在前,这意味着ObjectId大致会按照插入的顺序排列。

中4个字节也隐含了文档创建的时间,绝大多数驱动都会公开一个方法从ObjectId获取这个信息。

接下来的3个字节是所在主机的唯一标识符

为了确保在同一台机器上并发的多个线程产生的ObjectId是唯一的,接下来的两个字节来自产生ObjectId的进程标识符(PID)

前9个字节保证了同一秒钟不同机器不同进程产生的ObjectId是唯一的。后3个字节就是一个自动增加的计数器,确保相同进程同一秒产生的ObjectId也是不一样的。

2 自动生成_id

如果插入文档的时候没有“_id”键,系统会自动帮你创建一个。

可以由MongoDB服务器来做这件事情,但通常会在客户端由驱动程序完成。

虽然ObjectId设置成轻量型的,易于生成,但是毕竟生成的时候还是产生开销。

在客户端生成提现了MongoDB的设计理念:能从服务器端转移到驱动程序来做的事,就尽量转移。将事物交给客户端来处理,就减轻了数据库扩展的负担。

在客户端生成ObjectId,驱动程序能够提供更加丰富的API

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值