Java MongoDB Driverを使ってMongoDBへ接続・ドキュメントのCRUD

# Java MongoDB Driverとは
https://docs.mongodb.com/ecosystem/drivers/java/

javaから指定のMongoDBにアクセスして、中のデータを扱うためのドライバです。
BSON(バイナリ型JSON、MongoDBで主に扱われるフォーマット)のライブラリもある。

# jarをダウンロード

[公式github](https://github.com/mongodb/mongo-java-driver/releases)で過去バージョンも含めて配布されているので、こちらからダウンロードが可能。
Mavenだったらpomに書き加えるだけなので楽。(ステマじゃないよ)

```pom.xml
<dependencies>
    <dependency>
        <groupId>org.mongodb</groupId>
        <artifactId>mongodb-driver</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>
```

あるいはGradle。

```build.gradle
dependencies {
   compile 'org.mongodb:mongo-java-driver:3.3.0'
}
```

# 接続
接続は、以下のステップで組んでいきます。

## クライアント作成

クライアント作成は以下のように設定します。
引数にhostやportを指定したり、サーバーアドレスを指定したり。
お手元のMongoDBの環境に合わせて設定してください。

(以下は一例です。
詳しくは→ http://api.mongodb.com/java/2.10.1/com/mongodb/MongoClient.html )

```java
//基本形
MongoCliant cliant = new MongoCliant();


//hostとport設定
String host = "localhost";
int port = 27017;
MongoCliant cliant = new MongoCliant(host, port);


//複数のサーバーを対象としたアクセスの場合
ServerAddress primary = new ServerAddress("hoge", port);
ServerAddress secondary = new ServerAddress("fuga", port);
List<ServerAddress> addressArr = new ArrayList<>(Arrays.asList(primary, secondary));

MongoCliant cliant = new MongoCliant(addressArr);
```

レプリカセットを組んでいるなど、複数のDBで冗長構成している場合、一番下のように設定することで、勝手に接続するDBを選んでくれます。ちなみにServerAddressもMongoDBドライバAPIの一つで、MongoCliant作成時と同様、hostやportなどを指定することで作成することができます。

## DB・collection取得

クライアントを通じてデータベースとコレクションを取得します。
**※MongoDBでは、テーブルのことを「コレクション」といいます。**

```java
//DB取得
String databaseName = "dbname";
MongoDatabase database = cliant.getDatabase(databaseName);

//collection取得
String collectionName = "collection";
MongoCollection<Document> coll = mongoDb.getCollection(collectionName);
```

これで、ドキュメントにアクセスするための準備ができました。

## (option)read preference設定
こちらに書きました:v_tone1:
http://qiita.com/ricoirico/items/3d83baba6c191806f097

基本的にはMongoCollectionインスタンス、
或いはMongoDatabaseインスタンスを作成するときに(または作成した後)付加する形で設定します。

# Read
コレクションの中のドキュメントを参照・取得します。

## ドキュメント件数取得
long型で返ってきます。

```java
long cnt = coll.count();

System.out.println(cnt);
```

## ドキュメント全件取得

```java
String doc = "";
FindIterable<Document> iterator = coll.find();
MongoCursor cursor = iterator.iterator();

while(cursor.hasNext()){
     doc += cursor.next();
     doc += System.getProperty("line.separator");
}

System.out.println(doc); //出力例: Document{{_id=582dbfd76d255a19a02b22b2, name=test}}
```
FindIterableとかいう見慣れないクラスが出てきました。
これもドライバAPIの一つで、データ検索用クラスです。
上記のようにMongoCursorと組み合わせて、iteratorとして使います。

(ちなみに、iteratorについてはこちらが詳しいです→ http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1345203893 )

``cursor.next();``はObject型で返ってきますので、そのままStringで持たせることも可。

## パラメータを指定してドキュメント取得

```java
String doc = "";
String key = "name";
String param = "test";
Document query = new Document(key, param);

FindIterable<Document> iterator = coll.find(query);
MongoCursor cursor = iterator.iterator();

while(cursor.hasNext()){
     doc += cursor.next();
     doc += System.getProperty("line.separator");
}

System.out.println(doc);

```

単純で、iteratorに検索条件を引数として持たせるだけです。
bsonインターフェースの実装クラスであるDocumentクラスにぶちこむことで、MongoDB内でも読める形式に変換している・・・という推測。
ちなみに、複数の検索条件を設定する場合は、連想配列で格納します。以下のように。

```java
Map<String, Object> map = new HashMap<String, Object>(){
    {put("name", "ほげ山ほげ太郎");}
    {put("age", 13);}
};
Document query = new Document(map);
```

# Create
データの挿入は、先程書いたDocumentクラスを使います。
タイムスタンプは、Date型で突っ込んでおけば、Document側で勝手にやってくれます。便利！

```java
Document doc = new Document();

doc.append("name", "ほげ山ほげ太郎");
doc.append("age", 13);
Date currentDate = new Date();
doc.append("lastModified", "ISODate("+currentDate+")");

coll.insertOne(doc);

```

ObjectIDはMongoDBから直接突っ込んだときと同様、自動生成されます。
以前はJson形式の文字列をパースすることで作成できたようですが、
ドライバのバージョンアップに伴い、できなくなったのか非推奨になったのか、
実行例を見つけることができませんでした。残念。

複数のデータを同時に挿入する場合は、``insertMany()``を使います。
Document型のListが引数として対応しています。

# Update

http://qiita.com/saba1024/items/f2ad56f2a3ba7aaf8521#updateコマンド---ドキュメントレコードの更新

↑こちらにもあるとおり、MongoDBはデータ更新の際に指定した内容でそっくり上書きするという独特の挙動をします。回避策としてオプション文字があるわけですが、本ドライバでもドキュメント更新の際は同じように使えます。

``` java
Document updDoc = new Document("name", "ピコ山ピコ太郎");

coll.updateOne(Filters.eq("name", "ほげ山ほげ太郎"), new Document("$set", updDoc));
```

構造的にはちょっとややこしいですが・・・
Filtersクラスのeqメソッドで一致するドキュメントを検索し、一致したデータにオプション文字を使ったアップデート用ドキュメントで更新をかける仕組みです。
インサートと同じく、アップデートにも``updateMany()``というメソッドがあって、こちらは一致するドキュメントを**すべて**更新します。

## Upsert

「一致するデータがあったら更新、なかったら新規作成」という挙動をさせたいとき、同じく``updateOne()``を使えば実現可能です。
唯一異なる点は、UpdateOptionsを設定すること。

```java
Document updDoc = new Document("name", "ピコ山ピコ太郎");

UpdateOptions option = new UpdateOptions().upsert(true);

coll.updateOne(Filters.eq("name", "ほげ山ほげ太郎"), new Document("$set", updDoc), option);
```

# Delete

##ドキュメントの削除
ドキュメントのDeleteは、Mongoからの直接操作と同じく、条件をFiltersで指定して``deleteOne()``です。
察しの良い方はわかるかもしれませんが、``deleteMany()``メソッドも用意されています。

```java
//条件に一致するドキュメント1件削除
coll.deleteOne(Filters.eq("name", "test"));

//条件に一致するドキュメント全件削除
coll.deleteMany(Filters.eq("name", "test"));
```

## コレクションの削除

``drop()``メソッドを使ってコレクションごと削除することもできます。
``DROP DATABASE``というくらいですから、これはSQLユーザーからもおなじみ。
個人的に、イメージとしてはMongoDatabaseから``dropCollection("collection_name")``みたいにできたほうが分かりやすい気がするのですが、そういうのはないみたいです[^1]。

```java
coll.drop();
```

[^1]: MongoDatabase.drop()というdatabaseそのものを消し去るメソッドがありますので、誤用には注意。

# コネクション削除

データの操作が終わったら、クライアントのコネクションをクローズします。

```java
cliant.close();
```

なお、MongoCliantもtry-with-Resourcesを使うことで、クローズ処理の記入をスキップできます。

```java
try (MongoClient cliant = new MongoCliant(addressArr)) {
    
　　//DBを色々操作
　　//本当は最後にcliant.close()としなければいけないところをスキップできる
}
```

try-with-Resources構文についてはこちらからどうぞ↓
https://docs.oracle.com/javase/jp/7/technotes/guides/language/try-with-resources.html

# おしまい
以上、基本的な処理について一通り書き出してみました。
MongoDBはNoSQLで、データ構造がかっちりしたMySQLやpostgresなどに慣れているとちょっと癖がありますが、ドライバ自体は順序立てて見ていけばそれほど複雑ではないですし、CRUD部分についてはMongoのクライアントから書くのとかなり似ている部分も多いので、Javaに慣れていればとっつきやすいような気がします。
他にもAPIはたくさんありますので、やりたいことが実現可能かどうか、公式ドキュメント等で調べてみると良いのではないでしょうか。おわり。