Javaでざっくり理解するMongoDB

## 概要
最近バックエンドの開発でMongoに触れる機会が増えたため、理解を深める目的で記事を書いていきます。

また、本記事は<a href="https://s3-ap-northeast-1.amazonaws.com/azusa.me.blog/blog/wp-content/uploads/2015/05/the-little-mongodb-book-ja.pdf">MongoDBの薄い本</a>を基に作成しています。
今回説明する内容はMongoの概要や基本的なCRUD操作になりますので、より詳しく学びたい方は、ぜひ一読してみてください。

## MongoDBとは
MongoDBは、MySQLのようにデータを行と列で保存(RDBMS)するのではなく、複数のJSONに似たドキュメントやコレクションでデータを表示する、ドキュメント指向データベース(NoSQL)です。


RDBMSと比較すると以下のようなメリットがあります。

・**データモデルを変更する際にスキーマ変更が不要(スキーマレス)**
・**データの分散処理が比較的容易なため、大規模なデータの処理が可能**
・**高速な読み書き操作**
・**データの格納に柔軟性がある**
・**大規模なアプリケーションやデータセットに対して水平方向にスケールアウト可能**

## 基礎
Mongoの基礎については以下説明の通りです。
> MongoDB はデータベースを作成し、その中にはコレクションを含みます。
コレクションはドキュメントを作成します。それぞれのドキュメントはフィールドを作成します。コレクションはインデックス化可能であり、これは参照やソートの性能を改善します。最後に、MongoDB からデータを取得する際、カーソルを経由して操作を行います。

### ドキュメント
KeyValue型で保存されます。Jsonみたいな感じですね。
```java
{
    "name" : "Suzuki Ichiro",
    "age" : 24,
    "gender" : "male",
    "job" : "engineer",
    "height" : 180,
    "hobby" : "baseball"
    "login_time" : "20240405130000"
}
```
**コレクション**はドキュメントの集まりのことです。

### フィールド
ドキュメント内の各要素のことを指します。
```java
"name" : "Suzuki Ichiro"
```

### カーソル
**クエリ**（データベースから情報を取得したり、データベースに対して操作を実行したりするための要求または命令）の結果をセットするためのインターフェースです。

```java
// コレクションを紐づける
collection = db.getCollection("userListCollection");

// カーソルの準備
MongoCursor<Document> cursor = null;

// 名前がSuzuki Ichiroのドキュメントを検索して見つかったら返す
try {
    BasicDBObject findQuery = new BasicDBObject();
    findQuery.append("name", "Suzuki Ichiro");

    cursor = collection.find(findQuery).iterator();
    while (cursor.hasNext()) {
        Document doc = cursor.next();
        String name = doc.getString("name");
        return name;
    }
} catch (Exception ex) {
    throw new Exception;
} finally {
    if (cursor != null) {
        cursor.close();
    }
}
```

## 検索
findコマンドを使ってドキュメントを取得します。
```Java
collection.find(findQuery).iterator();
```
また、検索時には**クエリ**を使って条件を追加することができます。
```java
// 年齢が20歳以上のドキュメントを取得する
Document query = new Document("age", new Document("$gte", 20));
FindIterable<Document> results = collection.find(query);
return results;
```
**gte**は比較演算子と呼ばれるものの一つです。他には以下のようなものがあります。
| 演算子 | 説明               |
|--------|--------------------|
| $eq    | 等しい             |
| $gt    | より大きい         |
| $gte   | 以上               |
| $lt    | より小さい         |
| $lte   | 以下               |
| $ne    | 等しくない         |
| $in    | リスト内のいずれか |
| $nin   | リスト内のどれでもない |

また、複数の条件を追加することもできます。
```java
// 年齢が20歳以上かつ名前がSuzuki Ichiroのドキュメントを取得する
Document query = new Document();
query.append("age", new Document("$gte", 20));
query.append("name", "Suzuki Ichiro");
FindIterable<Document> results = collection.find(query);
return results;
```

## 登録
ドキュメントを追加する時は**insertOne**コマンドを使用します。

```Java
Document newDocument = new Document("name", "Tanaka Taro")
    .append("age", 35)
    .append("city", "Tokyo");

collection.insertOne(newDocument);
```
また、複数追加したい時は**insertMany**を使用します。
```java
List<Document> documents = new ArrayList<>();
documents.add(new Document("name", "Tanaka Taro").append("age", 35).append("city", "Tokyo"));
documents.add(new Document("name", "Suzuki Ichiro").append("age", 30).append("city", "Osaka"));

collection.insertMany(documents);
```

## 更新
**更新のみ**の場合には**updateOne**や**updateMany**コマンドを使用します。
```java
// 更新対象のクエリを作成
Document query = new Document("name", "Tanaka Taro");

// 更新内容を作成
Document update = new Document("$set", new Document("age", 40).append("city", "Yokohama"));

// 更新実行
UpdateResult result = collection.updateOne(query, update);

// 更新結果をチェック
if (result.getModifiedCount() > 0) {
    System.out.println("ドキュメントが更新されました");
} else {
    System.out.println("更新対象のドキュメントが見つかりませんでした");
}
```

また、登録と更新を同時に行うことができる**upsert**コマンドもあります。
```java
// 更新対象のクエリを作成
Document query = new Document("name", "Tanaka Taro");

// 更新内容を作成
Document update = new Document("$set", new Document("age", 40).append("city", "Yokohama"));

// upsertオプションを指定して更新実行
UpdateOptions options = new UpdateOptions().upsert(true);
UpdateResult result = collection.updateOne(query, update, options);

// 更新結果をチェック
if (result.getUpsertedId() != null) {
    System.out.println("新規ドキュメントが挿入されました");
} else if (result.getModifiedCount() > 0) {
    System.out.println("ドキュメントが更新されました");
} else {
    System.out.println("更新対象のドキュメントが見つかりませんでした");
}
```
上記はnameがTanaka Taroであるドキュメントが存在しない場合に新規ドキュメントとして挿入し、存在する場合はageとcityを更新する操作を行っています。


## 削除
削除には**deleteOne**や**deleteMany**コマンドを使用します。
```java
// 削除対象のクエリを作成
Document query = new Document("name", "Tanaka Taro");

// 削除実行
DeleteResult result = collection.deleteOne(query);

// 削除結果をチェック
if (result.getDeletedCount() > 0) {
    System.out.println("ドキュメントが削除されました");
} else {
    System.out.println("削除対象のドキュメントが見つかりませんでした");
}
```

## 最後に
今回はMongoDBの基本的な概要と操作を紹介しました。
今の所これらの知識のみで仕事はなんとかこなせています（気がします笑）
この記事がMongoDBをこれから学ぶ方の一助になれば幸いです。