MongoDBでなんやらデータをいじってると、配列をネストして持つようなことが多々あります。例えば以下。

> db.test.find()
{
  "_id": ObjectId("f70bafc4be4c41708aeae77b"),
  "test": [
    {"id": "id_1", "val": 1},
    {"id": "id_2", "val": 2},
    {"id": "id_3", "val": 3}
  ]
}

これを見ていると、だんだんid_2valをupdateしたくなってくるでしょう…?

そんな時は以下のように指定してupdateできます。

> db.test.update({"_id": ObjectId("f70bafc4be4c41708aeae77b")}, {$set: {"test.1.val": 10}})
{
  "_id": ObjectId("f70bafc4be4c41708aeae77b"),
  "test": [
    {"id": "id_1", "val": 1},
    {"id": "id_2", "val": 10},
    {"id": "id_3", "val": 3}
  ]
}

test.1.valという風に、key.index.keyで指定できます。

そしてさらに汎用的に、以下のようにもできます。

> db.test.update({"_id": ObjectId("f70bafc4be4c41708aeae77b"), "test.id": "id_3"}, {$inc: {"test.$.val": 1}})
{
  "_id": ObjectId("f70bafc4be4c41708aeae77b"),
  "test": [
    {"id": "id_1", "val": 1},
    {"id": "id_2", "val": 10},
    {"id": "id_3", "val": 4}
  ]
}

updateの検索クエリ部分で、対象のidを持つオブジェクトまで絞り込み、test.$.valという風に、$マークを使用することで、絞り込んだオブジェクトのインデックスを指定せずに、インクリメントすることができました。

もちろん、ハッシュキーも指定できていることがわかりますね。

ただしこの方法だと、インクリメント対象がオブジェクトの配列の場合に、自動的に配列にappend(insert)されません…ハッシュだとできるのですが、配列で持ちたい場合もあるため、分岐を書かないといけないかもしれませんね。もし良い方法を知っていたら教えて下さい笑

これらはとても便利なので、是非使ってみてください。