クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【NoSQL】ターミナルからMongoDBにCRUDする(JS & JSONライク!)

DB操作の続きです。の続きです。前回はJSONファイルをDBにできるAPPモックの「JSON Server」をCRDUしました。今回はNoSQLのデータベース「MongoDB」をCRUDします。データがJSONライクで処理もJSライクというところが興味をそそります。それではいきましょう!

【目次】

※参考:前回記事
【擬似NoSQL】ターミナルからAPIモックのJSON ServerにCRUDする - クモのようにコツコツと

※参考:Web開発環境についてのまとめ
qiita.com

前回のおさらい:JSON Serverで擬似NoSQL体験

データベース(DB)にはSQL(RDB)以外にNoSQLもあり、「SQL以外のDB」という意味。下記のようないろいろな種類がある。

f:id:idr_zz:20200803080059j:plain

※参考:KVS系NoSQLのまとめ(Hibari、Dynamo、Voldemort、Riak編) (1/4):知らないなんて言えないNoSQLまとめ(1) - @IT

その中の代表格「MongoDB」はドキュメント指向のDBでJSONライクなデータ形式だった!


前回はAPIモック「JSON Server」を使ってJSONファイルそのものをDBにする擬似NoSQL的なDB操作を体験した。

f:id:idr_zz:20200804183637j:plain

※参考:前回記事
【擬似NoSQL】ターミナルからAPIモックのJSON ServerにCRUDする - クモのようにコツコツと

ドキュメント指向データベースとは何か

MongoDBはドキュメント指向のデータベース。ではドキュメント指向とは何か。

ドキュメント指向(Document-oriented、Document store) - XMLやJSONといった、 スキーマレスでデータ構造が柔軟なもの。MongoDB、Apache CouchDB、Amazon DynamoDBなど。XMLデータベースなどのシステムでは、XQueryを利用できるものもある。

※参考:NoSQL - Wikipedia

JSONだけでなくXMLも含まれるんだ。スキーマレスなところが共通点らしい。ではスキーマレスとは?

スキーマとは、データベースの構造を定義したものです。データベースのデータをどう管理するかの、一連の約束を意味します。
例えば、リレーショナルデータベースのテーブルを設計する際、各フィールドのデータ型やデータの大きさ、主キーの選択なども、スキーマ定義の一種です。

※参考:スキーマ/スキーマレスDBとは | クラウド・データセンター用語集/IDCフロンティア

ふむ、まず「スキーマ」とはデータベースの構造の定義(約束事)か。

ドキュメント指向データベースでは、1件分のデータを「ドキュメント」と呼びます。また、個々のドキュメントのデータ構造は自由で、データを追加する都度変えることができます(図1)。リレーショナルデータベースとは違って、事前にテーブルの構造を決めておく必要がありません。このことを「スキーマレス」(Schemaless)と呼びます。

※参考:ドキュメント指向データベースと列指向データベース | Think IT(シンクイット)

なるほど、RDBは事前にテーブルのカラムを設定したが、ドキュメント指向は不要だよと。

MongoDBはJSONライク & JSライク!

MongoDBの公式サイトはこちら

※参考:The most popular database for modern apps | MongoDB

データはJSONライク!

{
“_id” : ObjectId(“1234567890asdfghjkl”),
“name” : “Taro Suzuki”,
“age” : “25”,
“scores” : [90, 85, 100],
“update” : ISODate(“2015-01-01T12:00:00.000z”)
}

※参考:【MongoDB】初めてでも分かる!MongoDB入門(前編) | ヘッドウォータースのブログ TechNote

かつCRUD処理もJSライク!

CRUD MongoDB SQL文 機能
Create insert() INSERT文 データを追加
Read find() SELECT文 データを取得
Update update() UPDATE文 データを更新
Delete remove() DELETE文 データを削除

※参考:【MongoDB】初めてでも分かる!MongoDB入門(後編) | ヘッドウォータースのブログ TechNote

以前、Expressの記事で書いたように「MEANスタック」の「M」の部分に相当する。

  • M:MongoDB(データベース)
  • E:Express(バックエンド)
  • A:AngularJS(フロントエンド)
  • N:Node.js(サーバー実行環境)

※参考:ExpressとJSフレームワーク(React、Vue、Angularなど)との関係について調べたこと - クモのようにコツコツと

ちなみにMongoは「humongous」という単語から来ているそうな。てっきりモンゴルかとw

humongousは「ばかでかい、途方もない」という意味らしい。

※参考:humongous とは 意味・読み方・表現 | Weblio英和辞書


MongoDBのCRUD操作、下記の記事などを参考にさせていただいた。

※参考:MACにMongoDBをインストールする | アールエフェクト

※参考:MongoDBことはじめ – CRUD操作をMongo Shell, Node.js Driverで行う | maesblog

※参考:MongoDB入門してみた(1)CRUD操作 - Qiita

※参考:MongoDBのCRUD - Qiita

※参考:MongoDB - CRUD操作 | mongodb Tutorial

Homebrew → MongoDBインストール

まずはMongoDBのインストールから。いくつか方法があるが、自分はMac環境でMacPortsよりHomebrewの方が短時間なようだ。

※参考:MongoDBのインストール - Qiita

Homebrewは以前Laraberlのときにインストールしたのだが

LaravelをMAMP環境からHerokuにデプロイするためにやったこと調べたこと - クモのようにコツコツと

前のPCだったようで、見当たらない。この記事を参考にインストール。

※参考:Homebrewのインストール - Qiita

しばらく待つ。。


Homebrewのインストールが終わったのでMongoDBを入れる。

公式サイトのインストール方法より

※参考:https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/

まずHomebrew のtapという機能でHomebrewにMongoDBを追加して

brew tap mongodb/brew

MongoDBをインストールする。

brew install mongodb-community@4.4

インストールが終わってバージョン表示すると

mongod --version

入った!

db version v4.4.0
Build Info: {
    "version": "4.4.0",
    "gitVersion": "563487e100c4215e2dce98d0af2a6a5a2d67c5cf",
    "modules": [],
    "allocator": "system",
    "environment": {
        "distarch": "x86_64",
        "target_arch": "x86_64"
    }
}

なお、以前はこちらのコマンドでインストールできたらしいが

brew install mongodb

やってみたらこのようなエラーになった。。

Error: No available formula with the name "mongodb" 

どうやらMongoDBがオープンソースライセンスではなくなったからhomebrew-coreから消えたらしい

※参考:Homebrew CoreからMongoDBが消えた - Qiita

ぎょぎょ!そうなんだ…

MongoDBの起動と接続

とりあえずMongoDBを起動してみる。

brew services start mongodb-community

成功した!

==> Successfully started `mongodb-community` (label: homebrew.mxcl.mongodb-commu

次にMongoDBに接続

mongo

いろいろでた後にこのような表示

> 

これはMongoDBに接続できているということ。(以下この記号が頭についている状態でコマンドを打つが省略)

データベースの作成(use)

データベースを作っていく。中の構造はテーブルではなくコレクション、などRDBとは呼称がだいぶ異なる。

No RDBでの呼称 MongoDBでの呼称
1 database database
2 table collection
3 row document
4 column field
5 index index
6 primary key _id

※参考:【MongoDB】初めてでも分かる!MongoDB入門(前編) | ヘッドウォータースのブログ TechNote


まずはデータベースの確認してみる。

show dbs

結果:この3つがあると。

admin   0.000GB
config  0.000GB
local   0.000GB

「beatles」というデータベースを作成する。

useはDBの切り替えのコマンドだが、ない場合は新規作成されるらしい。

use beatles

結果:beatlesに切り替えたよと。作成できたということか。

switched to db beatles

現在使用しているDBを確認する。

db

結果:「beatles」だよと。よし、作られている!

beatles

データベース一覧を再表示。

show dbs

結果:現時点ではbeatlesは表示されない。

admin   0.000GB
config  0.000GB
local   0.000GB

ここに表示するためにはコレクション(RDBでいうテーブル)を作成する必要がある。


データベースを削除したい場合は削除したいデータベースを選択した状態で

use データベース名

下記のコマンドを実行。

db.dropDatabase()

コレクション作成(createCollection ())

コレクションを作成する。db.createCollection()とJSチックな書き方。行末のセミコロンは不要。

MySQLの時のテーブルと同じく「member」というコレクション名する。

db.createCollection("member")

結果:OKなようだ!

{ "ok" : 1 }

データベース一覧を再表示。

show dbs

やた!beatlesが表示されとる!

admin    0.000GB
beatles  0.000GB
config   0.000GB
local    0.000GB

作成したコレクションを表示

show collections

memberが表示された!

member

コレクションを削除したい場合は下記のコマンド

db.コレクション名.drop()

Create:ドキュメントの作成(insertOne()、insertMany())

ここからCRUD操作に入っていく!RDBではこの前にテーブルのカラムを設定した。

※参考:【SQL】ターミナルからMAMPのMySQLにCRUDする - クモのようにコツコツと

MongoDBの場合はいきなりドキュメントを追加してOK。

コマンドはdb、コレクション名menberの次に繋げて書く。

db.コレクション名.コマンド(引数)

ドキュメントの追加はinsertOne()コマンドを使う。

まずはリーダのジョン・レノンを追加してみる。引数の中は連想配列。

db.member.insertOne({ name: "ジョン・レノン", part: "ギター"})

結果:acknowledgedは「認めた」という意味なのでtrueは成功か。自動で振られたid番号はすごい桁数。

{
    "acknowledged" : true,
    "insertedId" : ObjectId("5f2b28d7bab508003e3acfe5")
}

ポールとジョージを二人いっぺんに追加できるかテスト。クオリーメン生え抜きメンバー。

db.member.insertOne({ name: "ポール・マッカートニー", part: "ギター"})
db.member.insertOne({ name: "ジョージ・ハリスン", part: "ギター"})

結果:まずポールの追加だけ実行されて

{
    "acknowledged" : true,
    "insertedId" : ObjectId("5f2b28e5bab508003e3acfe6")
}

次にエンター押すとジョージの追加が実行された

{
    "acknowledged" : true,
    "insertedId" : ObjectId("5f2b28ebbab508003e3acfe7")
}

一度に複数追加するにはinsertMany()コマンドを使うようだ。

スチュとピートを追加する。連想配列を配列で囲う形。

db.member.insertMany([ { name: "スチュアート・サトクリフ", part: "ベース", etc: "絵も描ける" }, { name: "ピー・ベスト", part: "ドラム" } ])

結果:二人いっぺんに追加された!

{
    "acknowledged" : true,
    "insertedIds" : [
        ObjectId("5f2b2a54bab508003e3acfe8"),
        ObjectId("5f2b2a54bab508003e3acfe9")
    ]
}

なお、スチュにはetcという別のドキュメントも追加してみた。こういうことができるのがRDBとの違いらしい。例えば後から「年齢」や「性別」のドキュメントを追加することもできる。

Read:ドキュメントの取得(find())

ドキュメントの処理。これまでの処理でドキュメントがちゃんと追加されているかを確認する。find()というコマンドを使う。

db.member.find()

結果:おお!スチュのetcドキュメントも含め、追加されてる!ハンブルグ巡業メンバーが揃った♪

{ "_id" : ObjectId("5f2b28d7bab508003e3acfe5"), "name" : "ジョン・レノン", "part" : "ギター" }
{ "_id" : ObjectId("5f2b28e5bab508003e3acfe6"), "name" : "ポール・マッカートニー", "part" : "ギター" }
{ "_id" : ObjectId("5f2b28ebbab508003e3acfe7"), "name" : "ジョージ・ハリスン", "part" : "ギター" }
{ "_id" : ObjectId("5f2b2a54bab508003e3acfe8"), "name" : "スチュアート・サトクリフ", "part" : "ベース", "etc" : "絵も描ける" }
{ "_id" : ObjectId("5f2b2a54bab508003e3acfe9"), "name" : "ピー・ベスト", "part" : "ドラム" }

find().pretty()と書くと整形した状態で表示される。

db.member.find().pretty()

結果:見やすい!

{
    "_id" : ObjectId("5f2b28d7bab508003e3acfe5"),
    "name" : "ジョン・レノン",
    "part" : "ギター"
}
{
    "_id" : ObjectId("5f2b28e5bab508003e3acfe6"),
    "name" : "ポール・マッカートニー",
    "part" : "ギター"
}
{
    "_id" : ObjectId("5f2b28ebbab508003e3acfe7"),
    "name" : "ジョージ・ハリスン",
    "part" : "ギター"
}
{
    "_id" : ObjectId("5f2b2a54bab508003e3acfe8"),
    "name" : "スチュアート・サトクリフ",
    "part" : "ベース",
    "etc" : "絵も描ける"
}
{
    "_id" : ObjectId("5f2b2a54bab508003e3acfe9"),
    "name" : "ピー・ベスト",
    "part" : "ドラム"
}

ID番号から一人だけ見たい場合はfind()の引数で指定する

db.member.find(ObjectId("5f2b28d7bab508003e3acfe5")).pretty()

ジョンだけが表示された!

{
    "_id" : ObjectId("5f2b28d7bab508003e3acfe5"),
    "name" : "ジョン・レノン",
    "part" : "ギター"
}

つってもID番号わからないよね、という場合はnameなどのキー名からも指定できる

db.member.find({"name" : "ポール・マッカートニー"}).pretty()

ポールだけ表示された!

{
    "_id" : ObjectId("5f2b28e5bab508003e3acfe6"),
    "name" : "ポール・マッカートニー",
    "part" : "ギター"
}

フルネームがわからないよねって時は、部分一致で検索したい!正規表現の/ /が使えるようだ。

※参考:こんな時どうする? MongoDBクエリ逆引きリファレンス - Qiita

特にスチュのフルネームは覚えにくいw

db.member.find({"name" : /スチュ/}).pretty()

結果:おお、「スチュ」で検索できた!

{
    "_id" : ObjectId("5f2b2a54bab508003e3acfe8"),
    "name" : "スチュアート・サトクリフ",
    "part" : "ベース",
    "etc" : "絵も描ける"
}

Update:ドキュメントの更新(update())

次にドキュメントの更新。update()メソッドを使う。$setという演算子を使うようだ。

db.ドキュメント名.update({キー: 値}, {$set: {キー: 修正後})

スチュが美術に専念するため、ポールをベースにしたい。

これでいいのかな?名前に「ポール」が含まれるデータのパートを「ベース」にせよと。

db.member.update({"name": /ポール/},{$set: {"part": "ベース"}})

結果:1件マッチ、0件アップサート?、1件変更したよ、と。アップサートってなんぞ?

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

確認のためポールを表示してみる。

db.member.find({"name" : /ポール/}).pretty()

おお、ポールがベースになった!

{
    "_id" : ObjectId("5f2b28e5bab508003e3acfe6"),
    "name" : "ポール・マッカートニー",
    "part" : "ベース"
}

では、スチュのパートはもう「画家」にしちゃおうw

db.member.update({"name": /スチュ/},{$set: {"part": "画家"}})

結果:ポールと同じなので大丈夫っぽい。

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

後から気づいたけどピート・ベストの名前が「ピー・ベスト」になってたので修正する。ごめん、ピート。

db.member.update({"name": /ベスト/},{$set: {"name": "ピート・ベスト"}})

結果:こちらもポールと同じ。

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

全体がどうなったか表示してみる。

db.member.find().pretty()

結果:スチュが画家になり、ポールはベースに!ピートは正しい名前にw

{
    "_id" : ObjectId("5f2b28d7bab508003e3acfe5"),
    "name" : "ジョン・レノン",
    "part" : "ギター"
}
{
    "_id" : ObjectId("5f2b28e5bab508003e3acfe6"),
    "name" : "ポール・マッカートニー",
    "part" : "ベース"
}
{
    "_id" : ObjectId("5f2b28ebbab508003e3acfe7"),
    "name" : "ジョージ・ハリスン",
    "part" : "ギター"
}
{
    "_id" : ObjectId("5f2b2a54bab508003e3acfe8"),
    "name" : "スチュアート・サトクリフ",
    "part" : "画家",
    "etc" : "絵も描ける"
}
{
    "_id" : ObjectId("5f2b2a54bab508003e3acfe9"),
    "name" : "ピート・ベスト",
    "part" : "ドラム"
}

Delete:ドキュメントの削除(deleteOne()、deleteMany())

最後、データの削除を行う。deleteOne()コマンドを使う。

まず、スチュが亡くなる・・

db.member.deleteOne({ "name" : /スチュ/})

結果:1件削除、とでた。

{ "acknowledged" : true, "deletedCount" : 1 }

複数削除はdeleteMany()コマンドを使う。

スチュの削除はpartの「ドラム」からやってみる。

db.member.deleteMany({ "part" : "ドラム"})

結果:削除は1件だが、もしpartを「ギター」でやったらジョンとジョージの削除で2件になってたはず。

{ "acknowledged" : true, "deletedCount" : 1 }

この時点でどうなったか表示してみる。

db.member.find().pretty()

結果:クオリーメンの生え抜きメンバーに戻った。。

{
    "_id" : ObjectId("5f2b28d7bab508003e3acfe5"),
    "name" : "ジョン・レノン",
    "part" : "ギター"
}
{
    "_id" : ObjectId("5f2b28e5bab508003e3acfe6"),
    "name" : "ポール・マッカートニー",
    "part" : "ベース"
}
{
    "_id" : ObjectId("5f2b28ebbab508003e3acfe7"),
    "name" : "ジョージ・ハリスン",
    "part" : "ギター"
}

では最後のワンピース!リンゴを追加する。

db.member.insertOne({ name: "リンゴ・スター", part: "ドラム"})

結果:追加成功

{
    "acknowledged" : true,
    "insertedId" : ObjectId("5f2b3784bab508003e3acfea")
}

最終的な形を表示する!

db.member.find().pretty()

結果:おお、デビュー時のファブフォーが揃った!

{
    "_id" : ObjectId("5f2b28d7bab508003e3acfe5"),
    "name" : "ジョン・レノン",
    "part" : "ギター"
}
{
    "_id" : ObjectId("5f2b28e5bab508003e3acfe6"),
    "name" : "ポール・マッカートニー",
    "part" : "ベース"
}
{
    "_id" : ObjectId("5f2b28ebbab508003e3acfe7"),
    "name" : "ジョージ・ハリスン",
    "part" : "ギター"
}
{
    "_id" : ObjectId("5f2b3784bab508003e3acfea"),
    "name" : "リンゴ・スター",
    "part" : "ドラム"
}

MongoDBの停止と再起動

最後に「Control + C」でサーバを閉じると

bye

と表示された。


次にターミナルを開いたときにmongoと打つと

mongo

再びMongoDBが起動して下記の状態になった!

>

最後に

f:id:idr_zz:20200806080740j:plain

ということでMongoDBでNoSQLのCURD体験ができました!

JSON Serverとは処理の書き方が違いましたが、JSライクなコマンドの書き方なので、とても直感的に実現できたと思います。

後からドキュメントを追加するのも楽でいいですね。それはそれでデータが増えてくると後からドキュメント名の把握が難しくなりそうですが。

  • MySQLはきっちりカラムが定義されて整合性の取れたデータベース
  • MongoDBはドキュメントを自由に編集できる柔軟性の高いデータベース

という印象を持ちました。

次は、Fetch APIを使ってブラウザからJSON ServerにCRUD操作、をやってみたく思います。それではまた!


※参考:Web開発環境についてのまとめ
qiita.com