ドキュメントの更新
ドキュメントの更新を行うためのメソッドは、update()、save()、findAndModify()の3種類が用意されています。
update()
与えられた条件にマッチするドキュメントを新しいドキュメントで更新する。
save()
引数に指定したドキュメントの"_id"が存在していればupdate、存在しなければinsertを行う。
findAndModify()
アトミックにドキュメントを更新して取得する。
では、それぞれの使い方を例を示しつつ解説していきます。
update()メソッドが基本です。update()は以下のような引数を取ります。
db.collection.update(<条件>, <更新内容>, <upsert,> <multi>)
第1引数の条件には、先ほどの検索と同様の条件が指定できます。第2引数には更新内容をJSON形式で表現したものを指定します。更新はドキュメント全体を更新することもできますし、特定のフィールドだけ更新することもできます。特定のフィールドだけ更新する場合などには、update operatorというもので更新内容を指定します。update operatorにはいくつかありますので、こちらも公式のドキュメントに目を通しておくことをおすすめします。
また、オプションとして"upsert"と"multi"があり、それぞれtrue/falseで指定します。まず、"upsert"オプションは指定した条件にマッチするドキュメントが存在しない場合に、更新内容で指定したドキュメントを新規作成するというオプションです。次に"multi"についてですがMongoDBのupdate()はデフォルトでは条件にマッチにした最初のドキュメントのみ更新を行うという動作をします。しかし、条件にマッチするドキュメントが複数ある場合など、複数ドキュメントの更新を行いたい場合もあります。そういった場合に"multi"オプションをtrueにセットします。
> db.sample_coll.find()
{ "_id" : ObjectId("510779ccb796a688e5d541aa"), "key3" : "value3", "key4" : "value4" }
{ "_id" : ObjectId("51077a02b796a688e5d541ab"), "key1" : "value1_1", "key2" : "value2_1" }
{ "_id" : ObjectId("51068b04b796a688e5d541a9"), "key1" : "value1", "key2" : "value2" }
> db.sample_coll.update({ "_id" : ObjectId("51068b04b796a688e5d541a9")}, {$set : { "key1" : "value100", "key2" : "value200" }},false,true)
> db.sample_coll.find()
{ "_id" : ObjectId("510779ccb796a688e5d541aa"), "key3" : "value3", "key4" : "value4" }
{ "_id" : ObjectId("51077a02b796a688e5d541ab"), "key1" : "value1_1", "key2" : "value2_1" }
{ "_id" : ObjectId("51068b04b796a688e5d541a9"), "key1" : "value100", "key2" : "value200" }
この例では、先ほどまでのデータのうち_idがObjectId("51068b04b796a688e5d541a9")のドキュメントを対象にとして、key1フィールドとkey2フィールドの値をそれぞれvalue100とvalue200に更新しています。また、multiアップデートのオプションは有効とし、upsertオプションは無効としています。update()の実行後のfind()の結果を見てみると、対象のドキュメントの値だけが更新されていることが確認できるでしょう。
次にsave()です。このメソッドはupdate()のupsertをtrueにし、条件のクエリに_idフィールドを指定したものと同じです。
db.collection.save( <ドキュメント> )
引数として指定できるのは、JSON形式で指定するドキュメントのデータのみです。このドキュメントの内容に_idフィールドとその値が含まれていて、かつ一致するドキュメントが存在する場合に、そのドキュメントは指定されたドキュメントの内容で更新されます。_idフィールドが指定されていない、もしくは指定されていてもマッチするドキュメントがない場合は、指定された内容でドキュメントが新規作成されます。
> db.sample_coll.find()
{ "_id" : ObjectId("510779ccb796a688e5d541aa"), "key3" : "value3", "key4" : "value4" }
{ "_id" : ObjectId("51077a02b796a688e5d541ab"), "key1" : "value1_1", "key2" : "value2_1" }
{ "_id" : ObjectId("51068b04b796a688e5d541a9"), "key1" : "value100", "key2" : "value200" }
> db.sample_coll.save({ "_id" : ObjectId("510779ccb796a688e5d541aa"), "key3" : "value300", "key4" : "value400" })
> db.sample_coll.find()
{ "_id" : ObjectId("51077a02b796a688e5d541ab"), "key1" : "value1_1", "key2" : "value2_1" }
{ "_id" : ObjectId("51068b04b796a688e5d541a9"), "key1" : "value100", "key2" : "value200" }
{ "_id" : ObjectId("510779ccb796a688e5d541aa"), "key3" : "value300", "key4" : "value400" }
> db.sample_coll.save({ "key5" : "value5", "key6" : "value6" })
> db.sample_coll.find()
{ "_id" : ObjectId("51077a02b796a688e5d541ab"), "key1" : "value1_1", "key2" : "value2_1" }
{ "_id" : ObjectId("51068b04b796a688e5d541a9"), "key1" : "value100", "key2" : "value200" }
{ "_id" : ObjectId("510779ccb796a688e5d541aa"), "key3" : "value300", "key4" : "value400" }
{ "_id" : ObjectId("5107bb5bb796a688e5d541ac"), "key5" : "value5", "key6" : "value6" }
上記の例では、_idがObjectId("510779ccb796a688e5d541aa")のドキュメントを対象にkey3とkey4のフィールドの値をsave()で更新しています。続けて、今度は_idフィールドを指定せずにsave()を実行したところ、新たなドキュメントとして追加されていることが確認できます。
最後にfindAndModify()です。これはドキュメントの更新と取得をアトミックに行うものです。例えば、カウンタのような値をドキュメント内で保持している場合で考えてみましょう。値を1増やした後の結果を取得する処理を行う時、1つのクライアントであれば特に問題はありません。ところが複数のクライアントが同時に行った場合など、重複した値を読み取ってしまう場合があります。そういった場合に使うものがfindAndModify()です。サンプルとして実際にカウンタを持つドキュメントを新しいコレクションに作成し、findAndModify()で更新をしてみます。
> db.seq.insert({ value : 1 })
> db.seq.find()
{ "_id" : ObjectId("5108bd38b796a688e5d541af"), "value" : 1 }
> db.seq.findAndModify({ query : { "_id" : ObjectId("5108bd38b796a688e5d541af") }, update : { $inc : { value : 1 }}, upsert : false, new : true })
{ "_id" : ObjectId("5108bd38b796a688e5d541af"), "value" : 2 }
1行目で新しいドキュメントを作成しています。2行目のfind()で表示されている通りこの時点ではvalueフィールドの値は1です。これをfindAndModify()を実行すると値をインクリメントした後の結果が取得されています。findAndModify()の引数ですが、query/update/upsertに関してはupdate()と同じですが、queryに関して条件にマッチするドキュメントは1件だけである必要があります。newをtrueにすると例のように更新後の値が取得できます。falseの場合は更新前の値が返ってきます。
なお、$incというのはupdate()で使えるmodifirの一つで値を1つ増加させるものです。findAndModify()は、この他にもsortやremoveなどオプショナルな引数が存在しますので、公式ドキュメントで確認してください。
ドキュメントの削除
ドキュメントの削除はシンプルで、remove()メソッドを実行するだけです。remove()メソッドの引数に何も指定しないで実行すると、コレクションに含まれるドキュメントすべてが削除されます。条件にマッチするドキュメントを削除したい場合は、第1引数にfind()などと同じように条件を指定します。また、第2引数としてtrueを指定すると条件にマッチしたドキュメントから1件だけ削除するという挙動になります。
> db.sample_coll.find()
{ "_id" : ObjectId("51077a02b796a688e5d541ab"), "key1" : "value1_1", "key2" : "value2_1" }
{ "_id" : ObjectId("51068b04b796a688e5d541a9"), "key1" : "value100", "key2" : "value200" }
{ "_id" : ObjectId("510779ccb796a688e5d541aa"), "key3" : "value300", "key4" : "value400" }
{ "_id" : ObjectId("5107bb5bb796a688e5d541ac"), "key5" : "value5", "key6" : "value6" }
> db.sample_coll.remove({ "_id" : ObjectId("51077a02b796a688e5d541ab") })
> db.sample_coll.find()
{ "_id" : ObjectId("51068b04b796a688e5d541a9"), "key1" : "value100", "key2" : "value200" }
{ "_id" : ObjectId("510779ccb796a688e5d541aa"), "key3" : "value300", "key4" : "value400" }
{ "_id" : ObjectId("5107bb5bb796a688e5d541ac"), "key5" : "value5", "key6" : "value6" }
> db.sample_coll.remove()
> db.sample_coll.find()
上記の例では、最初に_idの値を指定して削除を行っています。その後、残りのドキュメントをすべて削除しています。
なお、条件を指定せずに実行するremove()とよく似たメソッドとして、drop()というものが存在します。条件を指定せずに実行するremove()はあくまでもコレクション内のドキュメントをすべて削除するのに対して、drop()はコレクションそのものを削除します。
まとめ
今回はMongoDBを使ったアプリケーション開発の前提知識として、MongoDBの基本概念と基本操作を解説しました。今回解説したこと以外にも配列や埋め込みドキュメントなど、実際にMongoDBを使ったアプリケーションを開発する上で知っておくべきことがあります。次回はそれらと合わせてSpring Dataを使ってMongoDB上のデータを操作するアプリケーションの開発について解説を行います。
