资讯专栏INFORMATION COLUMN

MongoDB指南---2、MongoDB基础知识-文档、集合、数据库、客户端

SnaiLiu / 3382人阅读

摘要:上一篇文章指南简介下一篇文章指南基础知识数据类型非常强大但很容易上手。把同种类型的文档放在一个集合里,数据会更加集中。命名集合使用名称进行标识。集合名不能是空字符串。简单起见,数据库名应全部小写。

上一篇文章:MongoDB指南---1、MongoDB简介
下一篇文章:MongoDB指南---3、MongoDB基础知识-数据类型

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

2.1 文档

文档是MongoDB的核心概念。文档就是键值对的一个有序集。每种编程语言表示文档的方法不太一样,但大多数编程语言都有一些相通的数据结构,比如映射(map)、散列(hash)或字典(dictionary)。例如,在JavaScript 里面,文档被表示为对象:

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

这个文档只有一个键"greeting",其对应的值为"Hello,world!"。大多数文档会比这个简单的例子复杂得多,通常会包含多个键/值对:

 {"greeting" : "Hello, world!", "foo" : 3}

从上面的例子可以看出,文档中的值可以是多种不同的数据类型(甚至可以是一个完整的内嵌文档,详见2.6.4节)。在这个例子中,"greeting"的值是一个字符串,而"foo"的值是一个整数。
文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。
键不能含有0(空字符)。这个字符用于表示键的结尾。
.和$具有特殊意义,只能在特定环境下使用(后面的章节会详细说明)。通常,这两个字符是被保留的;如果使用不当的话,驱动程序会有提示。
MongoDB不但区分类型,而且区分大小写。例如,下面的两个文档是不同的:

{"foo" : 3}
{"foo" : "3"}

下面两个文档也是不同的:

{"foo" : 3}
{"Foo" : 3}

还有一个非常重要的事项需要注意,MongoDB的文档不能有重复的键。例如,下面的文档是非法的:

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

文档中的键/值对是有序的:{"x" : 1, "y":2}与{"y": 2, "x": 1}是不同的。通常,字段顺序并不重要,无须让数据库模式依赖特定的字段顺序(MongoDB会对字段重新排序)。在某些特殊情况下,字段顺序变得非常重要,本书将就此给出提示。
一些编程语言对文档的默认表示根本就不包含顺序问题(如:Python中的字典、Perl和Ruby 1.8中的散列)。通常,这些语言的驱动具有某些特殊的机制,可以在必要时指定文档的顺序。

2.2 集合

集合就是一组文档。如果将MongoDB中的一个文档比喻为关系型数据库中的一行,那么一个集合就相当于一张表。

2.2.1 动态模式

集合是动态模式的。这意味着一个集合里面的文档可以是各式各样的。例如,下面两个文档可以存储在同一个集合里面:

{"greeting" : "Hello, world!"}
{"foo" : 5}

需要注意的是,上面的文档不光值的类型不同(一个是字符串,一个是整数),它们的键也完全不同。因为集合里面可以放置任何文档,随之而来的一个问题是:还有必要使用多个集合吗?这的确值得思考:既然没有必要区分不同类型文档的模式,为什么还要使用多个集合呢?这里有几个重要的原因。
如果把各种各样的文档不加区分地放在同一个集合里,无论对开发者还是对管理员来说都将是噩梦。开发者要么确保每次查询只返回特定类型的文档,要么让执行查询的应用程序来处理所有不同类型的文档。如果查询博客文章时还要剔除含有作者数据的文档,这会带来很大困扰。
在一个集合里查询特定类型的文档在速度上也很不划算,分开查询多个集合要快得多。例如,假设集合里面一个名为"type"的字段用于指明文档是skim、whole还是chunky monkey。那么,如果从一个集合中查询这三种类型的文档,速度会很慢。但如果将这三种不同类型的文档拆分为三个不同的集合,每次只需要查询相应的集合,速度快得多。
把同种类型的文档放在一个集合里,数据会更加集中。从一个只包含博客文章的集合里查询几篇文章,或者从同时包含文章数据和作者数据的集合里查出几篇文章,相比之下,前者需要的磁盘寻道操作更少。
创建索引时,需要使用文档的附加结构(特别是创建唯一索引时)。索引是按照集合来定义的。在一个集合中只放入一种类型的文档,可以更有效地对集合进行索引。
上面这些重要原因促使我们创建一个模式,把相关类型的文档组织在一起,尽管MongoDB对此并没有强制要求。

2.2.2 命名

集合使用名称进行标识。集合名可以是满足下列条件的任意UTF-8字符串。

集合名不能是空字符串("")。

集合名不能包含0字符(空字符),这个字符表示集合名的结束。

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

用户创建的集合不能在集合名中包含保留字符"$"。因为某些系统生成的集合中包含$,很多驱动程序确实支持在集合名里包含该字符。除非你要访问这种系统创建的集合,否则不应该在集合名中包含$。

子集合

组织集合的一种惯例是使用“.”分隔不同命名空间的子集合。例如,一个具有博客功能的应用可能包含两个集合,分别是blog.posts和blog.authors。这是为了使组织结构更清晰,这里的blog集合(这个集合甚至不需要存在)跟它的子集合没有任何关系。
虽然子集合没有任何特别的属性,但它们却非常有用,因而很多MongoDB工具都使用了子集合。

GridFS(一种用于存储大文件的协议)使用子集合来存储文件的元数据,这样就可以与文件内容块很好地隔离开来。(第6章会详细介绍GridFS。)

大多数驱动程序都提供了一些语法糖,用于访问指定集合的子集合。例如,在数据库shell中,db.blog代表blog集合,而db.blog.posts代表blog.posts集合。

在MongoDB中,使用子集合来组织数据非常高效,值得推荐。

2.3 数据库

在MongoDB中,多个文档组成集合,而多个集合可以组成数据库。一个MongoDB实例可以承载多个数据库,每个数据库拥有0个或者多个集合。每个数据库都有独立的权限,即便是在磁盘上,不同的数据库也放置在不同的文件中。按照经验,我们将有关一个应用程序的所有数据都存储在同一个数据库中。要想在同一个MongoDB服务器上存放多个应用程序或者用户的数据,就需要使用不同的数据库。
数据库通过名称来标识,这点与集合类似。数据库名可以是满足以下条件的任意UTF-8字符串。

不能是空字符串("")。

不得含有/、、.、"、*、<、>、:、|、?、$(一个空格)、0(空字符)。基本上,只能使用ASCII中的字母和数字。

数据库名区分大小写,即便是在不区分大小写的文件系统中也是如此。简单起见,数据库名应全部小写。

数据库名最多为64字节。

要记住一点,数据库最终会变成文件系统里的文件,而数据库名就是相应的文件名,这是数据库名有如此多限制的原因。
另外,有一些数据库名是保留的,可以直接访问这些有特殊语义的数据库。这些数据库如下所示。

admin

从身份验证的角度来讲,这是“root”数据库。如果将一个用户添加到admin数据库,这个用户将自动获得所有数据库的权限。再者,一些特定的服务器端命令也只能从admin数据库运行,如列出所有数据库或关闭服务器。

local

这个数据库永远都不可以复制,且一台服务器上的所有本地集合都可以存储在这个数据库中。(第9章会详细介绍复制及本地数据库。)

config

MongoDB用于分片设置时(参见第13章),分片信息会存储在config数据库中。

把数据库名添加到集合名前,得到集合的完全限定名,即命名空间(namespace)。例如,如果要使用cms数据库中的blog.posts集合,这个集合的命名空间就是cms.blog.posts。命名空间的长度不得超过121字节,且在实际使用中应小于100字节。(参考附录B,了解MongoDB中集合的命名空间及内部表示的更多信息。)

2.4 启动MongoDB

通常,MongoDB作为网络服务器来运行,客户端可连接到该服务器并执行操作。下载MongoDB(http://www.mongodb.org/downloads)并解压,运行mongod命令,启动数据库服务器:

$ mongod
mongod --help for help and startup options
Thu Oct 11 12:36:48 [initandlisten] MongoDB starting : pid=2425 port=27017
    dbpath=/data/db/ 64-bit host=spock
Thu Oct 11 12:36:48 [initandlisten] db version v2.4.0, pdfile version 4.5
Thu Oct 11 12:36:48 [initandlisten] git version:
    3aaea5262d761e0bb6bfef5351cfbfca7af06ec2
Thu Oct 11 12:36:48 [initandlisten] build info: Darwin spock 11.2.0 Darwin Kernel
    Version 11.2.0: Tue Aug 9 20:54:00 PDT 2011;
    root:xnu-1699.24.8~1/RELEASE_X86_64 x86_64 BOOST_LIB_VERSION=1_48
Thu Oct 11 12:36:48 [initandlisten] options: {}
Thu Oct 11 12:36:48 [initandlisten] journal dir=/data/db/journal
Thu Oct 11 12:36:48 [initandlisten] recover : no journal files present, no
    recovery needed
Thu Oct 11 12:36:48 [websvr] admin web console waiting for connections on
    port 28017
Thu Oct 11 12:36:48 [initandlisten] waiting for connections on port 27017

在Windows系统中,执行这个命令:

$ mongod.exe

mongod在没有参数的情况下会使用默认数据目录/data/db(Windows系统中为C:datadb)。如果数据目录不存在或者不可写,服务器会启动失败。因此,在启动MongoDB前,先创建数据目录(如mkdir -p /data/db/),以确保对该目录有写权限,这点非常重要。

sudo mkdir -p /data/db/
sudo chmod -R 777 /data/db

启动时,服务器会打印版本和系统信息,然后等待连接。默认情况下,MongoDB监听27017端口。如果端口被占用,启动将失败。通常,这是由于已经有一个MongoDB实例在运行了。
mongod还会启动一个非常基本的HTTP服务器,监听数字比主端口号高1000的端口,也就是28017端口。这意味着,通过浏览器访问http://localhost:28017,能获取数据库的管理信息。
中止mongod的运行,只须在运行着服务器的shell中按下Ctrl-C。

2.5 MongoDB shell简介

MongoDB自带JavaScript shell,可在shell中使用命令行与MongoDB实例交互。shell非常有用,通过它可以执行管理操作,检查运行实例,亦或做其他尝试。对MongoDB来说,mongo shell是至关重要的工具,其应用之广泛将体现在本书接下来的部分中。

2.5.1 运行shell

运行mongo启动shell:

$ mongo
MongoDB shell version: 2.4.0
connecting to: test

启动时,shell将自动连接MongoDB服务器,须确保mongod已启动。
shell是一个功能完备的JavaScript解释器,可运行任意JavaScript程序。为说明这一点,我们运行几个简单的数学运算:

> x = 200
200
> x / 5;
40

另外,可充分利用JavaScript的标准库:

> Math.sin(Math.PI / 2);
1
> new Date("2010/1/1");
"Fri Jan 01 2010 00:00:00 GMT-0500 (EST)"
> "Hello, World!".replace("World", "MongoDB");
Hello, MongoDB!
再者,可定义和调用JavaScript函数:
> function factorial (n) {
... if (n <= 1) return 1;
... return n * factorial(n - 1);
... }
> factorial(5);
120

需要注意,可使用多行命令。shell会检测输入的JavaScript语句是否完整,如没写完可在下一行接着写。在某行连续三次按下回车键可取消未输入完成的命令,并退回到>-提示符。

2.5.2 MongoDB客户端

能运行任意JavaScript程序听上去很酷,不过shell的真正强大之处在于,它是一个独立的MongoDB客户端。启动时,shell会连到MongoDB服务器的test数据库,并将数据库连接赋值给全局变量db。这个变量是通过shell访问MongoDB的主要入口点。
如果想要查看db当前指向哪个数据库,可以使用db命令:

> db
test

为了方便习惯使用SQL shell的用户,shell还包含一些非JavaScript语法的扩展。这些扩展并不提供额外的功能,而是一些非常棒的语法糖。例如,最重要的操作之一为选择数据库:

> use foobar
switched to db foobar

现在,如果查看db变量,会发现其正指向foobar数据库:

> db
foobar

因为这是一个JavaScript shell,所以键入一个变量会将此变量的值转换为字符串(即数据库名)并打印出来。
通过db变量,可访问其中的集合。例如,通过db.baz可返回当前数据库的baz集合。因为通过shell可访问集合,这意味着,几乎所有数据库操作都可以通过shell完成。

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-24T21:12:09.982Z")
}

这个对象是个有效的MongoDB文档,所以可以用insert方法将其保存到blog集合中:

> db.blog.insert(post)

这篇文章已被存到数据库中。要查看它可用调用集合的find方法:

> db.blog.find()
{
    "_id" : ObjectId("5037ee4a1084eb3ffeef7228"),
    "title" : "My Blog Post",
    "content" : "Here"s my blog post.",
    "date" : ISODate("2012-08-24T21:12:09.982Z")
}

可以看到,我们曾输入的键/值对都已被完整地记录。此外,还有一个额外添加的键"_id"。"_id"突然出现的原因将在本章末尾解释。

2. 读取

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

> db.blog.findOne()
{
    "_id" : ObjectId("5037ee4a1084eb3ffeef7228"),
    "title" : "My Blog Post",
    "content" : "Here"s my blog post.",
    "date" : ISODate("2012-08-24T21:12:09.982Z")
}

find和findOne可以接受一个查询文档作为限定条件。这样就可以查询符合一定条件的文档。使用find时,shell会自动显示最多20个匹配的文档,也可获取更多文档。第4章会详细介绍查询相关的内容。

3. 更新

使用update修改博客文章。update接受(至少)两个参数:第一个是限定条件(用于匹配待更新的文档),第二个是新的文档。假设我们要为先前写的文章增加评论功能,就需要增加一个新的键,用于保存评论数组。
首先,修改变量post,增加"comments"键:

> post.comments = []
[ ]

然后执行update操作,用新版本的文档替换标题为“My Blog Post”的文章:

> db.blog.update({title : "My Blog Post"}, post)

现在,文档已经有了"comments"键。再用find查看一下,可以看到新的键:

> db.blog.find()
{
    "_id" : ObjectId("5037ee4a1084eb3ffeef7228"),
    "title" : "My Blog Post",
    "content" : "Here"s my blog post.",
    "date" : ISODate("2012-08-24T21:12:09.982Z"),
    "comments" : [ ]
}
4. 删除

使用remove方法可将文档从数据库中永久删除。如果没有使用任何参数,它会将集合内的所有文档全部删除。它可以接受一个作为限定条件的文档作为参数。例如,下面的命令会删除刚刚创建的文章:

> db.blog.remove({title : "My Blog Post"})

现在,集合又是空的了。

上一篇文章:MongoDB指南---1、MongoDB简介
下一篇文章:MongoDB指南---3、MongoDB基础知识-数据类型

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/43992.html

相关文章

  • MongoDB指南---2MongoDB基础知识-文档集合数据客户

    摘要:上一篇文章指南简介下一篇文章指南基础知识数据类型非常强大但很容易上手。把同种类型的文档放在一个集合里,数据会更加集中。命名集合使用名称进行标识。集合名不能是空字符串。简单起见,数据库名应全部小写。 上一篇文章:MongoDB指南---1、MongoDB简介下一篇文章:MongoDB指南---3、MongoDB基础知识-数据类型 MongoDB非常强大但很容易上手。本章会介绍一些Mon...

    W4n9Hu1 评论0 收藏0
  • MongoDB指南---3、MongoDB基础知识-数据类型

    摘要:如将构造函数作为函数进行调用即不包括的方式,返回的是日期的字符串表示,而非日期对象。如果不注意这一点,没有始终使用日期构造函数,将得到一堆混乱的日期对象和日期的字符串。关于日期类的完整解释,以及构造函数的参数格式,参见规范节。 上一篇文章:MongoDB指南---2、MongoDB基础知识-文档、集合、数据库、客户端下一篇文章:MongoDB指南---4、MongoDB基础知识-使用M...

    aervon 评论0 收藏0
  • MongoDB指南---3、MongoDB基础知识-数据类型

    摘要:如将构造函数作为函数进行调用即不包括的方式,返回的是日期的字符串表示,而非日期对象。如果不注意这一点,没有始终使用日期构造函数,将得到一堆混乱的日期对象和日期的字符串。关于日期类的完整解释,以及构造函数的参数格式,参见规范节。 上一篇文章:MongoDB指南---2、MongoDB基础知识-文档、集合、数据库、客户端下一篇文章:MongoDB指南---4、MongoDB基础知识-使用M...

    tunny 评论0 收藏0
  • MongoDB指南---1、MongoDB简介

    摘要:不采用关系模型主要是为了获得更好的扩展性。易于扩展应用程序数据集的大小正在以不可思议的速度增长。过去非常罕见的级别数据,现在已是司空见惯了。这种精简方式的设计是能够实现如此高性能的原因之一。下一篇文章指南基础知识文档集合数据库客户端 下一篇文章:MongoDB指南---2、MongoDB基础知识-文档、集合、数据库、客户端 MongoDB是一款强大、灵活,且易于扩展的通用型数据库。它...

    VPointer 评论0 收藏0
  • MongoDB指南---1、MongoDB简介

    摘要:不采用关系模型主要是为了获得更好的扩展性。易于扩展应用程序数据集的大小正在以不可思议的速度增长。过去非常罕见的级别数据,现在已是司空见惯了。这种精简方式的设计是能够实现如此高性能的原因之一。下一篇文章指南基础知识文档集合数据库客户端 下一篇文章:MongoDB指南---2、MongoDB基础知识-文档、集合、数据库、客户端 MongoDB是一款强大、灵活,且易于扩展的通用型数据库。它...

    guyan0319 评论0 收藏0

发表评论

0条评论

SnaiLiu

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<