私ならわかるMongoDB

## はじめに
勉強がてらまとめたものです

※ 私ならわかります

## MongoDBの概要
- ドキュメント思考
- NoSQL:データ構造決めずにデータ管理する（スキーマーレス）
- [公式Web](https://docs.mongodb.com/)

## 専門用語
- データーベース
- コレクション
 - RDBでいうテーブル
- ドキュメント
 - RDBでいうレコード
 - オブジェクト形式 {field: val, field: val...}
 - データ構造関係なくオブジェクトを保管できる
 

## 環境構築
### 想定環境
- Ubuntu 18.0.4 LTS
- その他の環境の方は[こちら](https://docs.mongodb.com/manual/installation/)を参照

### ローカルで利用
ローカルで環境構築

```bash
# パッケージ管理システムが使用する公開鍵をインポート
$ wget -qO-https://www.mongodb.org/static/pgp/server-4.2.asc | sudo apt-key add-

# エラーが出た場合、必要なライブラリをインストール
$ sudo apt-get install gnupg

# バージョンのリストファイル作成
$ echo  "deb [arch = amd64、arm64] https://repo.mongodb.org/apt/ubuntu bionic / mongodb-org / 4.2 multiverse"  | sudo tee /etc/apt/sources.list.d/mongodb-org-4.2.list

# ローカルパッケージのリロード
$ sudo apt-get update

# MongoDBのインストール
$ sudo apt-get install -y mongodb-org

# MongoDBの起動
$ sudo systemctl start mongod

# MongoDBの起動確認
$ sudo systemctl status mongod

# MongoDBを使ってみる
$ mongo

# ※ 再起動後も起動したい場合
$ sudo systemctl enable mongod
```

### Dockerで利用
Dockerおよび、Docker Composeを使って環境構築

```yml
version: '3'
services:
  # MongoDBのサービス
  mongo:
    image: mongo:latest
    container_name: mongo
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: password
      MONGODB_DATA_DIR: /data/db
    ports:
      - 27017:27017
    volumes:
      - ./db:/data/db
      - ./configdb:/data/configdb
  # MongoDBのGUIサービス
  mongo-express:
    image: mongo-express:latest
    container_name: mongo-express
    restart: always
    links:
      - mongo
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: root
      ME_CONFIG_MONGODB_ADMINPASSWORD: password
```

#### MongoDBを使ってみる

```bash
# コンテナへアクセス
$ docker-compose run --rm mongo bash

# MongoDBの対話モード
$ mongo admin -u root -p
パスワード入力（ymlファイルで設定した通りpasswordを入力）
```

## コマンド
### データベースの操作

- データベース一覧

```bash
> show dbs;
```
- データベースの作成 or 切り替え

```bash
> use データベース名;
```
※ データベース作成時、まだコレクションがないため存在しないことになっている

- データベースの削除

```bash
> db.dropDatabase();
```

- 現在のデータベースの状態

```bash
> db.stats();
```
現在選択しているデータベースの名前などの詳細な情報が見れる

### コレクションの操作

- コレクションの作成

```bash
> db.createCollection("コレクション名");
```
- コレクションの削除

```bash
<!DOCTYPE html>
<html lang="jp">
  <head>
> db.コレクション名.drop();
```

- コレクション名の変更

```bash
> db.古いコレクション名.renameCollection("新しいコレクション名");
```

- コレクション一覧

```bash
> show collections;
```

### ドキュメントの操作
#### 基本操作

- ドキュメントの作成

```bash
> db.コレクション名.insert({
    field: val,
    field: val,
       .
       .
       .
 });
```
※ スキーマレスなので、同一のコレクションに異なるドキュメントの構造を保存できる

- javascriptでドキュメントの作成

例）

```bash
> for (val i = 0; i < 10; i++) {
    db.コレクション名.insert({
      num: i
    });
  }
```
結果として、以下のようなドキュメントが登録される
_idは一意なものが自動で決定される

```bash
{ "_id" : ObjectId("5e626f3ef980343ff158c2a0"), "num" : 0 }
{ "_id" : ObjectId("5e626f3ef980343ff158c2a1"), "num" : 1 }
{ "_id" : ObjectId("5e626f3ef980343ff158c2a2"), "num" : 2 }
{ "_id" : ObjectId("5e626f3ef980343ff158c2a3"), "num" : 3 }
{ "_id" : ObjectId("5e626f3ef980343ff158c2a4"), "num" : 4 }
{ "_id" : ObjectId("5e626f3ef980343ff158c2a5"), "num" : 5 }
{ "_id" : ObjectId("5e626f3ef980343ff158c2a6"), "num" : 6 }
{ "_id" : ObjectId("5e626f3ef980343ff158c2a7"), "num" : 7 }
{ "_id" : ObjectId("5e626f3ef980343ff158c2a8"), "num" : 8 }
{ "_id" : ObjectId("5e626f3ef980343ff158c2a9"), "num" : 9 }
```

- 全件削除

```bash
> db.コレクション名.remove({});
```

- 条件を指定して削除

```bash
> db.コレクション名.remove(条件);
```

例)

```bash
# idが1であるドキュメントの削除
> db.コレクション名.remove({id: 1})
```

#### 表示

- 全件表示

```bash
> db.コレクション名.find();
```
- 表示したいフィールドの指定

keyに表示したいフィールド名を、valを`true`または`1`と指定する

```bash
> db.コレクション名.find({field1: true, field2: 1, ...});
```

- 表示するフィールドの除外

keyに除外したいフィールド名、valを`false`または、`1`と指定する

```bash
> db.コレクション名.find({}, {field1: false, field2: 0, ...})
```

#### 表示条件の加工

- 昇順にソート

```bash
> db.コレクション名.find({}).sort({field: 1})
```

- 降順にソート

```bash
> db.コレクション名.find({}).sort({field: -1})
```

- limit指定（表示する件数を指定）

```bash
> db.コレクション名.find({}).limit(数字)
```

- 先頭の行だけ表示

```bash
> db.コレクション名.findOne({})
```


- オフセット指定

```bash
> db.コレクション名.find({}).skip(数字)
```
指定した数字は、先頭から数えたスキップしたい行数を表し、
数字以降の行を表示する


#### 特定ドキュメントの表示

- 一致するドキュメントの表示

```bash
> db.コレクション名.find({field: val});
```

- 条件を指定したドキュメントの表示

```bash
> db.コレクション名.find({field: {条件: 数字}});
```

例）

```bash
> db.コレクション名.find({field: {$gte: 50}});
50以上のvalをもつドキュメントが表示される
```

**条件一覧**

|  条件  |  意味  |
| ---- | ---- |
|  $eq  |  等しい（val == 数字）  |
|  $ne  |  等しくない（val != 数字）  |
|  $gte  | 以上（val >= 数字） |
|  $gt  |  より大きい（val > 数字）  |
|  $lte  |  以下（val <= 数字）  |
|  $lt  |  より小さい（val < 数字）  |

- 正規表現を使ったドキュメントの表示

```bash
> db.コレクション名.find({field: /正規表現/});
```
※ javascriptの正規表現が使える

#### 複雑な条件を指定したドキュメントの表示

- AND

```bash
> db.コレクション名.find({field1:条件, field2:条件,...});
```

- OR

```bash
> db.コレクション名.find({$or: [{field1:条件},{field2:条件},...]});
```

- IN

指定したfieldに[val1, val2, ...]配列内の値が一致するドキュメントを表示

```bash
> db.コレクション名.find({field: {$in: [val1, val2, ...]}});
```

- 指定したフィールドを持つドキュメントを表示

```bash
> db.コレクション名.find({field: {$exists: true}});
```
※ `{$exists: false}`にすると指定したフィールドを持たないドキュメントが表示される


#### ドキュメント全体の情報

- 特定のフィールドの値を表示(射影)

```bash
> db.コレクション名.distinct(field);
```

- ドキュメント数のカウント

```bash
> db.コレクション名.count();
```

#### ドキュメントの編集

- フィールドの更新 or 追加

```bash
> db.コレクション名.update({field1: val1}, {$set: {field2: val2}});
```
field1とval1は抽出条件を表しています

field2は更新対象のフィールドでval2は更新する値を示しています

※ $setをつけない場合、置き換えてしまうので注意
※ 抽出した最初の一件のみ書き換えを行う

- 条件に一致するドキュメントが存在していれば更新、なければ作成

```bash
> db.コレクション名.update({field1: val1}, {field2: val2}, {upsert: true})
```
※ $setとの違いは条件に一致する全ドキュメントを更新するという点です。

第三引数に`{upsert: true}`を指定することで、

- 条件に一致する全フィールドの更新

```bash
> db.コレクション名.update({field1: val1}, {$set: {field2: val2}}, {multi: true});
```

※ 変更対象は、コレクションの中で、field1: val1を持つドキュメント

- 値を元に更新する場合

```bash
> db.コレクション名.update({field1: val1}, {$inc: {field2: val2}});
```

`元の値 + val2`に更新してくれる

- フィールドの名前を変更

```bash
> db.コレクション名.update({field1: val1}, {$rename: {field2: "new_field"}});
```

field2の名前をnew_fieldで指定したものに変更

- フィールドの削除

```bash
> db.コレクション名.update({field1: val1}, {$unset: {field2: ""}});
```

field2を削除

### インデックスの操作
- メリット

フィールドにインデックスを付けることで、データの検索が早くなる

- デメリット

データの追加ごとに、適切なインデックス割り当てる必要があり、逆に効率が悪くなる可能性がある

#### インデックスの表示

```bash
> db.コレクション名.getIndexes();
```

#### インデックスの追加

```bash
# 昇順にインデックスを付与する場合
> db.コレクション名.createIndex({インデックス名: 1})

# 降順にインデックスを付与する場合
> db.コレクション名.createIndex({インデックス名: -1})
```

#### ユニークの指定
インデックスにユニークを指定することで、データの重複を禁止することができる

```bash
> db.コレクション名.createIndex({インデックス名: 1}, {unique: true})
```
#### インデックスの削除

```bash
# 昇順のインデックス
> db.コレクション名.dropIndex("インデックス名_1")

# 降順のインデックス
> db.コレクション名.dropIndex("インデックス名_-1")
```

※ 昇順か降順を忘れた場合は、`getIndexes()`で確認

## バックアップと復元

#### バックアップ

```bash
$ mongodump -d データベース名
```

`dump`フォルダーをカレントディレクトリが作成される

#### 復元

```bash
$ mongorestore --drop
```

`--drop`オプションを付けると、既存のデータベースを消して、バックアップに置き換える