下記の「リアルタイム分析にMongoDBを使う」という内容のエントリに触発されて自分でも試してみた。
- Using Mongo for Real-Time Analytics
- http://www.10gen.com/blog/2009/3/using-mongo-for-real-time-analytics
"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エンジンって何だろう?