検索結果をCouchDBにとっておく。

昨日、twitter のTLで流れてはっとしたのですが、確かにtwitterの検索結果、履歴がさかのぼれなくなることがあって、これはこれで不便だなぁ、というときがあります。ということで、例によってCouchDBに放り込んじゃいましょう。今回は年も変わったということで、今後触る機会が減るであろうPythonで書きました(去年はPython結構仕事で書いた...)。

twitter の search APItwitter.com/search.json?q=XXX でとれて、認証がいらない、その上、必ず次のページへのリンクを"next_page"というフィールドに含めてくれるので非常に使いやすかったです。pagingをするAPIを提供する場合は、next/prev はHTMLだろうがJSONだろうがXMLだろうがちゃんと提供すべき。ハイパーリンク重要だっていってるじゃん。ちょうどCouchDB本のページングのレシピの部分を翻訳しているんだが、こんなのレシピでも何でもない、Viewの結果に next: "startkey=XXXX&...", prev:"startkey=XXX" というフィールドいれればいいだけじゃん、と一昨年の年末ぼやいてたっけか。変わってない。

検索結果は認証がいらないので、後日キーワード監視?サービスとして公開することにしようかと思います。これで、

  • キーワードを登録して保管しておける。
  • サービスとしてどんな検索キーワードがモニターされているのかがわかる(タグクラウドっぽく)
  • あるtweetに対して、どんな検索キーワードを引っかけられているのかがわかる。

かなぁ。似たようなのありそうな気もしますが、ひとまず自分がほしいのと、探して試すより作った方が早そうなので。

あとは、ネガティブtweetが多いのかポジティブtweetが多いのか、とか盛り上がり具合とか。。その辺は 2004年頃のBlogの付加価値サービスとかわらんですね。そこまではやるかどうかしりませんが、githubにおいとくことにします。

とりあえず以下は書いたPythonスクリプトPythonは素人です。こんなのを書いていたら除夜の鐘が鳴っていました。

続きを読む

update フィルタと validation のコンビネーションでHTTPを制御する。

http://d.hatena.ne.jp/yssk22/20091212/1260617233 に書いたupdateフィルターですが、こんなことをしたくなるかもしれません。

// test/updates/foo.js 
function(doc, req){
   var v = parseInt(req.form.value)
   if( v > 0 ){
      doc.value = v;
      return [doc, "OK"];
   }else{
      return [null, {code: 403, body: "forbidden"}]
   }
}

つまり、予期しないパラメーターが渡されたらドキュメントの更新をやめて(returnの第一要素がnullの場合は、何もしません)、4XX のものを返したくなるでしょう。403よりも400のほうがよさそうですが、それはおいといて、updateフィルター関数内ではHTTPのステータスコードを制御できないようになっています。

この場合は、validate_doc_update.js を使っておきます。

// test/updates/foo.js 
function(doc, req){
   doc.v = parseInt(req.form.value)
   return [doc, "OK"];
}

// test/validate_doc_update.js
function(newDoc, oldDoc, userCtx){
   if(!(newDoc.value > 0)){
      throw({forbidden: "forbidden"})
   }
}

こうやっておくと、updates/foo/docId をformを使ってvalue=-1などでたたいたときには、ちゃんと 403 がかえるようになります。つまり、updateフィルタを通過したドキュメントがvalidateにかけられてそこで例外が投げられると、レスポンスを上書きできる(この場合 "OK" => "forbidden" にして 200 -> 403 にする)ことになります。

大抵はこうやって、validateはvalidate_doc_update.js に。updateフィルタでは検証せずとりあえず値をセットしておく、ということになります。

これはこれでいいのですが、たまに不便で、updateフィルターで、不整合が起こりそうだったら更新そのものを中止することを明示的に書いておきたい場合があります。

// test/updates/foo.js 
function(doc, req){
    // なんか不整合が起こった
    throw("error");
}

こんな形でかけるといいのですが、あいにく、update フィルタ内の例外は厳禁です。500 になるだけです。なので、"エラードキュメント"を返してやることで、validate_doc_update.js と連携するとよいです。

// test/updates/foo.js 
function(doc, req){
    try{
       // いろいろやる
       // 例外を投げる
       throw({forbidden: "forbidden"});
    }catch(e){
       // 例外を拾って、UpdateErrorDoc という架空のドキュメントタイプでvalidate_doc_update に伝える
       return [{type: "UpdateErrorDoc", error: e}, "ここは使わない];
    }
}

// test/validate_doc_update.js
function(newDoc, oldDoc, userCtx){
   // UpdateErrorDoc は update フィルタから送られてきた例外検知ドキュメントなので、有無をいわさずにthrowする。
   if( newDoc.type == "UpdateErrorDoc"){ 
      throw(newDoc.error) 
   }
}

こうすると、updateフィルタ内でも4XXや3XXなどのステータスコードを返す処理もかけるようになります。update フィルタはHTTPを扱うものの、実質はMVCでいうところのMでしかなくて、エラーになる場合はモデルの処理でエラーオブジェクト(ドキュメント)を返すだけにすることで、C相当のvalidate_doc_updateで正しくHTTPを操作してあげるのが良さそうです。

なんか、とっつきづらい気もしますが、これはこれで意外と便利だったりします。