MongoDB入門してみた（１）CRUD操作

特にきちんとした脈絡もなく記述。
ほとんど自分向けの備忘録なので雑に書いてます。

**純正のMongoDBではなく、Percona Server for mongoDBを利用している点には注意**

# 目的
mongoDBの基本的な操作方法(CRUD)を学ぶ

# MongoDBの導入

ここではあえて純正のMongoDBではなく、
Percona server for MongoDBを利用する。
セットアップ手順についても公式のDockerイメージを導入することで
利用を行う。

```bash:PerconaServerの導入
sudo docker pull percona/percona-server-mongodb
sudo docker run -d --name percona -p 3306:3306 percona/percona-server-mongodb
```

これで起動完了。

# MongoDBの操作

## MongoDBへの接続

```bash:MongoDBへの接続。
sudo docker exec -it percona /bin/bash
mongo
```

これで接続はOK。

## ドキュメントへのCRUD

### ドキュメントの作成(Create)

```bash:利用するデータベースの指定
mongo
use foo
```

mongoコマンドでmongo shellに入って利用するdb名を指定する。

```bash:insertによるドキュメント作成例
db.sample.insert({name: "test"})
```

```bash:特定コレクション内のドキュメント数のカウント
db.コレクション名.count()
1
```

### ドキュメントの読み込み(Read)

findでは一定数(デフォルトでは20件)分のカーソルを返す。

``` bash:読み込み
db.コレクション名.find()
{ "_id" : ObjectId("5867deb238a8218f8be46c55"), "name" : "test" }
```

findOneでは単一のドキュメントを返す

```bash:読み込みその2
db.コレクション名.findOne()
{ "_id" : ObjectId("5867deb238a8218f8be46c55"), "name" : "test" }
```

```bash:find,findOneの記述
db.コレクション名.find(検索条件, 射影)
db.コレクション名.findOne(検索条件, 射影)
```



#### 条件絞り込みのオペレータ

##### 範囲の確認

検索条件において一致だけではなく、数値の範囲を指定する場合に利用

|オペレータ|意味|
|---|---|
|$gt|〜より大きい(Greater Than)|
|$gte|〜以上(Greater Than or Equal|
|$lt|〜未満(Less Than)|
|$lte|〜以下(Less Than or Equal|

#####フィールドの存在の有無の確認($exists)

```bash:$existsオペレータ
db.コレクション名.find({フィールド名: {$exists: true}})
```
特定のフィールド名が存在するドキュメントを表示

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

```bash:ドキュメントの更新。
var doc1 = db.コレクション名.findOne()
doc1.フィールド名 = 更新値
```

#### オペレータによる操作
ドキュメントの値(特に数値)を更新する際には、
オペレータを利用する。

|オペレータ|内容|
|---|---|
|$set|値の設定|
|$inc|値の加算(減算)|
|$mul|値の乗算(除算)|
|$unset|値の削除|


##### $setオペレータによるフィールドの更新・追加

```bash:$setオペレータ
db.コレクション名.update({検索条件}, {$set: {キー: 値, ... })
```

数値情報を更新する際にはsetオペレータよりもincオペレータのほうが良い？

##### $incオペレータによるフィールド値のアトミックな更新(加算)

```bash:$incオペレータ
db.コレクション名.update({検索条件}, {$inc: {キー: 加算値, ...}})
```

既存のキーに対応する値に加える操作についてアトミックに記述することができる。

##### $mulオペレータによるフィールド値のアトミックな更新(乗算)

```bash:$mulオペレータ
db.コレクション名.update({検索条件}, {$mul: {キー: 乗算値, ...}})
```

##### $unsetオペレータによるフィールドの削除

```bash:$unsetオペレータ
db.コレクション名.update({検索条件}, {$unset: {キー: 値, ...}})
```

$unset内部で指定している値は意味がない。

#### 配列の操作
単独の値だけではなく配列の操作も必要となる

配列の操作オペレータは以下の通り

|オペレータ|意味|
|---|---|
|$push|要素の末尾に要素を一つ追加|
|$pop|要素の一つを削除|
|$pull|指定条件にマッチする要素を一つ削除|
|$pullAll|指定条件にマッチする要素すべて削除|

##### $pushによる要素の追加

```bash:$push
db.col1.insert({ary: [0, 1, 2, 3, 2, 1, 0]})
db.col1.update({ary: {$exists: true}}, {$push: {ary: 10}})
```

##### $popによる値の削除

```bash:$pop
db.col1.update({ary: {$exists: true}}, {$pop: {ary: -1}})
```

$popで"-1"を指定すると配列の先頭の値が削除される。
それ以外の値を指定すると配列末尾の値が削除される。

##### $pullによる一括削除

**条件にマッチする**要素を削除する。

```bash:$pull
db.col1.update({ary: {$exists: true}}, {$pull: {ary: {$gte: 2}}})
```

aryに含まれる値が2より大きい場合に削除
がっちまん
##### $pullAllによる一括削除

**条件に一致する**要素を削除する。

```bash:$pullAllによる削除例
db.col1.update({ary: {$exists: true}}, {$pullAll: {ary: [1,2]}})
```

aryに含まれる値が1または2の場合に削除


#### 特殊な更新(save, upsert)

ドキュメントの状態によって挙動が変わる場合としてsaveとupsertの2つが存在

##### save
```bash:save
db.コレクション名.save(挿入・更新したいドキュメント)
```

挿入・更新したいドキュメントの_idフィールドによって動作が変わる。

1. _idが既存ドキュメントと一致 => update
2. _idが既存ドキュメントと一致しない(または_idがない) => insert

##### upsert

```bash:upsert内容
db.コレクション名.update(検索条件, 挿入or更新したいドキュメント内容, {upsert: true})
```

### 複数ドキュメントの一括アップデート(multi)

```bash:一括アップデート
db.コレクション名.update(検索条件, アップデート内容, {multi:true})
```

### 検索と修正を一括で行う(findAndModify)

```text:findAndModify
db.コレクション名.findAndModify(
    query: 検索条件,
    sort: ソート条件,
    remove: 削除指定,
    update: アップデート指定,
    new: 関数の返り値指定,
    fields: 返り値の射影(sqlのselectに相当),
    bypassDocumentValidation: ドキュメントのバリデーション有無,
    writeConcern: 書き込み条件
)
```

### 削除(Delete)

#### ドキュメントの削除

```bash:ドキュメントの削除(remove)
db.コレクション名.remove(検索条件)
```

#### コレクションの削除

```bash:コレクションの削除(drop)
db.コレクション名.drop()
```

#### データベースの削除

```bash:データベースの削除(dropDatabase())
use 削除したいデータベース名
db.dropDatabase()
```



# 参考文献
[初めてのMongoDB 小笠原 徳彦著, 工学社刊](http://amzn.to/2hCKirg)