MongoDBでmemcachedのincr/decrのようなことをやりたい

下記の「リアルタイム分析にMongoDBを使う」という内容のエントリに触発されて自分でも試してみた。

"upsert"と"$inc"を使えばいいということなので、とりあえず次のようなコードを書いてみる。

# -*- coding: utf-8 -*-
from pymongo.connection import Connection

conn = Connection()
db = conn.test_db

collection = db.test_collection

# 最初にキーを消しておく
collection.remove({'key': 'SPAM'})

# この時点でキーSPAMはないはずなので、valueは1
collection.update({'key': 'SPAM'},
                  {'$inc': { 'value': 1 }},
                  upsert=True)

# キーSPAMがあるので、valueは1増えて2
collection.update({'key': 'SPAM'},
                  {'$inc': { 'value': 1 }},
                  upsert=True)

value = collection.find_one({'key': 'SPAM'})['value']
assert value == 2, value

upsertを使うと結果が返ってこないので、更新・追加は1クエリで実行できても、最後にデータ取得のクエリを実行するはめになる。memcacheプロトコルのincr/decrのように1オペレーションではできない。元記事に書いてあるように、「結果を取得する必要がない」という条件がないとだめだなぁ。

MongoDBのドキュメントを追っていて、サーバーサイドでJavaScriptを実行できるということで、下記のページにあった、"Example of using db.eval() for doing an atomic increment"というコードを参考にJavaScriptで「取得・更新・保存」という処理を書いてみる。

# -*- coding: utf-8 -*-
from pymongo.connection import Connection

conn = Connection()
db = conn.test_db

collection = db.test_collection
collection.remove({'key': 'EGG'})

incr = Code("""function() {
  var t = db.test_collection.findOne({ key: key });
  t = t || { key: key, value: 0 };
  t.value++;
  db.test_collection.save(t);
  return t;
}""", { 'key': 'EGG' })

assert db.eval(incr)['value'] == 1
assert db.eval(incr)['value'] == 2

これで本当に処理がAtomicならば、evalを呼ぶとデータベース全体にロックがかかるということなんだろうか?あと、evalで実行されるJavaScriptは、データベース側のJavaScriptエンジンで毎回"eval"されるんだろうか?そもそも、MongoDBのJavaScriptエンジンって何だろう?