第一章 - 基础知识
我们通过学习 MongoDB 的基本工作原理,开始我们的 MongoDB 之旅。当然,这是学习 MongoDB 的核心,它也能帮助我们回答诸如,MongoDB 适用于哪些场景这些更高层次的问题。
开始之前,这有六个简单的概念我们需要了解一下。
-
MongoDB中的
database
有着和你熟知的"数据库"一样的概念 (对 Oracle 来说就是 schema)。一个 MongoDB 实例中,可以有零个或多个数据库,每个都作为一个高等容器,用于存储数据。 -
数据库中可以有零个或多个
collections
(集合)。集合和传统意义上的table
基本一致,你可以简单的把两者看成是一样的东西。 -
集合是由零个或多个
documents
(文档)组成。同样,一个文档可以看成是一row
。 -
文档是由零个或多个
fields
(字段)组成。, 没错,它就是columns
。 -
Indexes
(索引)在 MongoDB 中扮演着和它们在 RDBMS 中一样的角色。 Cursors
(游标)和上面的五个概念都不一样,但是它非常重要,并且经常被忽视,因此我觉得它们值得单独讨论一下。其中最重要的你要理解的一点是,游标是,当你问 MongoDB 拿数据的时候,它会给你返回一个结果集的指针而不是真正的数据,这个指针我们叫它游标,我们可以拿游标做我们想做的任何事情,比如说计数或者跨行之类的,而无需把真正的数据拖下来,在真正的数据上操作。
综上,MongoDB 是由包含 collections
的 databases
组成的。而 collection
是由 documents
组成。每个 document
是由 fields
组成。 Collections
可以被 indexed
,以便提高查找和排序的性能。最后,当我们从 MongoDB 获取数据的时候,我们通过 cursor
来操作,读操作会被延迟到需要实际数据的时候才会执行。
那为什么我们需要新的术语(collection vs. table, document vs. row and field vs. column)?为了让看起来更复杂点?事实上,虽然这些概念和关系型数据中的概念类似,但是还是有差异的。核心差异在于,关系型数据库是在 table
上定义的columns
,而面向文档数据库是在 document
上定义的 fields
。也就是说,在 collection
中的每个 document
都可以有它自己独立的 fields
。因此,对于 collection
来说是个简化了的 table
,但是一个 document
却比一 row
有更多的信息。
虽然这些概念很重要,但是如果现在搞不明白也不要紧。多插几条数据就明白上面说的到底是什么意思了。反正,要点就是,集合不对存储内容严格限制 (所谓的无模式(schema-less))。字段由每个独立的文档进行跟踪处理。这样做的优点和缺点将在下面章节一一讨论。
好了我们开始吧。如果你还没有运行 MongoDB,那么快去运行 mongod
服务和开启 mongo shell。shell 用的是 JavaScript。你可以试试一些全局命令,比如 help
或者 exit
。如果要操作当前数据库,用 db
,比如 db.help()
或者db.stats()
。如果要操作指定集合,大多数情况下我们会操作集合而不是数据库,用 db.COLLECTION_NAME
,比如db.unicorns.help()
或者 db.unicorns.count()
。
我们继续,输入 db.help()
,就能拿到一个对 db
能执行的所有的命令的列表。
顺便说一句:因为这是一个 JavaScript shell,如果你输入的命令漏了 ()
,你会看到这个命令的源码,而不是执行这个命令。我提一下,是为了避免你执行漏了括号的命令,拿到一个以 function (...){
开头的返回的时候,觉得神奇不可思议。比如说,如果你输入 db.help
(不带括号), 你会看到 help
方法的内部实现。
首先我们用全局的 use
来切换数据库,继续,输入 use learn
。这个数据库实际存在与否完全没有关系。我们在里面生成集合的时候, learn
数据库会自动建起来。现在,我们在一个数据库里面了,你可以开始尝试一下数据库命令,比如db.getCollectionNames()
。执行之后,你会得到一个空数组 ([ ]
)。因为集合是无模式的,我们不需要特地去配置它。我们可以简单的插入一个文档到一个新的集合。像这样,我们用 insert
命令,在文档中插入:
db.unicorns.insert({name: 'Aurora',
gender: 'f', weight: 450})
这行命令对集合 unicorns
执行了 insert
命令,并传入一个参数。MongoDB 内部用二进制序列化 JSON 格式,称为 BSON。外部,也就是说我们多数情况应该用 JSON,就像上面的参数一样。然后我们执行 db.getCollectionNames()
,我们将能拿到两个集合: unicorns
和 system.indexes
。在每个数据库中都会有一个 system.indexes
集合,用来保存我们数据的的索引信息。
你现在可以对用 unicorns
执行 find
命令,然后返回文档列表:
db.unicorns.find()
请注意,除你指定的字段之外,会多出一个 _id
字段。每个文档都会有一个唯一 _id
字段。你可以自己生成一个,或者让 MongoDB 帮你生成一个 ObjectId
类型的。多数情况下,你会乐意让 MongoDB 帮你生成的。默认的 _id
字段是已被索引的 - 这就说明了为什么会有 system.indexes
集合。你可以看看 system.indexes
:
db.system.indexes.find()
你可以看到索引的名字,被索引的数据库和集合,以及在索引中的字段。
现在,回到我们关于数组无模式的讨论中来。往 unicorns
插入一个完全不同的文档,比如:
db.unicorns.insert({name: 'Leto',
gender: 'm',
home: 'Arrakeen',
worm: false})
然后,再用 find
列出文档。等我们理解再深入一点的时候,将会讨论一下 MongoDB 的有趣行为。到这里,我希望你开始理解,为什么那些传统的术语在这里不适用了。
掌握选择器(Selector)
除了我们介绍过的六个概念,在开始讨论更深入的话题之前,MongoDB 还有一个应该掌握的实用概念:查询选择器。MongoDB 的查询选择器就像 SQL 语句里面的 where
一样。因此,你会在对集合的文档做查找,计数,更新,删除的时候用到它。选择器是一个 JSON 对象,最简单的是就是用 {}
匹配所有的文档。如果我们想找出所有母独角兽,我们可以用 {gender:'f'}
。
开始深入学习选择器之前,让我们先做些准备。首先,把刚才我们插入 unicorns
集合的数据删除,通过:db.unicorns.remove({})
。现在,再插入一些用来演示的数据 (你不会手打吧):
db.unicorns.insert({name: 'Horny',
dob: new Date(1992,2,13,7,47),
loves: ['carrot','papaya'],
weight: 600,
gender: 'm',
vampires: 63});
db.unicorns.insert({name: 'Aurora',
dob: new Date(1991, 0, 24, 13, 0),
loves: ['carrot', 'grape'],
weight: 450,
gender: 'f',
vampires: 43});
db.unicorns.insert({name: 'Unicrom',
dob: new Date(1973, 1, 9, 22, 10),
loves: ['energon', 'redbull'],
weight: 984,
gender: 'm',
vampires: 182});
db.unicorns.insert({name: 'Roooooodles',
dob: new Date(1979, 7, 18, 18, 44),
loves: ['apple'],
weight: 575,
gender: 'm',
vampires: 99});
db.unicorns.insert({name: 'Solnara',
dob: new Date(1985, 6, 4, 2, 1),
loves:['apple', 'carrot',
'chocolate'],
weight:550,
gender:'f',
vampires:80});
db.unicorns.insert({name:'Ayna',
dob: new Date(1998, 2, 7, 8, 30),
loves: ['strawberry', 'lemon'],
weight: 733,
gender: 'f',
vampires: 40});
db.unicorns.insert({name:'Kenny',
dob: new Date(1997, 6, 1, 10, 42),
loves: ['grape', 'lemon'],
weight: 690,
gender: 'm',
vampires: 39});
db.unicorns.insert({name: 'Raleigh',
dob: new Date(2005, 4, 3, 0, 57),
loves: ['apple', 'sugar'],
weight: 421,
gender: 'm',
vampires: 2});
db.unicorns.insert({name: 'Leia',
dob: new Date(2001, 9, 8, 14, 53),
loves: ['apple', 'watermelon'],
weight: 601,
gender: 'f',
vampires: 33});
db.unicorns.insert({name: 'Pilot',
dob: new Date(1997, 2, 1, 5, 3),
loves: ['apple', 'watermelon'],
weight: 650,
gender: 'm',
vampires: 54});
db.unicorns.insert({name: 'Nimue',
dob: new Date(1999, 11, 20, 16, 15),
loves: ['grape', 'carrot'],
weight: 540,
gender: 'f'});
db.unicorns.insert({name: 'Dunx',
dob: new Date(1976, 6, 18, 18, 18),
loves: ['grape', 'watermelon'],
weight: 704,
gender: 'm',
vampires: 165});
现在我们有数据了,我们可以开始来学习掌握选择器了。{field: value}
用来查找那些 field
的值等于 value
的文档。 {field1: value1, field2: value2}
相当于 and
查询。还有 $lt
, $lte
, $gt
, $gte
和 $ne
被用来处理 小于,小于等于,大于,大于等于,和不等于操作。比如,获取所有体重大于700磅的公独角兽,我们可以这样:
db.unicorns.find({gender: 'm',
weight: {$gt: 700}})
//or (not quite the same thing, but for
//demonstration purposes)
db.unicorns.find({gender: {$ne: 'f'},
weight: {$gte: 701}})
$exists
用来匹配字段是否存在,比如:
db.unicorns.find({
vampires: {$exists: false}})
会返回一条文档。'$in' 被用来匹配查询文档在我们传入的数组参数中是否存在匹配值,比如:
db.unicorns.find({
loves: {$in:['apple','orange']}})
会返回那些喜欢 apple
或者 orange
的独角兽。
如果我们想要 OR 而不是 AND 来处理选择条件的话,我们可以用 $or
操作符,再给它一个我们要匹配的数组:
db.unicorns.find({gender: 'f',
$or: [{loves: 'apple'},
{weight: {$lt: 500}}]})
上面的查询会返回那些喜欢 apples
或者 weigh
小于500磅的母独角兽。
在我们最后两个例子里面有个非常赞的特性。你应该已经注意到了,loves
字段是个数组。MongoDB 允许数组作为基本对象(first class objects)处理。这是个令人难以置信的超赞特性。一旦你开始用它,你都不知道没了它你怎么活下去了。最有趣的是,基于数组的查询变得非常简单: {loves: 'watermelon'}
会把文档中 loves
中有 watermelon
的值全部查询出来。
除了我们介绍的这些,还有更多可用的操作。所有这些都记载在 MongoDB 手册上的 Query Selectors 这一章。我们介绍的仅仅是那些你学习时所需要用到的,同时也是你最经常用到的操作。
我们已经学习了选择器是如何配合 find
命令使用的了。还大致介绍了一下如何配合 remove
命令使用,count
命令虽然没介绍,不过你肯定知道应该怎么做,而 update
命令,之后我们会花多点时间来详细学习它。
MongoDB 为我们的 _id
字段生成的 ObjectId
可以这样查询:
db.unicorns.find(
{_id: ObjectId("TheObjectId")})
小结
我们还没有看到 update
, 或是能拿来做更华丽事情的 find
。不过,我们已经安装好 MongoDB 并运行起来了, 简略的介绍了一下 insert
和 remove
命令 (完整版也没比我们介绍的多什么)。 我们还介绍了 find
以及了解了 MongoDBselectors
是怎么一回事。 我们起了个很好的头,并为以后的学习奠定了坚实基础。 信不信由你,其实你已经掌握了学习 MongoDB 所必须的大多数知识 - 它真的是易学易用。 我强烈建议你在继续学习之前在本机上多试试多玩玩。 插入不同的文档,可以试试看在不同的集合中,习惯一下使用不同的选择器。试试 find
, count
和 remove
。 多试几次之后,你会发现原来看起来那么格格不入的东西,用起来居然水到渠成。
更多建议: