MongoDB指南---三、MongoDB基础知识-数据类型

上一篇文章: MongoDB指南---二、MongoDB基础知识-文档、集合、数据库、客户端
下一篇文章: MongoDB指南---四、MongoDB基础知识-使用MongoDB Shell

本章开始部分介绍了文档的基本概念,如今你已经会启动、运行MongoDB,也会在shell中进行一些操做了。这一节的内容会更加深刻。MongoDB支持将多种数据类型做为文档中的值,下面将一一介绍。正则表达式

2.6.1 基本数据类型

在概念上,MongoDB的文档与JavaScript中的对象相近,于是可认为它相似于JSON。JSON(http://www.json.org)是一种简单的数据表示方式:其规范仅用一段文字就能描述清楚(其官网证实了这点),且仅包含6种数据类型。这样有不少好处:易于理解、易于解析、易于记忆。然而,从另外一方面来讲,由于只有null、布尔、数字、字符串、数组和对象这几种数据类型,因此JSON的表达能力有必定的局限。
虽然JSON具有的这些类型已具备很强的表现力,但绝大多数应用(尤为是在与数据库打交道时)都还须要其余一些重要的类型。例如,JSON没有日期类型,这使本来容易的日期处理变得烦人。另外,JSON只有一种数字类型,没法区分浮点数和整数,更别说区分32位和64位数字了。再者,JSON没法表示其余一些通用类型,如正则表达式或函数。
MongoDB在保留JSON基本键/值对特性的基础上,添加了其余一些数据类型。 在不一样的编程语言下,这些类型的确切表示有些许差别。下面说明MongoDB支持的其余通用类型,以及如何在文档中使用它们。shell

null

null用于表示空值或者不存在的字段:数据库

{"x" : null}

布尔型

布尔类型有两个值true和false:编程

{"x" : true}

数值

shell默认使用64位浮点型数值。所以,如下数值在shell中是很“正常”的:json

{"x" : 3.14}

或:segmentfault

{"x" : 3}

对于整型值,可以使用NumberInt类(表示4字节带符号整数)或NumberLong类(表示8字符带符号整数),分别举例以下:数组

{"x" : NumberInt("3")}
{"x" : NumberLong("3")}

字符串

UTF-8字符串均可表示为字符串类型的数据:服务器

{"x" : "foobar"}

日期

日期被存储为自新纪元以来通过的毫秒数,不存储时区:并发

{"x" : new Date()}

正则表达式

查询时,使用正则表达式做为限定条件,语法也与JavaScript的正则表达式语法相同:ecmascript

{"x" : /foobar/i}

数组

数据列表或数据集能够表示为数组:

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

内嵌文档

文档可嵌套其余文档,被嵌套的文档做为父文档的值:

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

对象id

对象id是一个12字节的ID,是文档的惟一标识。详见2.6.5节。

{"x" : ObjectId()}

还有一些不那么经常使用,但可能有须要的类型,包括下面这些。

二进制数据

二进制数据是一个任意字节的字符串。它不能直接在shell中使用。若是要将非UTF-8字符保存到数据库中,二进制数据是惟一的方式。

代码

查询和文档中能够包括任意JavaScript代码:

{"x" : function() { /* ... */ }}

另外,有几种大多数状况下仅在内部使用(或被其余类型取代)的类型。在本书中,出现这种状况时会特别说明。
关于MongoDB数据格式的更多信息,参考附录B。

2.6.2 日期

在JavaScript中,Date类能够用做MongoDB的日期类型。建立日期对象时,应使用new Date(…),而非Date(…)。如将构造函数(constructor)做为函数进行调用(即不包括new的方式),返回的是日期的字符串表示,而非日期(Date)对象。这个结果与MongoDB无关,是JavaScript的工做机制决定的。若是不注意这一点,没有始终使用日期(Date)构造函数,将获得一堆混乱的日期对象和日期的字符串。因为日期和字符串之间没法匹配,因此执行删除、更新及查询等几乎全部操做时会致使不少问题。
关于JavaScript日期类的完整解释,以及构造函数的参数格式,参见ECMAScript规范15.9节(http://www.ecmascript.org)。
shell根据本地时区设置显示日期对象。然而,数据库中存储的日期仅为新纪元以来的毫秒数,并未存储对应的时区。(固然,可将时区信息存储为另外一个键的值)。

2.6.3 数组

数组是一组值,它既能做为有序对象(如列表、栈或队列),也能做为无序对象(如数据集)来操做。
在下面的文档中,"things"这个键的值是一个数组:

{"things" : ["pie", 3.14]}

此例表示,数组可包含不一样数据类型的元素(在此,是一个字符串和一个浮点数)。实际上,常规的键/值对支持的全部值均可以做为数组的值,数组中甚至能够套嵌数组。
文档中的数组有个奇妙的特性,就是MongoDB能“理解”其结构,并知道如何“深刻”数组内部对其内容进行操做。这样就能使用数组内容对数组进行查询和构建索引了。例如,以前的例子中,MongoDB能够查询出"things"数组中包含3.14这个元素的全部文档。要是常用这个查询,能够对"things"建立索引来提升性能。
MongoDB可使用原子更新对数组内容进行修改,好比深刻数组内部将pie改成pi。本书后面还会介绍更多这种操做的例子。

2.6.4 内嵌文档

文档能够做为键的值,这样的文档就是内嵌文档。使用内嵌文档,可使数据组织方式更加天然,不用非得存成扁平结构的键/值对。
例如,用一个文档来表示一我的,同时还要保存他的地址,能够将地址信息保存在内嵌的"address"文档中:

{
    "name" : "John Doe",
    "address" : {
        "street" : "123 Park Street",
        "city" : "Anytown",
        "state" : "NY"
    }
}

上面例子中"address"键的值是一个内嵌文档,这个文档有本身的"street"、"city"和"state"键以及对应的值。
同数组同样,MongoDB可以“理解”内嵌文档的结构,并能“深刻”其中构建索引、执行查询或者更新。
稍后会深刻讨论模式设计,可是从这个简单的例子也能够看得出内嵌文档能够改变处理数据的方式。在关系型数据库中,这个例子中的文档通常会被拆分红两个表中的两个行 (“people”和“address”各一行)。在MongoDB中,就能够直接将地址文档嵌入到人员文档中。使用得当的话,内嵌文档会使信息的表示方式更加天然(一般也会更高效)。
MongoDB这样作的坏处就是会致使更多的数据重复。假设“address”是关系数据库中的一个独立的表,咱们须要修正地址中的拼写错误。当咱们对“people”和“address”执行链接操做时,使用这个地址的每一个人的信息都会获得更新。可是在MongoDB中,则须要对每一个人的文档分别修正拼写错误。

2.6.5 _id和ObjectId

MongoDB中存储的文档必须有一个"_id"键。这个键的值能够是任何类型的,默认是个ObjectId对象。在一个集合里面,每一个文档都有惟一的"_id",确保集合里面每一个文档都能被惟一标识。若是有两个集合的话,两个集合能够都有一个"_id"的值为123,可是每一个集合里面只能有一个文档的"_id"值为123。

1. ObjectId

ObjectId是"_id"的默认类型。它设计成轻量型的,不一样的机器都能用全局惟一的同种方法方便地生成它。这是 MongoDB采用ObjectId,而不是其余比较常规的作法(好比自动增长的主键)的主要缘由,由于在多个服务器上同步自动增长主键值既费力又费时。由于设计MongoDB的初衷就是用做分布式数据库,因此可以在分片环境中生成惟一的标示符很是重要。
ObjectId使用12字节的存储空间,是一个由24个十六进制数字组成的字符串(每一个字节能够存储两个十六进制数字)。因为看起来很长,很多人会以为难以处理。但关键是要知道这个长长的ObjectId是实际存储数据的两倍长。
若是快速连续建立多个ObjectId,会发现每次只有最后几位数字有变化。另外,中间的几位数字也会变化(要是在建立的过程当中停顿几秒钟)。这是ObjectId的建立方式致使的。ObjectId的12字节按照以下方式生成:
267f3406f5ff12e5d569add926b9dcd7.png

ObjectId的前4个字节是从标准纪元开始的时间戳,单位为秒。这会带来一些有用的属性。

  • 时间戳,与随后的5字节(稍后介绍)组合起来,提供了秒级别的惟一性。
  • 因为时间戳在前,这意味着ObjectId大体会按照插入的顺序排列。这对于某些方面颇有用,好比能够将其做为索引提升效率,可是这个是没有保证的,仅仅是“大体”。
  • 这4字节也隐含了文档建立的时间。绝大多数驱动程序都会提供一个方法,用于从ObjectId获取这些信息。

由于使用的是当前时间,不少用户担忧要对服务器进行时钟同步。虽然在某些状况下,在服务器间进行时间同步确实是个好主意(参见23.6.1节),可是这里其实没有必要,由于时间戳的实际值并不重要,只要它老是不停增长就行了(每秒一次)。
接下来的3字节是所在主机的惟一标识符。一般是机器主机名的散列值(hash)。这样就能够确保不一样主机生成不一样的 ObjectId,不产生冲突。
为了确保在同一台机器上并发的多个进程产生的ObjectId是惟一的,接下来的两字节来自产生ObjectId的进程的进程标识符(PID)。
前9字节保证了同一秒钟不一样机器不一样进程产生的ObjectId是惟一的。最后3字节是一个自动增长的计数器,确保相同进程同一秒产生的ObjectId也是不同的。一秒钟最多容许每一个进程拥有
16 777 216个不一样的ObjectId。

clipboard.png

2. 自动生成_id

前面讲到,若是插入文档时没有"_id"键,系统会自动帮你建立一个。能够由MongoDB服务器来作这件事,但一般会在客户端由驱动程序完成。这一作法很是好地体现了MongoDB的哲学:能交给客户端驱动程序来作的事情就不要交给服务器来作。这种理念背后的缘由是,即使是像MongoDB这样扩展性很是好的数据库,扩展应用层也要比扩展数据库层容易得多。将工做交由客户端来处理,就减轻了数据库扩展的负担。

上一篇文章: MongoDB指南---二、MongoDB基础知识-文档、集合、数据库、客户端
下一篇文章: MongoDB指南---四、MongoDB基础知识-使用MongoDB Shell
相关文章
相关标签/搜索