七个数据库,七个世界-第五章 MongoDB 第一天 1/2

首先


所以最终选择了MongoDB。为什么选择MongoDB呢?主要原因有以下几点:首先,MongoDB是一种NoSQL数据库,适用于海量数据的处理。其次,MongoDB具有极高的灵活性和扩展性,可以轻松应对未来的需求变化。最后,MongoDB具备强大的查询和索引功能,能够满足我们对日志数据的高效分析和利用需求。因此,我决定从本章开始学习MongoDB。

    • Redis => ハッシュの探索が高速で、簡単に分散可能(らしい)。

 

    • Riak => Redisと同じく、ハッシュの探索が高速。高可用性。Erlangで作られている。

 

    • HBase => ビッグデータに強し。列指向ってのが難しそうだし、どうせならHadoopと一緒に使いたい。

 

    MongoDB => データへの問い合わせが簡単。ただ、速度がイマイチらしい。JSONで使用可能。

考虑到这些数据库并为了种种原因,决定采用MongoDB。因为它可以保存为JSON格式,所以我们可以考虑使用D3.js进行数据可视化。那么,让我们立刻开始吧。

简而言之


据说MongoDB于2009年发布。我感到惊讶,因为它很新颖。据说它结合了强大的关系型数据库查询能力和分布式数据存储(如Riak和HBase)的优点。这是个好消息。它没有结构化模式,所以可以根据数据模型进行增长和变更,并且具备水平扩展能力。

安装步骤


似乎可以通过brew轻松实现。

brew install mongodb
mkdir mongodb
mkdir /mongodb/db
mkdir /mongodb/log

顺便说一下,我创建了mongod用于保存数据的目录和用于日志的目录。现在需要注册这个操作目录的路径。
首先,启动Mongo服务,并使用–dbpath选项注册路径。

mongod --dbpath ~/Documents/mongodb/db

在这种状态下,访问http://localhost:27017,可以确认启动。要结束,可以按Ctrl+C。
通常情况下,可以在后台启动并保存日志,可以通过选项进行添加。

$ mongod --dbpath ~/Documents/mongodb/db --logpath ~/Documents/mongodb/log/mongodb.log &
$ ps u
$ cat log/mongodb.log
$ kill -KILL プロセスID

您可以通过使用”ps u”命令来确认是否已经启动。通过查看日志文件,可以确认日志文件”mongodb.log”中是否积累了日志信息。要停止以后台方式运行的进程,您只需使用”kill”命令,并输入您想要停止的进程的PID(进程ID)。–很多人喜欢使用”–fork”选项来启动,但我认为只需在命令后加上”&”符号更方便。

第一天: “CRUD和嵌套”


命令行很有趣


让我们创建一个名为”book”的新数据库。首先启动mongod,然后执行以下命令。

$ cd db
$ mongo book
MongoDB shell version: 2.6.7
connecting to: book
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
    http://docs.mongodb.org/
Questions? Try the support group
    http://groups.google.com/group/mongodb-user
Server has startup warnings: 
2015-09-17T22:20:25.531+0900 [initandlisten] 
2015-09-17T22:20:25.531+0900 [initandlisten] ** WARNING: soft rlimits too low. Number of files is 256, should be at least 1000

一启动,首先在控制台输入help,然后会被告知输入help以获得帮助。
看了help之后就能知道,要查看数据库列表,需要输入show dbs。要切换数据库,需要使用use命令。
在Mongo中创建集合(类似Riak中的bucket),只需要向集合中添加第一条记录即可。Mongo是无模式的,所以不需要事先定义任何内容。实际上,在添加了值之前,book数据库也是不存在的。下面的代码是用来创建/插入集合towns的。

> db.towns.insert({
... name: "New York",
... papulation: 22200000,
... last_census: ISODate("2009-07-31"),
... famous_for: [ "statue of library", "food" ],
... mayor : {
... name : "Michael Bloomberg",
... party : "I"
... }
... })
WriteResult({ "nInserted" : 1 })

在前一节中,解释了文档是以JSON(严格来说是BSON)格式存在的。所以,我们可以尝试添加一个新的JSON格式的文档。花括号{…}表示由键值对构成的对象(也称为哈希表或映射)。方括号[…]表示一个数组。这些值可以嵌套到任意深度。
通过使用”show collections”命令,可以查看已存在的集合。

> show collections
system.indexes
towns

towns 是之前创建的,但 system.indexes 则始终存在。要将集合的内容列出,可以执行 find() 命令。

> db.towns.find()
{ "_id" : ObjectId("55fabee041ad5a22d4d4d977"), "name" : "New York", "papulation" : 22200000, "last_census" : ISODate("2009-07-31T00:00:00Z"), "famous_for" : [ "statue of library", "food" ], "mayor" : { "name" : "Michael Bloomberg", "party" : "I" } }

与关系型数据库不同的是,Mongo不支持在服务器端进行连接。在一次JavaScript调用中,可以读取文档和嵌套的内容。
新插入的城市的JSON输出中包含ObjectId的_id字段。这与PostgreSQL的数值类型主键的SERIAL自动递增相同。ObjectId由时间戳、客户端机器ID、客户端进程ID和3字节增量计数器组成,总共12字节。
这种自动编号机制的好处在于,所有机器进程都可以在不与其他Mongod实例重复的情况下生成ID。这种设计选择表明了Mongo的分布式性质。

JavaScript –
JavaScript (JavaScript)

Mongo使用JavaScript来支持各种功能,从复杂的mapreduce到简单的帮助工具。

> db.help()
> db.towns.help()

这些指令列出了给定对象的功能。db是JavaScript对象,包含有关当前数据库的信息。db.x是表示名为x的集合的JavaScript对象。这些指令只是JavaScript函数而已。

> typeof db
object
> typeof db.towns
object
> typeof db.towns.insert
function

想要查看函数的源代码,可以不带参数和括号调用函数(可以参考Python,它更接近Ruby)。

> db.towns.insert
function ( obj , options, _allow_dot ){
    if ( ! obj )
        throw "no object passed to insert!";

    var flags = 0;

    var wc = undefined;
    var allowDottedFields = false;
    if ( options === undefined ) {
        // do nothing
    }
    else if ( typeof(options) == 'object' ) {
        if (options.ordered === undefined) {
            //do nothing, like above
        } else {
            flags = options.ordered ? 0 : 1;
        }

        if (options.writeConcern)
            wc = options.writeConcern;
        if (options.allowdotted)
            allowDottedFields = true;
    } else {
        flags = options;
    }

    // 1 = continueOnError, which is synonymous with unordered in the write commands/bulk-api
    var ordered = ((flags & 1) == 0);

    if (!wc)
        wc = this.getWriteConcern();

    var result = undefined;
    var startTime = (typeof(_verboseShell) === 'undefined' ||
                     !_verboseShell) ? 0 : new Date().getTime();

    if ( this.getMongo().writeMode() != "legacy" ) {
        // Bit 1 of option flag is continueOnError. Bit 0 (stop on error) is the default.
        var bulk = ordered ? this.initializeOrderedBulkOp() : this.initializeUnorderedBulkOp();
        var isMultiInsert = Array.isArray(obj);

        if (isMultiInsert) {
            obj.forEach(function(doc) {
                bulk.insert(doc);
            });
        }
        else {
            bulk.insert(obj);
        }

        try {
            result = bulk.execute(wc);
            if (!isMultiInsert)
                result = result.toSingleResult();
        }
        catch( ex ) {
            if ( ex instanceof BulkWriteError ) {
                result = isMultiInsert ? ex.toResult() : ex.toSingleResult();
            }
            else if ( ex instanceof WriteCommandError ) {
                result = isMultiInsert ? ex : ex.toSingleResult();
            }
            else {
                // Other exceptions thrown
                throw ex;
            }
        }
    }
    else {
        if ( ! _allow_dot ) {
            this._validateForStorage( obj );
        }

        if ( typeof( obj._id ) == "undefined" && ! Array.isArray( obj ) ){
            var tmp = obj; // don't want to modify input
            obj = {_id: new ObjectId()};
            for (var key in tmp){
                obj[key] = tmp[key];
            }
        }

        this.getMongo().insert( this._fullName , obj, flags );

        // enforce write concern, if required
        if (wc)
            result = this.runCommand("getLastError", wc instanceof WriteConcern ? wc.toJSON() : wc);
    }

    this._lastID = obj._id;
    this._printExtraInfo("Inserted", startTime);
    return result;
}

那么,我现在要离开shell,创建一个用于将文档添加到towns集合的JavaScript函数。文件夹结构是/mongo/js/js文件夹下。

function insertCity(
    name, population, last_census,
    famous_for, mayor_info
) {
    db.towns.insert({
        name : name,
        population: population,
        last_census : ISODate( last_census ),
        famous_for : famous_for,
        mayor : mayor_info
    });
}

如果把这段代码粘贴到shell中,就可以调用它了。本书只有这一段解释,但是要注意的是,必须加载insert_city.js才能在book中使用该函数。实际操作如下。

$ mongo book ~/Documents/mongodb/js/insert_city.js --shell
MongoDB shell version: 2.6.7
connecting to: book
type "help" for help
Server has startup warnings: 
2015-09-17T22:20:25.531+0900 [initandlisten] 
2015-09-17T22:20:25.531+0900 [initandlisten] ** WARNING: soft rlimits too low. Number of files is 256, should be at least 1000
>

这样一来,insert_city.js和book被连接在一起了。通过添加–shell选项,可以直接使用函数。

> insertCity("Punxsutawney", 6200, '2008-01-31', ["phil the groundhog"], { name : "Jim Wehrle" } )
> insertCity("Portland", 582000, '2007-09-20', ["beer", "food"], { name : "Sam Adams", party : "D" } )
> db.towns.find()
{ "_id" : ObjectId("55fabee041ad5a22d4d4d977"), "name" : "New York", "papulation" : 22200000, "last_census" : ISODate("2009-07-31T00:00:00Z"), "famous_for" : [ "statue of library", "food" ], "mayor" : { "name" : "Michael Bloomberg", "party" : "I" } }
{ "_id" : ObjectId("55fac70993b946919b1619ea"), "name" : "Punxsutawney", "population" : 6200, "last_census" : ISODate("2008-01-31T00:00:00Z"), "famous_for" : [ "phil the groundhog" ], "mayor" : { "name" : "Jim Wehrle" } }
{ "_id" : ObjectId("55fac75793b946919b1619eb"), "name" : "Portland", "population" : 582000, "last_census" : ISODate("2007-09-20T00:00:00Z"), "famous_for" : [ "beer", "food" ], "mayor" : { "name" : "Sam Adams", "party" : "D" } }

现在,这个集合中已经有三个towns。你可以通过之前提到的db.towns.find()来验证这个结果。

在Mongo中更加有趣。


通过调用find()函数且不带参数,可以获取所有文档。要访问特定的文档,需要设置_id属性。由于_id是ObjectId类型,因此需要通过ObjectId(str)将用于查询的字符串转换为ObjectId类型。

> db.towns.find({ "_id" : ObjectId("55fac70993b946919b1619ea") })
{ "_id" : ObjectId("55fac70993b946919b1619ea"), "name" : "Punxsutawney", "population" : 6200, "last_census" : ISODate("2008-01-31T00:00:00Z"), "famous_for" : [ "phil the groundhog" ], "mayor" : { "name" : "Jim Wehrle" } }

find()函数有一个可选的第二个参数,用于筛选要读取的字段。例如,如果需要城市的名称,则可以传递将name设为1(或true),同时与_id一起使用的参数。

> db.towns.find({ "_id" : ObjectId("55fac70993b946919b1619ea") }, { name : 1 })
{ "_id" : ObjectId("55fac70993b946919b1619ea"), "name" : "Punxsutawney" }

相反地,當讀取除了name以外的所有字段時,將name設置為0(或false或null)。

> db.towns.find({ "_id" : ObjectId("55fac70993b946919b1619ea") }, { name : 0 })
{ "_id" : ObjectId("55fac70993b946919b1619ea"), "population" : 6200, "last_census" : ISODate("2008-01-31T00:00:00Z"), "famous_for" : [ "phil the groundhog" ], "mayor" : { "name" : "Jim Wehrle" } }

似乎可以通过组合字段值、范围和条件来创建任意查询(这与PostgreSQL相同)。例如,要查找首字母以“P”开头且人口少于10,000的城镇,可以使用Perl兼容的正则表达式(PCRE)和范围运算符。

> db.towns.find(
... { name : /^P/, population : { $lt : 10000 } },
... { name : 1, population : 1 }
... )
{ "_id" : ObjectId("55fac70993b946919b1619ea"), "name" : "Punxsutawney", "population" : 6200 }

Mongo的条件运算符遵循field: {$op: value}的格式。$op似乎是代表$ne(不等)等运算符。我可能想使用更简洁的语法,例如field < value,但这是JavaScript代码的特征。
使用JavaScript作为查询语言的优点是可以自己创建运算符。我尝试创建一个条件运算符,以表示“人口大于1万人且小于100万人”。

> var population_range = {}
> population_range['$lt'] = 1000000
1000000
> population_range['$gt'] = 10000
10000
> db.towns.find( { name : /^P/, population : population_range }, { name : 1 } )
{ "_id" : ObjectId("55fac75793b946919b1619eb"), "name" : "Portland" }

而且,不仅可以读取数字范围,也可以读取日期范围。例如,可以搜索在2008年1月31日之前的所有name,其中”last_census”是一个例子。

> db.towns.find(
... { last_census : { $lte : ISODate('2008-01-31') } },
... { _id : 0, name : 1 }
... )
{ "name" : "Punxsutawney" }
{ "name" : "Portland" }

在这个例子中,还要注意到将_id字段设置为0并且没有显示。

最后一句话


因为启动方法等安装方法部分容易被忘记,所以相当重要。

因为发现了一本叫做MongoDB的薄书的PDF,所以我想试着读一读。

由于MongoDB一天的内容很长,所以我分成了两半。明天继续读。

请阅读以上内容。


    • MacにMongoDBを入れ直した。brew使わずにインストールしてみたメモ – Qiita

 

    • 忘れがちな記憶へ mongoの起動方法

 

    mongoインタラクティブシェルの使い方メモ – ペイパー・プログラマーズ・ダイアリー
广告
将在 10 秒后关闭
bannerAds