MongoDBチートシート

## 概要
- MongoDB = ドキュメント指向のDB
    - ドキュメント = 構造化されたデータのこと
        - RDBのレコードに相当する
        - これを単位としてデータを扱う
        - ドキュメントは、主キーとして `_id` を必ず持つ
        - ドキュメントはJSONのように扱える(内部的にはBSON)
        - データは型付けされる(BSON互換)
    - コレクション = データの集まり
        - RDBのテーブルに相当する(ただしスキーマは存在しない)
        - SQLのような複雑なデータ操作はできない
        - 検索機能としては柔軟に対応できる

- 企業(MongoDB Inc.)が開発し、オープンソースで公開されている
    - 有料版が存在し、セキュリティ強化などの追加機能とサポートが受けられる
    - その他、コンサルや教育も受け付けているとのこと

- GNU APGLライセンス
- クロスプラットフォーム
- C++実装
- スケーリング/クラスタリングに対応している
- スクリプト言語としてJavaScriptを採用している
- 復数の言語のドライバ(バインディング)を提供


## 導入

導入方法は[公式にプラットフォーム別の詳細な説明がある](https://docs.mongodb.com/manual/installation/?jmp=footer&_ga=2.241108867.1002782903.1495007311-853616835.1495007173)ので参照のこと。

### インストール

OSXの場合はbrewでインストールできる。

```bash:OSX
$ brew update
$ brew install mongodb --with-openssl
```

Linuxの場合も同様にパッケージマネージャを使うか、.rpmなどからインストール可能。


CentOS6.xの場合はリポジトリを登録し、

```bash:/etc/yum.repos.d/mongodb-org-3.4.repo
[mongodb-org-3.4]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.4/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-3.4.asc
```

その後yumで通る。

```bash:CentOS6.x
$ sudo yum install mongodb-org
```

### 起動

`mongod` でサーバを起動する。

```bash:OSX
$ mongod
```

```bash:CentOS6.x
$ service mongod start
```

OSXの場合はデフォルトで`/data/db/` にデータが置かれるので、このディレクトリを作って権限設定しないと失敗する。
インストール後の起動方法についても[公式の説明がある](https://docs.mongodb.com/manual/installation/?jmp=footer&_ga=2.241108867.1002782903.1495007311-853616835.1495007173)ので参照のこと。


## Mongo Shell

Mongo Shell = `mongo`コマンドで起動する対話型のコンソールのこと。
JavaScript(風のスクリプト)を使う。

```bash
# mongodに接続
$ mongo
```

`help`でヘルプ表示、Tabで補完ができる。

[公式のSQLとMongoの対応表](https://docs.mongodb.com/manual/reference/sql-comparison/)がわかりやすかった。


### 状態を表示する(show)

`show <表示内容>` でDBの状態を表示できる。

```js
show dbs            // DBの一覧
show collections    // 使用中のDB中のコレクション一覧
show users          // ユーザの一覧
show roles          // ロールの一覧
```


### DBを選択する/作成する(use)

`use`により、DBを切り替えて利用できる。
また存在しないDBを指定した場合、そのDBが新たに作成される。

```js
// DB(somedb)を選択
use somedb
```


### コレクションを追加/削除する(createCollection/drop)


`createCollection()`により、コレクションを新規に作成できる。
また後述する`insert`によって存在しないDBにドキュメントを挿入した場合も、自動的にそのコレクションが作成される。

```js
// 利用中のDBにコレクションfooを追加
db.createCollection('foo')
```

コレクションを削除する場合は`drop()`を使う。


```
// コレクションfooを削除
db.foo.drop()
```


### 代入する

シェル内ではjsと同様に、変数に任意のオブジェクトを代入できる。

```
// insertの結果をresultに代入
result = db.foo.insert({...})
```


### 挿入する(insert)

`insert()`を使う。
またショートハンドとして`insertOne()`, `insertMany()` も存在する。

挿入時に、そのドキュメントの直下に自動的に `_id` が含まれる。


```js
// abcコレクションにドキュメントを挿入
db.abc.insert({
    'name' : 'Alpha',
    'file' : {
        'name' : 'test file',
        'text' : '...',
    }
)
```


### 検索する(find)

`find()`を使う。
またショートハンドとして`findOne()`なども存在する。

#### 全件検索する
全件検索する場合は単に`find()`を呼ぶ。

```js
// 単にfindすると全件を取得する
db.abc.find()
```

#### 条件指定する
条件を指定する場合は`find()`の引数として渡す。

```js
// トップレベルの項目nameを検索する
db.abc.find({
    'name' : 'Alpha'
})
```

階層を指定する場合は、条件のキーをドット区切りで表記する。

```js
// file.nameを検索する
db.abc.find({
    'file.name' : 'test file',
})
```


配列内の要素を検索する場合は、

```
'<配列自身のキー>.<配列の要素の任意のキー>' : '<配列の要素の値>'
```


```js
// 各ドキュメントがこんな構造だったとして:
//  {
//      'list' : [
//          {'name' : 'item1', ...}
//          {'name' : 'item2', ...}
//          {'name' : 'item3', ...}
//      ]
//  }


// listの中にある、名前がAlphaの要素を検索する場合は:
db.abc.find({
    'list.name' : 'item1'
})
```


#### 比較する

検索条件に演算子を用いる場合は、

`<キー>: { <演算子> : '<比較値>' }`


```js
// file.sizebytes > 1024
db.abc.find({
    'file.sizebytes' : { $gt: 1024 }
})
```

演算子は`$gt`のように定数によって指定できる。([一覧](https://docs.mongodb.com/manual/reference/operator/query-comparison/))


演算子  | 意味
---     | ---
$eq     | `==`
$ne     | `!=`
$lt     | `<`
$lte    | `<=`
$gt     | `>`
$gte    | `>=`
$in     | 配列に含まれる
$nin    | 配列に含まれない


#### 複合条件で検索する

ANDの場合は単に条件を列挙すればいい。

```js
db.abc.find({
    'name' : 'alpha',
    'file.name' : 'test file',
})
```

ORの場合は、定数`$or`を指定する。

```js
db.abc.find({
    $or : [
        { 'name'        : 'alpha'       },
        { 'file.name'   : 'test file'   }
    ],
})
```

#### 条件指定する(count)

件数のみを取得したい場合は`count()` を使う。

```js
// nameがalphaのドキュメントの件数を返す
db.abc.count({
    'name' : 'alpha',
})
```


#### ソートする(sort)

`find()` による検索結果に対して`sort()` を実行する。
ソート条件は1 = ASC, -1 = DESC

```js
// serialの降順でソートする
db.abc.find().sort({
    'serial' : -1
})
```

### 正規表現を使う

検索条件には正規表現を使うことができる。
jsと同じリテラルを使って書ける。


```js
// nameがa/Aからはじまるドキュメントを検索
db.abc.find({
    'name'  : /^a.*/i
})
```

### 更新する(update)

`update()`を使う。
またショートハンドとして`updateOne()`, `updateMany()` も存在する。

なお`_id`は更新できない。
またMongoにおける書き込み操作は__ドキュメント単位でのアトミック__が保証される。


#### ドキュメントを部分的に更新する

```
update(
    <検索条件>,
    {
        $set : <更新内容>,
        $currentData : <現在時刻を更新する項目>,
    },
    {
        multi: <復数更新の有無>,
    }
)
```


```js
// nameがalphaのドキュメントを探し、特定の値を更新する
// 更新の際にupdated_atを現在時刻に更新する
db.abc.update({
    {
        'name' : 'alpha',
    },
    {
        $set : {
            // find()と同様にドット区切りで指定できる
            'file.name' : 'some filename updated',
        },
        $currentDate : {
            'updated_at' : true,
        },
    },
})
```

#### ドキュメント全体を更新する

```
update(
    <検索条件>,
    <置換えるドキュメント>,
)
```

```js
// nameがalphaのドキュメントを探し、まるごと置換える
db.abc.update({
    {
        'name' : 'alpha',
    },
    {
        'name' : 'alpha updated',
        // ...
    },
})
```

### 削除する(remove)

```
remove(
    <検索条件>,
    {
        justOne : <削除対象を1件に限定する場合はtrue>,
    },
)
```

#### 全件削除する
空のオブジェクトを渡して`remove()`すると全件削除される。

なお引数なしの状態で呼ぶとエラーとなり、削除されない。

```js
// 全件削除する
db.abc.remove({})
```

#### 条件を指定して削除する

```js
// nameがalphaのドキュメントを削除する
db.abc.remove({
    'name' : 'alpha',
})
```

### WriteResult()について

Mongoは書き込みを伴う処理の戻り値として、`WriteResult()`を返す。
以下のようなプロパティを持っていて、結果を識別できる。

プロパティ  | 内容
---         | ---
nInserted   | 挿入された件数
nMatched    | 更新対象となった件数
nModified   | 更新された件数
nRemoved    | 削除された件数


### 特殊なデータの扱いについての特記事項

#### 日付
日付の扱い方は2つある。

- 文字列で保持する方法: `Date()` を使う
- Unixエポックミリ秒で保持する方法: `ISODate()`を使う

比較/計算は後者のほうが簡単にできそうだが標準時刻になってしまうのが難点。
ベストプラクティスは未調査。

#### バイナリ
サイズの上限については[公式](https://docs.mongodb.com/manual/reference/limits/#data)に環境別のData Sizeの上限があるが、見る限りTB単位の数なので通常利用するぶんには問題なさそう。



## 設定

DBやコレクションに個別の設定は、旧来のRDBと同様にMongo Shellを使って設定していく。

全体的な設定は設定ファイル(`mongod.conf`など)として記述し、サーバの起動時に読み込む。
コマンドで直接起動する場合は`--config`オプションを使って設定を指定する。

```bash
$ mongod --config /path/to/mongod.conf
```

設定ファイルはYAML形式で記述する。


## インデックス

インデックスの概念については他のDBと同じ。
検索速度の向上や、ユニーク制約のために設定する。

Mongoはドキュメントに自動的に`_id` を設定するが、この`_id`に対してはインデックスも自動的に割当たる。
このインデックスはユニーク制約を持つため、_idを重複して挿入することができないようになっている。


インデックスの作成/確認/削除はコマンドで行う。

```js
// abcコレクションのfile.nameに対して、昇順かつユニークのインデックスを作成
db.abc.createIndex({
    { 'file.name'   : 1 },
    { 'unique' : true   },
})

// 設定済のインデックスを確認
db.some.getIndexes()


// インデックスを削除
db.some.dropIndex(
    { 'file.name' : 1 }
)
```


## ユーザ/ロール/認証

Mongoではユーザとロールの単位で権限を管理する。

### 管理ユーザの作成

まずは認証を特に指定しない状態でMongoを起動し、管理用のDBと、その管理者を設定する。

```js
use admin
db.createUser({
    user: "userAdmin",
    pwd: "some_password",
    roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
})
```

サーバ側(mongod)を終了し、認証を有効にした状態で再起動する。
認証機能は、コマンドの`--auth`オプションか設定ファイルによって指定可能。

```bash
$ mongod --auth
```

これ以降、ユーザの作成・管理にはこの管理者ユーザの認証が必要となる。
また管理者ユーザによって権限を与えられたユーザで認証しない限り、DBへのアクセスができなくなる。



認証するためにはクライアント側の接続時に指定するか、`auth()`を使う。

```bash
// 接続時に認証する
$ mongo -u 'userAdmin' -p 'some_password' --authenticationDatabase 'admin'
```

```js
// 接続後に認証する
db.auth('userAdmin', 'some_password')
```

### 一般ユーザの作成

管理ユーザと同様に、`createUser()`によってユーザを作成できる。
対象のロールおよびDBは復数指定することができる。

```js
db.createUser(
  {
    user: "alpha",
    pwd: "some_password",
    roles: [ { role: "readWrite", db: "alpha" },
             { role: "read", db: "bravo" } ]
  }
)
```

### ロールの種類について

ロールはMongo標準の[ビルトインロール](https://www.wantedly.com/users/18259901)の中から選んで指定できる。
また[ロールを自分で作成することもできる](https://docs.mongodb.com/manual/tutorial/manage-users-and-roles/#)。




## TODO
- コマンドの詳細
- 設定項目の詳細
- 暗号化
    - 通信の暗号化(TLS/SSL)
    - ストレージの暗号化
- レプリケーション
- シャーディング(クラスタリング)
- API/ドライバ

## 参考
- [Wikipedia - MongoDB](https://ja.wikipedia.org/wiki/MongoDB)
- [MongoDB(公式)](https://www.mongodb.com/)
    - [Getting Started with MongoDB (MongoDB Shell Edition)¶](https://docs.mongodb.com/getting-started/shell/)
    - [The MongoDB 3.4 Manual](https://docs.mongodb.com/manual/?jmp=footer&_ga=2.117204614.1866745580.1495007181-853616835.1495007173)
        - [mongo Shell Quick Reference](https://docs.mongodb.com/manual/reference/mongo-shell/)
        - [Data Types in the mongo Shell](https://docs.mongodb.com/manual/core/shell-types/)
        - [MongoDB CRUD Operations](https://docs.mongodb.com/manual/reference/operator/query-comparison/)
        - [Reference](https://docs.mongodb.com/manual/reference/)
        - [Indexes](https://docs.mongodb.com/manual/indexes/)
        - [Security](https://docs.mongodb.com/manual/security/)