MongoDBことはじめ インストールとコマンドライン、プログラムでの利用方法

## はじめに

NoSQL、言葉だけ知ってるけどどんなものかは知りませんでした。使う機会が出てきたので、mongoDBの利用記録を記載しておきます。
[やってみようNoSQL　MongoDBを最速で理解する](https://qiita.com/Brutus/items/8a67a4db0fdc5a33d549)が非常にわかりやすかったです。
NoSQLは**SQLを使わないデータベース管理システム**で、従来のテーブルを定義してデータをインサートしていく形ではなく、フレキシブルにデータを詰めていきます。
(そのうち動的型付け言語 VS 静的型付け言語みたいな論争が起きたりして)

## インストール

私の環境はUbuntu18.04なので、↑の参考記事ではなく以下を参考にでインストールしました。
[Ubuntu MongoDBのインストールする手順](https://www.trifields.jp/how-to-install-mongodb-on-ubuntu-2751)

記事の情報そのままにaptが使えるようにしてinstall。

```sh
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6
echo "deb [ arch=amd64,arm64 ] http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list
sudo apt update
sudo apt install mongodb-org
```

サービスの確認 && 有効化

```sh
sudo systemctl status mongod
sudo systemctl enable mongod
sudo systemctl start mongod
```

## コマンドラインで触る

### アクセス
まずはMongoDBサーバーにアクセスしてDBを指定。警告に関する解説も[Ubuntu MongoDBのインストールする手順](https://www.trifields.jp/how-to-install-mongodb-on-ubuntu-2751)に書いてますね。

```console
root@htsuji-VirtualBox:/usr/src/go-go1.12.5/src# mongo
MongoDB shell version v3.4.20
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.20
Server has startup warnings:
2019-06-01T19:45:44.038+0900 I STORAGE  [initandlisten]
2019-06-01T19:45:44.038+0900 I STORAGE  [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2019-06-01T19:45:44.038+0900 I STORAGE  [initandlisten] **          See http://dochub.mongodb.org/core/prodnotes-filesystem
2019-06-01T19:45:44.140+0900 I CONTROL  [initandlisten]
2019-06-01T19:45:44.140+0900 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2019-06-01T19:45:44.140+0900 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2019-06-01T19:45:44.140+0900 I CONTROL  [initandlisten]
> use testdb
switched to db testdb
```

### 基本

SQLではDB->テーブル->レコードとデータが詰められますが、MongoDBではDB->コレクション->ドキュメントという単位でデータを管理します。
ドキュメントはJsonのようなkey-valueの形で登録できるため、自由なフォーマットのデータをドキュメントとして扱うことが出来ます。

こちらも[やってみようNoSQL　MongoDBを最速で理解する](https://qiita.com/Brutus/items/8a67a4db0fdc5a33d549)の図が非常にわかりやすかったので紹介。
![](https://camo.qiitausercontent.com/5ecbc5be1c8fd8f65f9b3df78e40899c18379956/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3232313735392f64623039653335332d366230312d346462342d656439352d3763666637343233396233632e706e67)

触っている感覚としては、コレクションがクラスのインスタンスで、そのクラスメソッドを扱ってデータベース操作を行う感じですね。
参考URLに倣ってtestcollというコレクションを作った場合は、db.testcollのメソッドでinsert等を行います。
()を書かずに実行するとメソッドの中身が表示されるのが面白い。中身はJavaScript。

```javascript
> db.testcoll.find
function (query, fields, limit, skip, batchSize, options) {
    var cursor = new DBQuery(this._mongo,
                             this._db,
                             this,
                             this._fullName,
                             this._massageObject(query),
                             fields,
                             limit,
                             skip,
                             batchSize,
                             options || this.getQueryOptions());

    var connObj = this.getMongo();
    var readPrefMode = connObj.getReadPrefMode();
    if (readPrefMode != null) {
        cursor.readPref(readPrefMode, connObj.getReadPrefTagSet());
    }

    var rc = connObj.getReadConcern();
    if (rc) {
        cursor.readConcern(rc);
    }

    return cursor;
}
```

### 挿入

insertを使用。

```js
> db.testcoll.insert({"name":"me3", "value":"data3"})
WriteResult({ "nInserted" : 1 })
```

### 参照

findやfindOneを利用。select * 相当

```js
> db.testcoll.find()
{ "_id" : ObjectId("5cf257eacce5a81569e74804"), "name" : "me", "value" : "data" }
{ "_id" : ObjectId("5cf2599201876469e16bfcba"), "name" : "me2", "value" : "data2" }
```

select * テーブル名 where XXX相当

```js
> db.testcoll.find({name:"me"})
{ "_id" : ObjectId("5cf257eacce5a81569e74804"), "name" : "me", "value" : "data" }

> db.testcoll.findOne({name:"me"})
{
        "_id" : ObjectId("5cf257eacce5a81569e74804"),
        "name" : "me",
        "value" : "data"
}
```

### 他のコマンド

公式ドキュメントは[こちら](https://docs.mongodb.com/?_ga=2.93635093.962362200.1559385541-1179797152.1559385541)です。
他、こちらの記事等が参考になりそうです。
[MongoDBの基礎](https://qiita.com/CyberMergina/items/1010a93f1b5f1fc8d320)


## プログラムで触る

bash, JavaScript, Golangは試しました。他は参考リンクだけ貼っておきます。
各言語の情報は公式でまとめてくれています。
[MongoDB Drivers and ODM](https://docs.mongodb.com/ecosystem/drivers/)

### bash

(プログラムというのは微妙ですが、すぐ使うので記載。)
`mongo --host ホスト名 DB名 --eval 'コマンド'`でOK

```console
$ mongo --host localhost testdb --eval 'db.testcoll.findOne({name:"me"})' --quiet
{
        "_id" : ObjectId("5cf257eacce5a81569e74804"),
        "name" : "me",
        "value" : "data"
}
```

参考:[MongoDBシェルをコンソール外から実行する](https://open-groove.net/mongodb/mongodb-shell-eval/)

ちなみに、mongoコマンドにjsファイルを指定するオプションがあり、

```javascript
const testcoll = db.testcoll
print(JSON.stringify(testcoll.findOne({name:"me"}), null, " "))
```

```console
# mongo --host localhost:27017 testdb --quiet cmp.js
{
 "_id": {
  "$oid": "5cf257eacce5a81569e74804"
 },
 "name": "me",
 "value": "data"
}
```

参考:[MongoDBでらくらくバッチ処理をやる(Javascriptファイルを実行する)方法](http://dqn.sakusakutto.jp/2012/04/mongodb-batch-javascript.html)

### [JavaScript (Node.js)](https://mongodb.github.io/node-mongodb-native/)

ベースがJavaScriptなので、Node.jsで扱うのは楽っぽい。と思いきやデータはオブジェクトで帰ってくるのでところどころ感触が違いそうですね。
`npm install mongodb --save`でインストールして、以下の感じで使えます。

```javascript
const MongoClient = require("mongodb").MongoClient;
const _url = "mongodb://127.0.0.1:27017";

//connectで接続。mongoでログインした状態に近い
MongoClient.connect(_url, { useNewUrlParser: true }, (error, client) => {
    //use testdb相当
    const db = client.db('testdb');
    //collection指定
    var collection = db.collection("testcoll");
    collection.find().toArray((error, docs) => {
        console.log(docs)
    })
    client.close();
});
```

公式:[MongoDB Node.JS Driver](https://mongodb.github.io/node-mongodb-native/)
参考:[Node.jsのMongoClientの仕様が変わってた](https://qiita.com/mimizq/items/76d3a948acb33881c8db)

### Golang

#### [公式: mongodb/mongo-go-driver](https://godoc.org/go.mongodb.org/mongo-driver/mongo)

なんと、go getが使えない!depでパッケージ管理をしてensureで追加する形ですね。
depでパッケージ管理をした上で、`dep ensure -add "go.mongodb.org/mongo-driver/mongo@~1.0.0"`でパッケージを追加。
こんな感じで扱えます。

```golang:
package main
import (
        "go.mongodb.org/mongo-driver/mongo"
        "go.mongodb.org/mongo-driver/mongo/options"
        "go.mongodb.org/mongo-driver/bson"
        "context"
        "time"
        "fmt"
)

type Document struct {
        Name string
}

func main() {
        //client情報作成
        client, _ := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))
        //contextを作ってバックグラウンド実行。(nilだとmongo側でContextを作成してます。)
        ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
        defer cancel()
        //DB接続
        client.Connect(ctx)
        defer client.Disconnect(ctx)
        //use database
        db := client.Database("testdb")
        //collection指定
        coll := db.Collection("testcoll")
        //find
        current, _ := coll.Find(
                context.Background(),
                bson.D{},
        )
        defer current.Close(context.Background())
        //findの戻り値はポインタのような感覚で使う模様。Nextでなめていく
        for current.Next(context.Background()) {
                //Decodeで中身を取り出す。
                var doc Document
                current.Decode(&doc)
                fmt.Printf("%+v\n", doc)
        }
}
```

Contextは、goroutineを利用してリクエストを裏で処理してもらうためのもので、DBアクセスに時間がかかってもブロックされないようにしてるみたいです。
go routien指定しないとどうなるのかな～とちょっと中を覗いてみましたが、結局中でContextを作っています。

参考:
[公式:examples_test.go(DB接続部分)](https://github.com/mongodb/mongo-go-driver/blob/master/examples/documentation_examples/examples_test.go)
[公式:examples.go(DB接続後)](https://github.com/mongodb/mongo-go-driver/blob/master/examples/documentation_examples/examples.go)
[公式:GoDoc](https://godoc.org/go.mongodb.org/mongo-driver/mongo#pkg-examples)
[golang contextの使い方とか概念(contextとは)的な話](https://qiita.com/marnie_ms4/items/985d67c4c1b29e11fffc)

#### [globalsign/mgo](https://godoc.org/github.com/globalsign/mgo)

Golangのmongo利用で最初に出てきたのは公式ではなくてこちらでした。普通にgo getで使えます。

```sh
go get -v github.com/globalsign/mgo
```

後はJavaScriptと同様、connect, DB指定->collection指定->コマンド実行という形。こっちはContextを使わないのでシンプル。
Findでは情報取得のためのstructを作ってるだけで、その後OneやAll等を実行して初めて実際に情報取得をしています。

```golang
package main

import (
    "fmt"
    "github.com/globalsign/mgo"
    "github.com/globalsign/mgo/bson"
)

type Document struct {
        Id bson.ObjectId `bson:"_id"`
        Name string
}

func main() {
    //connect to mongo
    connection, _ := mgo.Dial("mongodb://localhost/testdb")
    defer connection.Close()
    //use testdb
    db := connection.DB("testdb")
    //get collection
    collection := db.C("testcoll")
    //Find
    documents := collection.Find(bson.M{})
    var doc Document
    documents.One(&doc)

    fmt.Printf("documents::%+v\n", doc)
}
```

mgo公式:
[GoDoc](https://godoc.org/github.com/globalsign/mgo)
参考:
[border/mgoExample.go](https://gist.github.com/border/3489566)
[Go言語でMongoDB使う ①インストール的なところ](https://qiita.com/otiai10/items/2c94bb53bf55266130c2)

MongoDB
https://docs.mongodb.com/ecosystem/drivers/

### その他

実際に試してないですが、ググって見かけたものを書いておきます。基準は特にない。
上でも紹介した[MongoDB Drivers and ODM](https://docs.mongodb.com/ecosystem/drivers/)が充実してるので、興味がある方はそちらを参照ください。

- PHP

公式:[MongoDB driver](https://www.php.net/manual/ja/set.mongodb.php)

- Ruby

公式:
[MongoDB Ruby Driver](https://github.com/mongodb/mongo-ruby-driver)
[Ruby MongoDB Driver (document)](https://docs.mongodb.com/ruby-driver/current/)
参考:[Ruby MongoDBに接続する](http://sh-yoshida.hatenablog.com/entry/2016/08/21/172342)

- Python

公式:
[PyMongo 3.8.0 Documentation](http://api.mongodb.com/python/current/)
[Python MongoDB Drivers](https://docs.mongodb.com/ecosystem/drivers/python/)
参考:
[NoSQLデータベース「MongoDB」をPythonで利用する～pymongoの基本的な使い方まとめ～](https://qiita.com/ognek/items/a37dd1cd0e26e6adecaa)