离线下载
PDF版 ePub版

geminiyellow · 更新于 2017-11-22 20:00:54

基础知识

我们通过学习 MongoDB 的基本工作原理,开始我们的 MongoDB 之旅。当然,这是学习 MongoDB 的核心,它也能帮助我们回答诸如,MongoDB 适用于哪些场景这些更高层次的问题。

开始之前,这有六个简单的概念我们需要了解一下。

  1. MongoDB中的 database 有着和你熟知的"数据库"一样的概念 (对 Oracle 来说就是 schema)。一个 MongoDB 实例中,可以有零个或多个数据库,每个都作为一个高等容器,用于存储数据。

  2. 数据库中可以有零个或多个 collections (集合)。集合和传统意义上的 table 基本一致,你可以简单的把两者看成是一样的东西。

  3. 集合是由零个或多个 documents (文档)组成。同样,一个文档可以看成是一 row

  4. 文档是由零个或多个 fields (字段)组成。, 没错,它就是 columns

  5. Indexes (索引)在 MongoDB 中扮演着和它们在 RDBMS 中一样的角色。

  6. Cursors (游标)和上面的五个概念都不一样,但是它非常重要,并且经常被忽视,因此我觉得它们值得单独讨论一下。其中最重要的你要理解的一点是,游标是,当你问 MongoDB 拿数据的时候,它会给你返回一个结果集的指针而不是真正的数据,这个指针我们叫它游标,我们可以拿游标做我们想做的任何事情,比如说计数或者跨行之类的,而无需把真正的数据拖下来,在真正的数据上操作。

综上,MongoDB 是由包含 collectionsdatabases 组成的。而 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() ,我们将能拿到两个集合: unicornssystem.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 并运行起来了, 简略的介绍了一下 insertremove 命令 (完整版也没比我们介绍的多什么)。 我们还介绍了 find 以及了解了 MongoDB selectors 是怎么一回事。 我们起了个很好的头,并为以后的学习奠定了坚实基础。 信不信由你,其实你已经掌握了学习 MongoDB 所必须的大多数知识 - 它真的是易学易用。 我强烈建议你在继续学习之前在本机上多试试多玩玩。 插入不同的文档,可以试试看在不同的集合中,习惯一下使用不同的选择器。试试 find, countremove。 多试几次之后,你会发现原来看起来那么格格不入的东西,用起来居然水到渠成。

上一篇: 简介 下一篇: 更新