はてなキーワードのリダイレクト機能の裏側

同じ意味のキーワードを一つにまとめるリダイレクト機能を追加しました。
これまで「Google」と「グーグル」のように、一つの同じ意味であるにもかかわらず、複数のキーワードとして登録されてしまう場合がありました。今回追加した、同じ意味のキーワードをまとめるリダイレクト機能を用いることで、一つのキーワードにまとめることができるようになりました。

同じ意味のキーワードをまとめるリダイレクト機能を追加しました - はてなキーワード開発ブログ

はてなキーワードの「同じ意味のキーワードを一つにまとめるリダイレクト機能」をリリースして2週間がたちました。機能の名前が長くてわかりづらい割に地味なリリースでしたが、中身は大きく変化していますので裏側の仕組みを解説してみようと思います。

リダイレクト機能とは

そもそも、「リダイレクト機能」ってわかりづらいですね。
先行して同様の機能を実装している、Wikipediaニコニコ大百科 の説明を参照してみましょう。

リダイレクト(転送)とは、ある記事へリンクしたときに、別のページに転送する機能のことです。また、そのようなページをリダイレクトページ(転送ページ)と呼びます。
通常、ウィキペディアの記事名は正式名称で作られますが、このリダイレクトページを作っておくと、略称や別名や別の表記で記事を検索しても、実際に存在する記事へ自動的に転送されるようになります。

Wikipedia:リダイレクト - Wikipedia

記事によっては特定の記事の略称だったり微妙に違ったりで正確な名称の記事へリンクさせたい。そういう場合は以下のようにするとその記事を開いたときに自動で正しい記事へ飛ばすことができます。

リダイレクトとは (リダイレクトとは) [単語記事] - ニコニコ大百科

かいつまんで言うと、同じ意味の言葉を別の文字列で表す場合に、どちらかにまとめてあげると便利だよね!という機能です。
リダイレクト元ページにアクセスすると自動的にリダイレクト先のページに移動(転送/リダイレクト)します。だから「リダイレクト機能」って言うんですね。リリース前に「ゆらぎ機能」とか別の名称も検討したのですが、他サービスで同様の機能が先行してあって、それらが「リダイレクト」という用語を使っているのでむしろ準じた方がわかりやすかろう、という判断ではてなでもこの名前で出しました。

どんなキーワードにリダイレクトを設定すればいいのか

現在、600件ほどのリダイレクト設定をユーザーさんにしていただいています(キーワード編集にご協力してくださっているユーザーさん、いつもありがとうございます!!)。
どういうキーワードがリダイレクト設定されているか、というと大まかには以下のような分類ができそうです。

  1. 異表記
  2. 表記ゆれ
  3. 別名
  4. 略称・略号
  5. 英語/日本語

リダイレクト設定をされたキーワードにアクセスすると、転送先のページにはこのように表示されます。

「リダイレクト元を編集」リンクからリダイレクトの解除を行えるので、まずは気軽にリダイレクト設定を試していただければと思います。

もちろん、リダイレクト設定を「した方が良いケース」と「しない方が良いケース」の両方があると思います。事例をためていって、必要であればガイドライン化してみなさんに提示していきたいと考えています。

はてなキーワードにおけるリダイレクトの意味

今までの説明ですと、Wikipediaニコニコ大百科 の機能を はてなキーワード にも載っけました!というだけのようですが違います!
まず、はてなキーワードと他のサービスとの大きな違いは、他のサービスは「百科事典」ですが、はてなキーワードは単なる百科事典ではない、ということです。
もちろん言葉の意味を説明するという「百科事典」としての側面もありますが、それだけではないということです。はてなキーワードには「含むブログ」という「キーワードを使ってブログをつなげる」「キーワードにまつわる口コミをチェックできる」事ができる、というもう一つの側面があります。

そして、リダイレクト機能は、両方の側面に大きく影響する機能なのです。

百科事典 × リダイレクト機能

これは簡単ですね。同じ意味の言葉の説明は一箇所に書けばよい、同じ意味の別のキーワードは一箇所に転送してあげるのがよい、という発想です。

含むブログ × リダイレクト機能

はてなキーワードにリダイレクトを設定する意味はもう一つ(以上)あります。
リダイレクト設定されたキーワードの「含むブログ」ページには、両方の言葉のどちらかを含むブログが集まるのです!
たとえば、現在こんなリダイレクトが設定されています。

ハートキャッチプリキュアハートキャッチプリキュア! (感嘆符ある/なし)
ハートキャッチプリキュア!ハートキャッチプリキュア! (感嘆符半角/全角)

この設定により、ハートキャッチプリキュア!に関するブログ記事まとめ ページでは、感嘆符が全角なもの、半角なもの、含まないもの、全てをまとめて読むことができるのです。


「含むブログ」ページは、そもそも「あるキーワードを含むブログ」を繋げたい・一覧したい、という欲求から作られたページです。「あるキーワード」というのは「ある文字列」ではなく、同じ意味の言葉であれば異なる文字列でも同じように扱って欲しい、というのも人間から見ると当然の希望ですが、機械的にパターンマッチをした結果ではそれが叶いません。今回の機能でそれをある程度解消できたと思っています。

もちろん、リダイレクトの設定を人手で行うという対応が完全とは言えません。それが自動でなされるのが一番幸せだと思いますので、ルール化できる部分は自動でゆらぎを吸収するといった施策を次の一手として考えています。

含むブログのリダイレクト対応の裏側

含むブログは、2008年のエンジニアインターンの成果であるはてなダイアリー全文検索機能がバックエンドになっています。これは、はてなキーワードで作成されたインデックスから、ある語を含むブログエントリのドキュメントIDを高速に出力します。
この全文検索エンジンの特徴として以下のものがあります。

  • インデックス対象をはてなキーワードに絞る事によりインデックスサイズを小さく収め高速に結果を返す
  • 出力が新着順になっている
  • ドキュメントIDは新着順になっており、さらにドキュメントIDから日付などのメタ情報を算出できる
  • 特定の日付(ドキュメントID)より前、という条件で引ける

今回、含むブログをリダイレクトに対応するにあたり、全文検索エンジン自体の刷新も考えましたが、これらの特徴を活かすことでエンジンには手を入れず、クライアント側で工夫することで速度をほぼ落とさず実現することができました。

  1. 複数のキーワード毎に期間指定をして全文検索エンジンから "ドキュメントID" を引く
  2. 結果をマージする
  3. "ドキュメントID" でソートする
  4. ページ表示に必要な分を残して削除する
  5. 表示に必要な情報を日記やユーザーデータベースから引く
  6. 次のページを表示するための期間指定は最後の ドキュメントID から逆算できる
  7. ページング毎に、以上を繰り返す

工程(1)の検索エンジンへのリクエストはがリダイレクトが設定された回数だけ走りますが、ここはボトルネックにはなっておらず、むしろ工程(5)の処理が支配的でした。
ですから今回は、工程(5)の速度をこれまでより速くすることに注力し、そこをチューニングすることで表示内容をリッチにしつつユーザー体験は損ねないように工夫しました。

速度向上は、ネットワークIO要因の速度低下を減らす(発行するSQLの数をなるべく減らす。適切にキャッシュする)、回数の多い計算(今回の場合、ドキュメントIDのマージ、およびソート)に高速なアルゴリズムを適用する、といった地道な改善を続けました。

おかげで、2008年のリニューアルで少し遅くなっていた「含むブログ」を快適な速度にできたのではないかと思います。

含むブログの裏側

全文検索を導入する前の「含むブログ」は、一種類のテーブル(時間軸で分割しているので一個の、というわけではありません)に双方向インデックスを張る事で実現していました。
インデックスが双方向に張られていたのは次の二種類の目的のためでした。

  1. あるブログエントリを表示する際にキーワードリンクを施す
    • (ユーザー,エントリ) から キーワードを引く
  2. あるキーワードを含むブログエントリを表示する
    • キーワード から (ユーザー,エントリ) を 時系列で引く

このテーブルひとつで、はてなダイアリーの主機能がまかなわれていたわけですが、ブログ数の増加に伴いスケールさせる事が困難になりました。
そこで2008年のリニューアルで(2)の役割を全文検索エンジンに移行したわけです。

全文検索エンジンのお陰でスケールはするようになったのですが、別の問題が生まれます。ブログにキーワードを書いた際に、インデックスを二種類書き込まなければいけなくなりました。これにより「日記に書いたけどすぐに『含むブログ』に載らない」といった事態が発生したのです。
日記更新時に全文検索のインデックスを追加しても、アプリケーション層でキャッシュをしていると、キャッシュのパージもしてやらないと「含むブログ」に表示されるまでにタイムラグが生じてしまいます。
今回のリダイレクト対応では、アプリケーション層のキャッシュを思い切って少なくし、キャッシュのパージの仕組みをシンプル化し、「含むブログ」に表示されるまでのラグをなるべく短くする事にも注力しました。

まとめ 「キーワードでつながる」

  • キーワードにリダイレクト機能ができました
  • リダイレクト機能は、意味の同じキーワードをまとめます
  • まとめることで、百科事典的にも「含むブログ」的にも嬉しいです
  • 「含むブログ」の表示速度を向上しました
  • 日記を書いて「含むブログ」に載るまでのタイムラグも短くしています

はてなダイアリーには「キーワードでつながる面白ブログ」というキャッチコピーがありました。はてなダイアリーの原点はまさにそこだと思っていますので、「キーワードでつながる」体験をより充実させていくように、はてなダイアリーはてなキーワード両面から進化させていきたいと思います。

Test::Apache::RewriteRules で mod_rewrite のテストを書こう

YAPC::Asia Tokyo 2010 で LT してきました。以下はその資料(に少し説明を追加したもの)です。

mod_rewrite

  • 正規表現によるURL書き換えモジュール
  • スイス製アーミーナイフ / 黒魔術
  • まだ Apache 使ってますよね?
  • reverse proxy とか…

はてなmod_rewrite 活用事例

  • ほぼ reverse proxy
  • URLにより用途別のbackendに振り分ける
    • 用途によりbackendを分けリソース効率化
    • 特定のアクセスをキャッシュサーバーに振る
  • URL加工
    • Squidにキャッシュさせたいが同一URLで異なるコンテンツを返す場合がある
    • →クエリに情報を付加する
  • BAN!

便利な半面…

  • 増える!
$ cat jp.www.proxy.apache.conf | grep Rewrite | wc -l
     179
  • テストしづらい! → 一行加えるのも恐ろしい…

Test::Apache::RewriteRules

そこで、 Test::Apache::RewriteRules 。

はてな社内製テストモジュール (id:wakabatan作成) で、Apache の RewriteRule の動作をテストできます。

こちらで公開中です → http://github.com/hatena/perl5-test-apache-rewriterules

しくみ

  1. Rewrite、Redirect など path 関係のディレクティブだけを残した apache.conf を作る
  2. バックエンドを登録する
  3. Apache を起動する
  4. 実際に HTTP 要求を投げて、どの URL にアクセスがあったかを調べる

環境設定

  1. Apache2 を入れる
  2. mod_ssl など必要モジュールを入れる

SYNOPSIS

こんな apache.conf があったとして

RewriteRule /foo/(.*)  http://%{ENV:ReverseProxyedHost1}/$1 [P,L]
RewriteRule /bar/(.*)  http://%{ENV:ReverseProxyedHost2}/$1 [P,L]
RewriteRule /hoge/(.*) http://external.test/$1 [R,L]

こんなテストが書けます。

use Test::Apache::RewriteRules;
use Path::Class qw/file/;
my $apache = Test::Apache::RewriteRules->new;

$apache->add_backend(name => 'ReverseProxyedHost1');
$apache->add_backend(name => 'ReverseProxyedHost2');

$apache->rewrite_conf_f(file('apache.rewrite.conf'));

$apache->start_apache;

$apache->is_host_path('/foo/a', 'ReverseProxyedHost1', '/a');
$apache->is_host_path('/bar/b', 'ReverseProxyedHost2', '/b');,
$apache->is_redirect('/hoge/z', 'http://external.test/z');

$apache->stop_apache;

設定ファイルの書き換え

通常、Include ディレクティブなどでフルパスを指定しているので、設定ファイルをコピーして書き換えます。

文字列 or 正規表現

文字列 or コード参照

書き換え例

my $copied_conf = $apache->copy_conf_as_f($org_file, [
    "foo"         => "bar",
    "hoge"        => sub { "fuga" },
    qr/(foo|bar)/ => "baz$1",
]);
  • VirtualHost や CustomLog など関係ないディレクティブも全部消します
  • RewriteRule だけの conf ファイルだと簡単です

apache.conf をセット

$apache->rewrite_conf_f($copied_conf);

Apache の起動に必要なディレクティブなどはモジュールが勝手に用意します。

バックエンドの登録

リバースプロキシ先のバックエンドを登録します。

$apache->add_backend(name => 'BackendMainVIP');
$apache->add_backend(name => 'BackendBotVIP');
$apache->add_backend(name => 'SquidVIP');

RewriteRule で %{ENV:BackendMainVIP} のように環境変数を使える
実際の apache.conf でこのような環境変数を使って記述していない時は、 copy_conf_as_f で書き換える

Apache の起動

$apache->start_apache;

テスト (1) is_host_path

$apache->is_host_path('/' => 'BackendMainVIP', '/');
  • is_host_path .. Rewrite 後の host, path のテスト
    1. アクセスする path
    2. バックエンド名
    3. rewrite 後の path

テスト (2) is_redirect

$apache->is_redirect('/foo' => 'http://example.com/bar');
  • is_redirect .. リダイレクトのテスト
    1. アクセスする path
    2. リダイレクト先の URL

Test::Apache::RewriteRules::ClientEnvs

特定のクライアント環境を再現するブロックを提供します。

with_docomo_browser {
    $apache->is_redirect('/n/' => 'http://n/mobile/?&guid=on');
};

with_request_method {
    $apache->is_host_path('/' => 'BackendMainVIP', '/');
} 'POST';

現在サポートしている環境

Apache の停止

$apache->stop_apache;
# (DESTROY でも呼ばれますが)

まとめ

Test::Apache::RewriteRules で何百行もあるRewriteRuleに自信を持って一行追加できます!

今後も、はてな社内製の便利モジュールをもっと外に出していこうと思います。

sugio さんから漫画が届きました

今ごろ08年秋アフタヌーン四季大賞受賞のお知らせとはてな話 - 庄司創のブログ を読んで楽しみにしていましたが、 id:sugio さんからアフタヌーン四季大賞をとられた「三文未来の家庭訪問」を含む四季賞ポータブルと本誌が送られてきました!ありがとうございます!
最後の頁に似顔絵入のサインをいただいて嬉しかったです。周囲の人間も似てる似てると騒いでました。

アフタヌーンは最近全然読んでいなかったので、作品は今回初めて読ませていただきましたが、味わい深いSF作品でした!もっとちゃんとした感想書こう(あとで)!

sugio さんとは、懐かしの第一回はてなオフ会でお会いして、いろいろとお話しさせていただき、その際にとある事実を告白させていただいた事があったり、浅からぬ縁があると勝手に思っておりました。ので、今回の受賞のお話を聞いて本当に嬉しく思います。今後のご活躍に期待します。単行本化楽しみにしてますね。

ダイアリーのリニューアル

1000コミット以上、diffにすると数万行の変更を加えたダイアリーリニューアルでした。僕は複数ブログ機能を中心に取り組みました。機能面でも速度面でも改善したと思っています。でも、これで終わりではありません。ダイアリーはまだまだ進化していきますので、期待していてください。

フロム・ヘル

フロム・ヘル 上

フロム・ヘル 上

フロム・ヘル 下

フロム・ヘル 下

http://www.fromhell.jp/

みすず書房からアメコミが!しかも異常に力が入ってる!どうしたみすず!応援しています!

iPhoneのMMSではてな活用

iPhone OS 3.0 から利用可能になったMMS。Eメールがあるから使ってないよ!という方も多いと思いますが(はてな社内のiPhoneユーザーの何人かはそうでした)、なかなか便利なので使ってみるとよいと思います。はてなのサービスと連携して使うと便利なのでご紹介します。

MMSとは

マルチメディアメッセージングサービス (Multimedia Messaging Service)、MMSは3GPPとOMA(オープン・モバイル・アライアンス)によって標準化されている、携帯電話用のメッセージングサービス(メール)。

ということですが、iPhoneにおいては、メッセンジャーっぽいUIで使えるプッシュ配信メール、と考えるといいのではないでしょうか。
このページの説明がとてもわかりやすかったです。 → iPhone のメッセージとメールの使い方

MMSの設定

まずはMMSの設定をしないと始まりません。が、これが結構難関です。僕も最初「パスワード」にはまり、以下のサイトを検索で見つけてようやく設定できました。 → iPhone でMMSのアドレス(@softbank.ne.jp)を取得/変更する方法 | AppBank – iPhone, スマホのたのしみを見つけよう

@softbank.ne.jp をはてなの携帯メールアドレスに登録

MMSのアドレスは一般のsoftbankの携帯メールアドレス用ドメインと同じ softbank.ne.jp ドメインです。これは、はてなの携帯メールアドレスに登録できます。
ユーザー設定 からどうぞ。

MMSの活用(1) ダイアリー、ハイク、フォトライフへの簡単アップローダ

はてなの携帯メールアドレスに登録すると、以下のメールアドレスにメールを送るだけでダイアリー、ハイク、フォトライフへの記事や画像の投稿ができます。IM風のインターフェースがなんとなく嬉しいです。

  • d@hatena.ne.jp ... ダイアリー
  • f@hatena.ne.jp ... フォトライフ
  • h@hatena.ne.jp ... ハイク

MMSのトップには最近送受信した相手が上に並びますので、アップロード先を手軽に選んで送信できます。



MMSの活用(2) 各種お知らせメールの受け取り

はてなダイアリーやグループのトラックバック通知、スターコメント通知などをMMSで受け取るようにしておくと便利です。はてな社内グループではトラックバックやスターコメントが飛び交っておりますので、プッシュ配信されるのがとても便利なのです。
各種通知を携帯メールで受け取る設定は はてなメッセージの設定 よりどうぞ。(グループの通知メールははてなメッセージ化が済んでいないので、各グループの設定ページからお願いします。すみませんすみません…)

ちなみに僕は以下のものをMMSで受信しています。ご参考まで。

と、ここまで説明してきた内容は、iPhoneならでは!というものでなく、普通の携帯電話で実現できてきたことが、iPhoneでもMMSでできるようになったよ、というだけなんですが、冒頭に書いたように社内でMMSを使っていない人が結構いたので、ご存じない方もいるかも、と思ってわざわざエントリにしてみました。
iPhoneユーザーの方もMMSでますますはてなを便利にご利用いただけると思います。是非ご設定ください。