ツナワタリマイライフ

日常ネタから技術ネタ、音楽ネタまで何でも書きます。

ドキュメント指向データベース「mongodb」を使ってみる

はじめに

node.jsの勉強中、データベースとの接続のところで定番のRDBMSであるmysqlのほか、mongodbという別の種類のデータベースとの接続方法があった。読んでる本はこれ。

今回はnode.jsから使う前に、mongodb単体で基本的なデータの作成、更新、削除操作を試す。

mongodb

ドキュメント指向データベース

RDBMSのように1つのテーブルにレコードを挿入していくタイプではない、NoSQLという分類になる。SQLクエリを持たないデータベースという解釈でいいと思う。

このドキュメント指向データベースは、JSONライクなBSON形式の構造をオブジェクトとして保存する。

ドキュメントとコレクション

RDBMSでいうテーブルの概念がドキュメント。レコードにあたるのがコレクション。このコレクションが先ほど言ったJSONライクな表現で格納できる。

インストール

公式サイトからダウンロードする。

www.mongodb.org

最新の安定版のtgzファイルをダウンロード。展開して任意の場所に置いて/binをパスを通す。

MacBook-Air:node take$ mongodb/bin/mongo --version
MongoDB shell version: 3.2.1

起動

mongodb/bin mongod

これでデフォルトポート27013で待ち受けする。

データベースを作る

sampledbという名前のデータベースを作成する。既存のデータベースに接続することも同様。mongoシェルにログインできる。

acBook-Air:node take$ mongodb/bin/mongo sampledb
MongoDB shell version: 3.2.1
connecting to: sampledb
Server has startup warnings: 
2016-02-04T05:55:03.346+0900 I CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2016-02-04T05:55:03.346+0900 I CONTROL  [initandlisten] 
2016-02-04T05:55:03.346+0900 I CONTROL  [initandlisten] 
2016-02-04T05:55:03.346+0900 I CONTROL  [initandlisten] ** WARNING: soft rlimits too low. Number of files is 256, should be at least 1000

ない場合は自動で作成される。素敵!

ドキュメントを作成する

topic1というドキュメントを作成する。内容はjson形式で、topicIdとtitleの2つの要素を持っている。これは自由に階層化できる。

> var topic1 = {
... topicId: 1,title: 'this is topic1'}

ドキュメントをコレクションに挿入する

topicsというコレクションにtopic1というドキュメントを挿入する。コレクションがなければこれまた自動作成される。素敵!

> db.topics.insert(topic1);
WriteResult({ "nInserted" : 1 })

コレクションを検索する

コレクション.findで検索できる。queryを指定しないと全てのドキュメントを表示する。

> db.topics.find();
{ "_id" : ObjectId("56b2710887587c5673ca8d0b"), "topicId" : 1, "title" : "this is topic1" }

データを更新する

新規にtopic2というドキュメントを作成し、これをupdateで変更してみましょう。引数には、検索対象を特定するクエリ、更新後の値を含むオブジェクトを指定します。

# topic2というドキュメントを作成
> var topic2 = { topicId: 2,title: 'this is topic2',user: 'take'}

# コレクションに挿入
> db.topics.insert(topic2);
WriteResult({ "nInserted" : 1 })

# 全てのコレクションを検索
> db.topics.find();
{ "_id" : ObjectId("56b2710887587c5673ca8d0b"), "topicId" : 1, "title" : "this is topic1" }
{ "_id" : ObjectId("56b271cc87587c5673ca8d0c"), "topicId" : 2, "title" : "this is topic2", "user" : "take" }

# topicIdで検索
> db.topics.find({topicId: 2});
{ "_id" : ObjectId("56b271cc87587c5673ca8d0c"), "topicId" : 2, "title" : "this is topic2", "user" : "take" }

# titleを変更してみる
> db.topics.update({topicId: 2},{title: 'mod_title'});
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.topics.find();
{ "_id" : ObjectId("56b2710887587c5673ca8d0b"), "topicId" : 1, "title" : "this is topic1" }
{ "_id" : ObjectId("56b271cc87587c5673ca8d0c"), "title" : "mod_title" }

なんと既存のデータが消えてしまいました。何も指定しないと変更分だけでなく全て変更されてしまいます。これを防ぐためには$setというキーワードを利用します。

# topics2を元に戻します。
> db.topics.update({title: 'mod_title'},{topicId :2,title: 'this is topic2',user: 'take'});
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.topics.find();
{ "_id" : ObjectId("56b2710887587c5673ca8d0b"), "topicId" : 1, "title" : "this is topic1" }
{ "_id" : ObjectId("56b271cc87587c5673ca8d0c"), "topicId" : 2, "title" : "this is topic2", "user" : "take" }

# $setを利用してタイトルのみ変更する
> db.topics.update({topicId: 2},{$set: {title: 'mod_title'}});
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.topics.find();
{ "_id" : ObjectId("56b2710887587c5673ca8d0b"), "topicId" : 1, "title" : "this is topic1" }
{ "_id" : ObjectId("56b271cc87587c5673ca8d0c"), "topicId" : 2, "title" : "mod_title", "user" : "take" }

titleのみ変更されました。

データを削除する

removeを使います。引数には削除対象を特定するための検索クエリを入れます。

> db.topics.remove({topicId: 2})
WriteResult({ "nRemoved" : 1 })
> db.topics.find();
{ "_id" : ObjectId("56b2710887587c5673ca8d0b"), "topicId" : 1, "title" : "this is topic1" }

おわりに

RDBMSしか使ったことなかったので、単なるkey:value型とRDBMS型のいいとこ取りという点には納得です。RDBMSは最初に一定の学習コストが必要なので、そういうのが必要なくデータ構造さえ決まってれば簡単に使える点がとても素敵。

実際プログラミング言語のデータオブジェクトをそのままぶちこめると考えれば親和性は高いのかなと思いました。得意なシーンが違うと思うのでもう少し勉強して実際にアプリケーションで使ってみたい。