2006-06-01 [長年日記]
_ Zend_Search_HyperEstraier設計中 その2
いろいろいじり続けて、現在の構成は以下のような感じ。
- Zend_Search_HyperEstraier
- Zend_Search_HyperEstraier_Exception
- Zend_Search_HyperEstraier_SearchCondition
- Zend_Search_HyperEstraier_Document_Abstract
- Zend_Search_HyperEstraier_Document
- Zend_Search_HyperEstraier_DocumentList
- Zend_Search_HyperEstraier_Document_ListItem
- Zend_Search_HyperEstraier_SearchResult
- Zend_Search_HyperEstraier_Document_SearchResult
- Zend_Search_HyperEstraier_Node
- Zend_Search_HyperEstraier_Node_Api_Abstract
- Zend_Search_HyperEstraier_Node_Api_Client
- Zend_Search_HyperEstraier_Node_Api_Master
- Zend_Search_HyperEstraier_Node_Client_Interface
- Zend_Search_HyperEstraier_Node_Client
- Zend_Search_HyperEstraier_Node_Client_Distributed
- Zend_Search_HyperEstraier_Node_Client_Information
- Zend_Search_HyperEstraier_Node_Document
Zend FrameworkというかPEAR系の命名規則だと、継承関係の下位にあるクラスも、上位にあるクラス(抽象クラスやインターフェース)も、単にグルーピングされただけの関連クラスも、すべてごちゃごちゃにディレクトリの下側に来ちゃって、なんだかすっきりしないなー。やっぱりネームスペースを導入して、ディレクトリ構造=ネームスペースにしちゃって、関連クラスの命名規則はもっと自由に(Document_AbstractとかClient_Interfaceとかしなくて済むように)した方がいいなー。
2006-06-02 [長年日記]
_ Zend_Search_HyperEstraier設計中 その3
だいたい以下のような形で固まりつつある。
- Zend_Search_HyperEstraier - ユーティリティクラス
- Zend_Search_HyperEstraier_Document_Abstract - ドキュメント基底
- Zend_Search_HyperEstraier_Document - 通常のドキュメント
- Zend_Search_HyperEstraier_Document_ListItem - ドキュメントリストのアイテムとしてのドキュメント
- Zend_Search_HyperEstraier_Document_SearchResult - 検索結果のアイテムのとしてのドキュメント
- Zend_Search_HyperEstraier_SearchCondition - 検索条件
- Zend_Search_HyperEstraier_SearchResult - 検索結果
- Zend_Search_HyperEstraier_Node_Client - クライアントAPI
- Zend_Search_HyperEstraier_Node_Document - クライアントAPIと連携するドキュメント
- Zend_Search_HyperEstraier_Node_NodeInformation - ノード情報
- Zend_Search_HyperEstraier_Node_UserInformation - ユーザー情報
- Zend_Search_HyperEstraier_Node_Api_Abstract - コアAPI基底
- Zend_Search_HyperEstraier_Node_Api_Client - コアAPIクライアント用
- Zend_Search_HyperEstraier_Node_Api_Master - コアAPIマスター用
分散クライアントは、URIとノードの対応をどこで管理するのが妥当か思いつかない(っつーか、毎回URI検索して既登録だったらそのノードを使い、未登録だったら空いているところに登録、とかしかない気がするんだけど、それじゃあ分散させる意味が薄くてやる気がなくなった)んでいったんキャンセル。
あと結局ほぼノードAPIそのままのコアAPI層と、アプリケーションから使うクライアントAPIを別にした。ノードAPIを直で使う場合は、
$clientApi = new Zend_Search_HyperEstraier_Node_Api_Client($url, $username, $password);
$nodeInfo = $clientApi->getInformation();
$documentDraft = $clientApi->getDocument($uriOrId);
$document = new Zend_HyperEstraier_Document(documentDraft);
$document->text .= 'added text';
$document->modified = time();
if (!$clientApi->putDocument($document)) {
die('cannot save');
}
なんて感じ。クライアントAPIを使うと、
$client = new Zend_Search_HyperEstraier_Node_Client($url, $username, $password);
$document = $client->getDocument($documentUri);
if (!$document) {
$document = $client->createDocument($documentUri);
}
$document->title = 'title';
$document->created = time();
$document->modified = time();
$document->keywords[$keyword1] = $score1;
$document->text = 'text';
$document->hiddenText = 'hidden text';
$document->foo = 'foo'; // 非システム属性
$document->setAttribute('foo', 'foo'); // 上と同じ
if (!$document->save()) {
die('cannot save');
}
$document->updateAttributes(); //属性だけ更新する場合
$document->delete(); // 削除
なんて感じ。
肝心の検索は、
$searchResult = $client->search('phrase'); // フレーズ検索
// 複雑な検索条件
$condition = $client->getSearchCondition();
$condition->phrase = 'phrase';
$condition->attributes[] = 'attribute STRINC str';
$condition->skip = 5;
$condition->max = 10;
$searchResult = $condition->search();
foreach ($searchResult->hints as $word => $count) {
// 検索ワード出現回数
}
foreach ($searchResult->links as $nodeUrl => $nodeInfo) {
// ノード情報
}
foreach ($searchResult as $document) {
$uri = $document->uri;
$title = $document->title;
$created = $document->created; // @cdateをYYYY-mm-dd HH:ii:ss形式で
$snippet = $document->snippet;
}
なんて感じ。API周りのテストまで書き終わったんだけど、検索条件周りがまだいまいちすっきりしないんで、その辺をもうちょっと練ってみよう。
2006-06-05 [長年日記]
_ lighttpd+fastcgi+php(Zend Framework)+のrewrite設定
最初、
url.rewrite = ( ".*\.(js|ico|gif|jpg|png|css)$" => "$0", "" => "/index.php" )
としていたんだけど、$_GETが取れないことに気がついた。パラメータはほとんどルーターレベルで解決していたから、ずっと気づかないでいたよ。ひとまず、
url.rewrite = ( ".*\.(js|ico|gif|jpg|png|css)$" => "$0", ".*" => "/index.php$0" )
とかやってごまかしてみた(「/foo?bar=123」が「/index.php/foo?bar=123」なんて感じになるんで、一応QUERY_STRINGは妥当な内容になるはず)けど、ちゃんと「?」以降だけを渡すようにした方がいいんだろうな。
_ 今朝渋谷駅南口側歩道橋で
白いワンピースを着た女の子が、手を鳥のようにゆっくり羽ばたかせながら、歩道橋を歩いて渡っていく、という撮影をやっていたんだけど、あれはアイドルか何か? どう見ても既知外系にしか見えない絵面だったんだけど、そういう映画とかかなー。スタッフの人数が結構多かったから、エロ系ではなさそうなんだけど。
_ 145キロのデッドボール
昨日久しぶりにバッティングセンターに行ったところ、145km/hのマシンがあったんで、話のたねにやってみた。すると、4、5球目にピッチングマシンの投げた球が、タイミング良く隣の人が打った打球にかすり、ほんのわずかに軌道が変わった球が、もろに俺に向かってやってきた。そんなのよけれねーよっ! というわけで、見事に左脇腹に当たった。しょせん軟球だし、145km/hとか書いてあっても実際にはそんなに出ていないとは思うけれども、いまだに当たったところがかなり痛い。ビデオとかに撮っていたら、その手のテレビ番組とかに送ってもいいくらい、レアな出来事だ。っつーか、今年はどうやら悪い方に当たり年らしい。
2006-06-06 [長年日記]
_ Zendネームスペースの下に入れるのはやめた
Zend_Search_HyperEstraierとして作りかけていたライブラリは、ひとまず単体のライブラリとしての完成度を高める方に集中するために、Zendネームスペース以下で構築するのは(他にやらなきゃならないことが増えるんで)いったんやめて、単にHyperEstraierネームスペース以下に構築することにした。いまいち使う気にならないLucene対抗にしようかと思ってたんだけど。
で、検索条件周りをいろいろいじっているうちに、HyperEstraier_SearchCondition::toQueryStringでノードAPIのパラメータになり、HyperEstraier_SearchCondition::to***(未定)Stringでestcmdのコマンドラインパラメータとなるのが妥当だよなーと思いつつ、そうなるとescmdのラッパーもないと意味がないよなーってことになり、そうなるといったいどこまで作ると一段落ってことになるのか先が見えなくなってきたのでした。
そういや検索条件をもっと設定しやすくしよう計画に関しては、一番わかりにくいのは属性検索の条件式表現だと思うんで、属性検索条件をセットする場合は、
$condition->addAttribute('@title', '==', 'foo');
$condition->addAttribute('@mtime', '>=', '2006/6/6');
とかやると、左辺値(既知のシステム属性の場合はそれにあわせる)や右辺値(左辺値から推測できなかった場合、右辺値が数値や日付として解釈できる場合はそれを優先する)から属性の種類を推測して、
$condition->attributes[] = '@title STREQ foo'; $condition->attributes[] = '@mtime NUMLE 2006/6/6';
とかに展開するようにしてみたんだけど、なんかいまいちすっきりしなくて中断中。どうせなら、
$condition->attributes[] = '@title == foo'; $condition->attributes[] = '@mtime >= 2006/6/6';
にしちゃった方がいいかなー。最悪でもセットされたパラメータをそのままHyper Estraierに渡せるようにしておけば、悪影響は出ないかな?
_ spycのパースって重いんだな
8kバイト+1kバイトのYAMLで書かれた設定ファイルをspycを使って 読み込むWebアプリ。当初の速度はこんな感じ。
Requests per second: 3.01 [#/sec] (mean) Time per request: 1662.891 [ms] (mean) Time per request: 332.578 [ms] (mean, across all concurrent requests)
ページ(データ取得+レンダリング)キャッシュをかましてみたんだけど、大して速くならなかった。
Requests per second: 3.67 [#/sec] (mean) Time per request: 1361.458 [ms] (mean) Time per request: 272.292 [ms] (mean, across all concurrent requests)
で、YAMLファイルの読み込みにキャッシュ(serializeして保存)をかましたら、劇的に速くなった。
Requests per second: 10.59 [#/sec] (mean) Time per request: 472.179 [ms] (mean) Time per request: 94.436 [ms] (mean, across all concurrent requests)
spycのパースってこんなに重い処理だったのか。
_ 配列が絡んだ__setの挙動
class Foo
{
protected $_array = array();
public function __get($name)
{
echo "__get called by $name\n";
switch ($name) {
case 'array': return $this->_array;
default:
throw new Exception();
}
}
public function __set($name, $value)
{
echo "__set called by $name, $value\n";
switch ($name) {
case 'array':
$this->_array = $value;
break;
default:
throw new Exception();
}
}
}
$foo = new Foo();
var_dump($foo->array);
$foo->array = array(1);
var_dump($foo->array);
$foo->array[] = 2;
var_dump($foo->array);
が、
__get called by array
array(0) {
}
__set called by array, Array
__get called by array
array(1) {
[0]=>
int(1)
}
__get called by array
__get called by array
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
ってなる(PHP 5.1.4/Windows XP Professional)のは正しいのか?
$foo->array[] = 2;
のところで__setが呼ばれずに、直接Foo::$_arrayに追加されているんだけど。
っつーか、
$foo->array[] = 2;
は
$tmp = $foo->array; array_push($tmp, 2); $foo->array = $tmp;
相当の処理だと信じていたんだけど。
_ serializeとvar_exportどちらが速いのか
Spycでパースしたデータを、serializeしてキャッシュするよりも、var_export($data, true)でPHPコードにしてキャッシュした方が速いんじゃね。と思ってベンチを取ってみた。
using Spyc::YAMLLoad start: 1149596540.0295 end: 1149596555.4353 elapsed: 15.405829191208 using serialize start: 1149596555.4358 end: 1149596555.64 elapsed: 0.2041699886322 using php var_export start: 1149596555.642 end: 1149596556.2202 elapsed: 0.57813310623169
あれー、PHPコードにするよりもserializeの方が速いのか。まあ確かにserializeの方が圧倒的にロジックが単純で済むしな。ただPHPコードとしてファイルに落としておけば、PHP Accelerator系のキャッシュが使えるだろうから、そういうのと組み合わせると速くなるかもしれない。けどまあ、ふつうはserializeを使おう。
一応コード
require_once 'spyc.php';
$yamlFile= 'spyc.yml';
echo "using Spyc::YAMLLoad\n";
$starttime = microtime(true);
for ($i = 0; $i < 1000; $i ++) {
$config = Spyc::YAMLLoad($yamlFile);
}
$endtime = microtime(true);
$elapsedtime = $endtime - $starttime;
echo "start: $starttime\n";
echo "end: $endtime\n";
echo "elapsed: $elapsedtime\n";
echo "\n";
echo "using serialize\n";
$serializedFile = 'spyc.yml.txt';
file_put_contents($serializedFile, serialize($config));
$starttime = microtime(true);
for ($i = 0; $i < 1000; $i ++) {
$config = unserialize(file_get_contents($serializedFile));
}
$endtime = microtime(true);
$elapsedtime = $endtime - $starttime;
echo "start: $starttime\n";
echo "end: $endtime\n";
echo "elapsed: $elapsedtime\n";
echo "\n";
echo "using php var_export\n";
$phpFile = 'spyc.yml.php';
file_put_contents($phpFile, '<?php return ' . var_export($config, true) . '?>');
$starttime = microtime(true);
for ($i = 0; $i < 1000; $i ++) {
$config = include $phpFile;
}
$endtime = microtime(true);
$elapsedtime = $endtime - $starttime;
echo "start: $starttime\n";
echo "end: $endtime\n";
echo "elapsed: $elapsedtime\n";
2006-06-07 [長年日記]
_ PDO-MySQLでのUNIXソケット接続指定
PDO-MySQLでホスト名(+ポート)じゃなくて、UNIXソケット経由で接続するときの設定をいつも忘れるんで、ここに書いておこう。
mysql:unix_socket=/path/to/socket;dbname=DBNAME;username=USER;password=PASS
マニュアルだとPDO_MYSQL DSNに書かれている。
2006-06-13 [長年日記]
_ 街区レベル位置情報+郵便番号データ
次期MM/Memoというか1470.netで、位置情報関連機能を強化しようと思って、まずは位置情報→住所、郵便番号→住所→位置情報とかが手軽に変換できるように、街区レベル位置参照情報と郵便番号を合成してみている。
主データとしては、街区レベル位置参照情報の方を利用する。というのは、郵便番号の方は位置情報ではなく、郵便の都合で区分けされているんで、より位置情報のキーとして使いやすそうなのは街区レベル位置参照情報の方だから。
街区レベル位置参照情報は「丁目」よりも細かい「街区符号・地番」レベルの情報も持っているけれども、そこまで扱うと
- 住所情報としてのセンシティブさが増しすぎる
- 郵便番号情報との粒度の違いも大きくなりすぎる
- AjaxとかでがんがんDBを叩くにはデータサイズが増えすぎるだろう
という理由で、データとしては「丁目」レベルまでを扱い、それ以下は切り捨てることにする。
で、まずは「都道府県」「市区町村」「町域(大字・町丁目)」「緯度」「経度」というデータを街区レベル位置参照情報からインポートして、位置情報テーブル(address)を作成する。
| id | prefecture | city | cityarea | latitude | longitude |
※実際にはprefecture、city、cityareaも別テーブルに切り出している
最初、上記の位置情報テーブルに、郵便番号カラムを追加してやればいいかと思っていたんだけど、郵便番号は町域レベル以内でダブって登録されていたりすることも多い(同じビルの階数ごとに別の郵便番号が振られていたり)んで、別に郵便番号テーブル(zip)を作ることになった。
| id | zipcode | address_id |
郵便番号データにも「都道府県」「市区町村」「町域」という情報があるので、それらが完全一致する場合は、その位置情報をセットすればいい。しかし、それで対応できるデータというのはかなり少ない。具体的には、
- 一つの郵便番号で市区町村レベルまでの範囲を受け持っている
- 一つの郵便番号で複数の町域(細かく指定)を受け持っている
- 街区レベル位置参照情報には存在しない市区町村に関する郵便番号がある
- 街区レベル位置参照情報には存在しない町域に関する郵便番号がある
- 町域レベルとして地名ではなく建物名などがセットされた郵便番号がある
などの例外の割合が非常に多い。
1の場合、位置参照情報の方では、基本的に町域レベルまで指定された位置情報を持っているので、市区町村レベルまでしか指定されない場合、その市区町村に所属する複数の位置情報のどれを代表として使用するのが妥当なのかという判断が、機械的にはできない。けれども、ひとまずはデータ上最初に出てきたものをセットしてみた。つまり、このようにしてセットした行については、位置情報を調整する(市区町村レベルの中心に近い町域を指定する)必要があるかもしれない。
2の場合、郵便番号データの町域情報では、そのような複数の町域指定を「町域共通部(町域1、町域2)」のような形式で指定しているようなので、ひとまず「(」の前までの町域共通部を利用して、そこまでマッチする位置情報が存在した場合は、その位置情報として登録するようにした。これは1と同様の問題があり得る。
3に関しては想定外だったが、街区レベル位置情報では網羅されていない市区町村というものが思いのほかたくさんあった。これらは、
- 単に街区レベル位置参照情報データには、掲載されなかった市区町村
- 新しくできた・合併した・改名された市区町村
などだろうか。特に地方(北海道や沖縄など)で目立つようだ。これらのデータに関しては、本来addressテーブルレベルで何らかの対応をするべきだろう(既登録データの修正もしくは新規登録など)が、その際には郵便番号データにしか存在しない市区町村データに関して、一件ごとの具体的な対応の検討等も必要になるため、現在は未対応(zip.zipcodeは登録したがzip.addressはnull)である。
4に関しては、ひとまず街区レベルの情報は無視して、1と同様の対応を行った。これは1と同様の問題があると同時に、場合によっては3と同様にaddressテーブルレベルでの対応も行うべきだろう。新規にaddressテーブルに追加する場合は、独自に位置情報データ用意する必要がある。
5に関しては、2と同じような対応を行っているが、2よりもずれが大きい可能性が高い。というのは、基本的に文字列一致で処理を行っている都合上、街区レベル位置参照情報データと郵便番号データの二つで、町域レベルでの指定よりも建物レベルの指定の方が、その表記のずれが大きい可能性が高いからだ。これらも最終的にはデータ一つ一つに対しての検討が必要だ。
と、いろいろ問題はあるけれども、機械的に合成したデータでもそれなりに使えることは使えるようだ。このデータベースに対して、Wikiみたいに誰でも自由に位置情報の詳細を登録・修正できるインターフェースを用意してアップデートをかけていくと良さそうな気がするんだけど、それを実現するためにはいろいろ考えなければならないことがありそうなんで、ひとまずは機械的に作成したバージョンを公開しておく。
あと、それを使って作ってみたGoogle Mapsとの連動サンプルも。こちらはそのうち消える(というか1470.netのリニューアルバージョンの一部として再構成される)だろうけど。一応Firefoxでは一通り動くはず。Opera 8でも大丈夫っぽい。IEだと微妙に動作が怪しい(selectの自動生成周りの互換性が怪しい)けど、まあその辺は後々の課題ってことで。Google Mapsを使っているんでJavaScript必須ね。
4のパターンは
漢字表記が違うというのもあるらしい。「杉並区堀ノ内」が「杉並区堀之内」になっていたり。どっちが正しいんだろう? っつーか地名表記漢字に「公式」ってのはあるのかな?
作ったとたんに
たたみラボ: GoogleがGeocodingAPIリリース!なんて話が……。
2006-06-14 [長年日記]
_ アルファな人のRSSは直接購読しない
アルファな人たちの情報を追いかけていると、ネタの幅と深さを同時に追求してしまうことになりがちだし、しかも情報の鮮度が高い分、興味を持ったときに自分で調べるコストも高くなりがち。そこでアルファな人たちのRSSを直接購読するのはやめ、アルファな人たちのRSSを直接購読している人たちのRSS(二次情報RSS)だけを購読するようにする。すると、ほどよいフィルタリングで取捨選択されたネタに関して、初めから必要な情報がうまくまとめられた段階で接することになり、情報収集が効率化する。
という夢を見た。
目が覚めたら、現実はどうだったかを知りたいところ。
Bloglinesの左フレームに「(200)」が並んでますが、なにか?
_ アルファな人のRSSは直接購読しない 実践編
アルファな人のRSSは直接購読しないは単に思いつきネタで、全然実践する気もない話なんだけど、なんかマジレス気味なコメント(ブクマ)が多い気がするんで、その辺対策を真面目に考えてみる。
基本的には、RSSはすべて購読する。ただしまめにチェックしている暇はないんで、未読管理数制限があると困る(Bloglinesの200だと間に合わない)。で、一般的なRSSリーダーの機能は一通りありつつ、次の機能を追加する。
それは、未読記事の中から注目された記事だけを抽出して、まとめ読みができるモード。注目記事ってのは、はてなブックマークみたいなSBSとか、あるいはtechnoratiのランキングとか、あたりから適当に引っ張ってきてURLマッチさせれば十分。そのモードで読んだ記事もちゃんと既読になって未読数は減る。
すると、未読を全部読まなくても、未読のうちの重要な記事だけを先行してチェックできることになるし、その分の未読数もちゃんと消化できる。まあなんて便利。
はてなRSSなんかだったら、すでにエントリー抽出SQLではてブ被ブクマ数(bookmarkcount)も持っているみたいだから、未読記事を被ブクマ数逆順でソートして表示(where bookmarkcount > [threshold] order by bookmarkcount desc)するだけだし、実装は簡単だよね。ちょちょいっとつけてみる気はないかい。その機能が載ったら、個人カスタマイズできるはてブのフィルターとして使えて便利そうだ。
あらかじめ言っておこう
それPla禁止。
_ 駅の位置情報
国土数値情報ダウンロードサービスで配布されている鉄道の数値情報を元に、路線+駅+位置(緯度・経度)情報を抽出しようと思ったんだけど、位置情報は扱いが難しいなー。データと仕様を見ても、しばらく何がなんだかわからなかった。けれども、しばらく見ているうちにようやく意味が理解できたんで、コンバートしてみた。が、いくつか問題があった。
駅ノードの属性番号と同一のメッシュコード+番号を含むラインデータ(=路線)がある場合は、その駅はその路線に所属している、というロジックでデータを抽出してみたんだけど、
- どの路線にも所属しない駅が発生する
- 複数路線に所属するはずの駅でも、一つの路線(基本的にはJR)にしか所属しない
ということになった。前者はデータ欠けを埋めていくだけだから、多少量が多くてもマンパワーをかければ埋まっていくと思うけれども、後者は難しいなー。複数乗り入れ駅(乗り入れ路線数付き)の一覧データとかがないと、マンパワーをかけたところでデータの埋まり具合がさっぱりわからない。でもまあマンパワーをかけつつ地道に埋めていくしかないのかな。どこかに複数乗り入れ駅一覧データをもったてっちゃんはいねが。
その他表記の揺らぎがあるとか、データが古いんで駅名とか路線名とかが最新の情報に追随していないとか、微妙な問題は他にもあるけれども、そっちはまあひとまず置いておいてもいいかな。
これももうちょいまとまったら公開しつつ、オープンな形で完成度を高めていく方策を考えよう。
2006-06-15 [長年日記]
_ リニューアル後の検索周りのサンプル
ひとまずAmazon検索で実装してみたけど、
/search/[type]/[query]?[その他パラメータ]
というURI構成で表現しきれるだろうか?
あとAllowEncodedSlashes周りでちょっとはまった。っつーかAllowEncodedSlashes Onにしても%2Fを含むURLのrewriteが通らないんだけど、情報が古いんだろうか? まあコードレベルで対応できそうだからさして重要じゃないんだけど。
_ 駅の位置情報 初版公開
- 鉄道路線+駅位置情報データベース
- サンプルアプリ(街区レベル位置情報に鉄道関係の機能も追加した)
ひとまず機械的に作ったものにちょっとだけ手を加えた(コンバート中に気づいた元データの間違いらしい部分を修正+所属路線不明の駅を手作業でいくつか登録)ものを公開。これでもそれなりに使い物にはなるけれども、まだ完全にはほど遠い。
まず、データコンバート中に気がついたんだけど、元データにわずかながらバグが混ざっている模様(一つは配布元に報告済み)。データの不整合を起こすようなデータならば、わかりやすいんだけど、もしもコンバート上は問題ないけれども、意味的には間違っているデータが混在していた場合、それらを修正する必要がある。あと、私がコンバート作業中に持ち込んでしまったバグも混ざっている可能性がある。
続いて、missing.txtに列挙してある、機械的なコンバートでは所属路線が一つも登録されなかった駅(700件弱)について、その所属路線を確定する(railway_stationに追加)する必要がある。ちなみに手作業でいくつかやってみたけれども、
- 同名の駅が複数ある場合に、正しい情報を特定するのがちょっと難しい
- 駅や路線の統廃合、改名などによって現状には即していない内容があり得る
という話があって、扱いは結構微妙。特に後者の場合、路線情報のレベルからデータの整合性を取り直す必要が出てくる可能性がある。が、ひとまず路線情報は現状のものを生きとして、それに駅情報を一通り合わせるところからはじめようかと思っている。
また、現状のデータでは一つの駅は一つの路線にしか所属していない。が、実際には複数路線が乗り入れている駅が多々ある。そのようなデータについて、路線−駅リンクテーブル(railway_station)に追加していく必要がある。
また、現在路線−駅リンクテーブルには元データに登録されていた順序で駅を登録していっているんで、それなりに路線上の駅順に近い並びで駅が登録されているけれども、上記のような作業を行っていくうちに並び順がどんどんおかしくなっていくはず。
路線−駅リンクテーブルには、路線単位での駅順序ソートキー(station_order)を用意しているんで、一通りデータがそろった段階で、そのソートキーを使って駅順序を正しく指定できるようにしてやる必要がある。
と、完成度を高めるにはまだ作業が必要な感じだけれども、ひとまずサンプルアプリで適当な場所に移動してから、[現在地の最寄り駅に移動]で最寄り駅に移動していると、何となく楽しいんで、試してみてくださいな。
_ 違いが分からない
はてな認証APIで返されるJSON形式のデータをZend_Json(のSubversion上の最新版)でデコードしようとするとエラーが出る。調べた結果、Zend_Json_Decorder::_eatWhitespaceの
'/(\t|\b|\f|\n|\r| )*/s'
という正規表現を、
'/[\t\b\f\n\r ]*/s'
に変えると正しく動作する(前者だとホワイトスペース除去がうまく動作しないせいで、Illegal Tokenになる)んだけど、その理由がわからない。
2006-06-16 [長年日記]
_ 有効範囲指定付きチケットを使ったAPI
リニューアル後は、Bookeyみたいなツールからの利用がやりやすいように、ちゃんとしたAPIを作る予定。
うちの場合は、外部認証サービスを利用しているんで、自前でパスワード的なものは持っていない。けれども、API用の認証を行うならば、やっぱりそれ的なものが必要になるだろう。だから、API専用の認証キーをランダム発行(ランダム再発行可能)し、それをAPI専用に使う(非ランダムなパスワード文字列は持たない)。
うちはSSLは使わないつもりなんで、毎回認証キーを流すのは気持ち悪い。だから認証キーを使って期限が短いチケットを発行させ、そのチケットを使ってAPIへのリクエストを行う形式を採用する。
チケット発行APIに、ユーザーID、認証キー、オプションパラメータを投げると、チケットIDが返される。オプションパラメータでそのチケットの有効範囲を指定する。
有効範囲ってのは、
- 利用回数(無制限可)
- 有効期限(必須。デフォルト値あり)
- 利用可能なAPI(指定した場合)
- 環境チェック(有効な場合、チケット発行時と同一のIPアドレス、ユーザーエージェントからのリクエストのみ許可)
あたりをイメージしている。
使う側で利便性とセキュリティのバランスを考えて、チケットを発行して使ってね、って感じ。
こういうチケットの仕組みって、どこかで使っているところあるかな? Flickrはread/write権限とか持っているみたいだけど、有効期限とか呼び出し回数のコントロールも可能なんだろうか(調べてない)。あるいはチケットにあまり機能を持たせると、負荷的に不利だからあまり採用されていない、とかあるのかも。まあ試してみよう。
ちなみにこの仕組みは非Webアプリからの利用しか考えていない。Webアプリ(第3者管理サーバー)でコールバックを使った認証委譲を利用したい場合は、その部分を別途考える必要がある(けど、多分そこまで作らない)。
っつーかその場合、外部Webアプリ → うちのサーバー → 外部認証サーバー(TypeKeyとか) → うちのサーバー → 外部Webアプリ って流れになるのか。なかなかわけわからない仕組みになりそうだな。
2006-06-18 [長年日記]
_ 検索条件の指定方法
andで複数条件をつないで指定する際に、わざわざ
$db = $table->getDbAdapter();
$table->fetchAll(array(
$db->quoteInto('foo = ?', $foo),
$db->quoteInto('bar > ?', $bar),
));
みたいな書き方をしていたけど、
$table->fetchAll(array( 'foo = ?' => $foo, 'bar > ?' => $bar, ));
で良かったのか。
_ Zend_Db_Table::findでの戻り値
primary key検索のfindは、引数にスカラーも配列も受け付けるけど、配列の要素が1つしかないときにRowSetじゃなくRowを返す仕様がとてもうざい。
$keys = somefunction(); // 何らかの条件にマッチするkeyの配列を返す
$rowset = $table->find($keys);
foreach ($rowset as $row) {
// 何らかの処理
}
みたいな書き方をしたときに、somefunctionがたまたま一つしか結果を返さなかったときに、$rowsetがRowになっちゃってちゃんと動かない。
配列の要素数を見て戻り値の型を切り替えるのではなく、引数の型がscalarかarrayかで戻り値の型を決める方が妥当じゃないか。
2006-06-21 [長年日記]
_ Zend Frameworkの記事を書きました
WEB+DB PRESS Vol.33にZend Frameworkの記事を書きました。サンプルアプリケーションのRSS/Atomフィードリーダーは技評さんのサーバーからダウンロードできますし、うちのサーバーで見ることもできますんで、Zend Frameworkアプリのサンプルを見てみたい方はどうぞ。逐一コメントを入れておいたんで、原稿本文を読むよりもサンプルのソースを読んだ方が話が早いかもしれません。素のサンプルではなくちょっと応用が入っているんで、これでZend Frameworkの基本を学ぼう、という人には向いてないかもしれませんが。
それにしても、もう一ヶ月以上前に書いたんで「本誌発売時点では公開されているであろう0.1.4」としておいたのに、まだ0.1.4が公開されていないとはなー。そういやようやくむかーし報告したログレベルのバグがtrunkで修正されてたよ。あとプロジェクト管理システムをtracからJIRAに変えたみたいね。確かにtracはお手軽なのはいいけど、真面目に管理しようとするといろいろ機能が足りてないからなー。JIRAはそういう意味では使えるけれども、あまりにお手軽さに欠けるのがガンだよなー。俺は前に使ったことがあるから使い方が分かったけど、使ったことがない人にはずいぶん(バグ報告等の)敷居が高くなったんじゃなかろうか。
_ RSSからのオートインポート機能
1470.netリニューアル版で、新しくつけるとうれしいんじゃないか機能案。
(blogの)RSS/AtomフィードのURIを登録しておくと、そのエントリー情報に含まれる、MM/Memoで対応可能なアイテム(URL、ASIN、位置情報など)について、自動的にメモを登録する機能があると便利だろうか。
そうすると、あるURLや本に関する情報を、blogに書いてもMM/Memoに登録しても、どちらにしても最終的にはMM/Memo側で統一的に扱う(検索する)ことができるようになる。
いったんURLをメモして、後からblogに書いたりしたときでも、自動的にURLをキーに検索されるようになるし。
_ microformats対応
別にmicroformatsでなくてもいいんだけど。
リニューアル版MM/Memoでは、URLやASINをキーにそのメモを取るという仕組みではなく、一つのメモに複数のURLやASINや位置情報や日時情報を添付する、といった形にする予定。
その際に、単にメモとURLやASINをリンクするだけでなく、どういう意味でリンクされているのか、も表現できるとずいぶんデータの表現力が高まるはず。
従来「関連URL」としてオプションでURL情報を追加できたけど、あれを「via」として使う人や、文字通り「related」として使う人など、いろいろいたはず。それを明示的に「via 〜」「related 〜」などとして登録できるようにしよう、という話。
で、その際にはどういうデータ形式および表現形式を使うのが妥当だろうか、と考えたときに思いついたのがmicroformatsなわけだけど、真面目に既存のmicroformats関連情報を調べる暇もなさそうだし、なんちゃってmicroformats(というかユーザーが自由にrel="〜"とかclass="〜"とか相当の情報を登録できるようにするだけで、システム側ではその内容まではサポートしない)で十分かなー。
_ タグの自動取り込み
そういやmicroformats関連ってことで、URLが指定されたときに、そのURLで示されるコンテンツが何らかのタグ情報を含んでいた場合、自動的にそのタグ情報を取り込む機能とかあると便利かなー、などと思い、でもいろんなサービスにアドホックに対応するのはいやだから、もしかしていまどきのその手のサービスは、みんなタグ情報へのリンクにはrel="tag"とか持たせていたりして、汎用的にタグ情報を抽出できたりするんじゃね、などとちょっと期待しつついくつか見てみたけど、んなとこまだ全然ねーのね。microformatsって言葉が出てきてからずいぶん経つ気がするけど、まだ全然(実装レベルでは)流行ってない? それともすでにobsolete気味?(実は「やっぱりRDFだよ」方面に向かってたりして)
2006-06-22 [長年日記]
_ Amazon WebサービスにHTTP/1.1でアクセスするとゴミが出る
Zend_Http_Clientをincubatorバージョンに変えてテストしていたら、Zend_Service_Amazonの動作がおかしい*1。なんでかなーと原因をたどっていったら、Amazon WebサービスへのリクエストがHTTP/1.0からHTTP/1.1に変わったことが原因だった。
以下再現コード。HTTP/1.1でのリクエスト。
$socket = fsockopen('webservices.amazon.co.jp', 80);
fwrite($socket, "GET /onca/xml?ResponseGroup=Request&SubscriptionId=[Subscription ID]&Service=AWSECommerceService&Operation=ItemLookup&ItemId=4774128104 HTTP/1.1\r\n");
fwrite($socket, "Host: webservices.amazon.co.jp\r\n");
fwrite($socket, "Connection: close\r\n");
fwrite($socket, "\r\n");
while (!feof($socket)) {
echo fread($socket, 8192);
}
fclose($socket);
結果。
HTTP/1.1 200 OK Date: Thu, 22 Jun 2006 04:39:16 GMT Server: Server x-amz-id-1: 0TR0TFSQ68NG6TEF33NC x-amz-id-2: Qc8vxAunuvNVpdAORHM5YVP2pvxgSkOv Connection: close Transfer-Encoding: chunked Content-Type: text/xml; charset=UTF-8 36f <?xml version="1.0" encoding="UTF-8"?><ItemLookupResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2005-10-05"><OperationRequest><HTTPHeaders><Header Name="UserAgent"></Header></HTTPHeaders><RequestId>0TR0TFSQ68NG6TEF33NC</RequestId><Arguments><Argument Name="SubscriptionId" Value="[Subscription ID]"></Argument><Argument Name="ResponseGroup" Value="Request"></Argument><ArgumentName="Operation" Value="ItemLookup"></Argument><Argument Name="Service" Value="AWSECommerceService"></Argument><Argument Name="ItemId" Value="4774128104"></Argument></Arguments><RequestProcessingTime>0.0139038562774658</RequestProcessingTime></OperationRequest><Items><Request><IsValid>True</IsValid><ItemLookupRequest><ItemId>4774128104</ItemId><ResponseGroup>Request</ResponseGroup></ItemLookupRequest></Request><Item><ASIN>4774128104</ASIN></Item></Items></ItemLookupResponse> 0
「36f」「0」などのゴミが混ざっている。上記を見るとボディ部の前後にゴミが混ざるように見えるけれども、もっと長い結果を返すリクエストの場合は、定期的に類似したゴミが混ざる。バッファ(最大0x2000バイト)から出力した文字数を16進数で出力しているっぽい。
続いてHTTP/1.0に変える。
$socket = fsockopen('webservices.amazon.co.jp', 80);
fwrite($socket, "GET /onca/xml?ResponseGroup=Request&SubscriptionId=[Subscription ID]&Service=AWSECommerceService&Operation=ItemLookup&ItemId=4774128104 HTTP/1.0\r\n");
fwrite($socket, "Host: webservices.amazon.co.jp\r\n");
fwrite($socket, "Connection: close\r\n");
fwrite($socket, "\r\n");
while (!feof($socket)) {
echo fread($socket, 8192);
}
fclose($socket);
以下、結果。
HTTP/1.1 200 OK Date: Thu, 22 Jun 2006 04:39:16 GMT Server: Server x-amz-id-1: 0TXZ0YSV0W5WA2TQGFCD x-amz-id-2: oew++LbGRDya+Ju9I49Vx7bXa6CAsn4V Connection: close Content-Type: text/xml; charset=UTF-8 <?xml version="1.0" encoding="UTF-8"?><ItemLookupResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2005-10-05"><OperationRequest><HTTPHeaders><Header Name="UserAgent"></Header></HTTPHeaders><RequestId>0TXZ0YSV0W5WA2TQGFCD</RequestId><Arguments><Argument Name="SubscriptionId" Value="[Subscription ID]"></Argument><Argument Name="ResponseGroup" Value="Request"></Argument><ArgumentName="Operation" Value="ItemLookup"></Argument><Argument Name="Service" Value="AWSECommerceService"></Argument><Argument Name="ItemId" Value="4774128104"></Argument></Arguments><RequestProcessingTime>0.00991702079772949</RequestProcessingTime></OperationRequest><Items><Request><IsValid>True</IsValid><ItemLookupRequest><ItemId>4774128104</ItemId><ResponseGroup>Request</ResponseGroup></ItemLookupRequest></Request><Item><ASIN>4774128104</ASIN></Item></Items></ItemLookupResponse>
こっちは正常な結果が返ってくる。
Amazon WebサービスへのリクエストはHTTP/1.0で行わなければならない、とか決まっていたんだっけ? どこに報告するべき問題なのかいまいちよくわからない。ひとまずZend_Http_Clientのincubator版はHTTP/1.1がデフォルトになっちゃってるから、このままだと正式版に格上げされたときにトラブりそうだな。
ああ
Transfer-Encoding: chunkedだからか! これで正常なのね。っつーか、HTTP/1.1で送るならば受信側がちゃんと対応しないといけないのね。HTTP/1.1のRFCちゃんと読んでおこう。
一応資料
ハイパーテキスト転送プロトコル -- HTTP/1.1 3.6.1 チャンク形式転送エンコーディング
すべての HTTP/1.1 アプリケーションは、"chunked" 転送コーディングを受信しデコードできなければならないし、理解できない転送コーディング拡張は無視しなければならない。
※参照するRFCを2068から2616に変更しました。
*1 コンストラクタのインターフェースが変わっていたのは修正した
2006-06-23 [長年日記]
_ 1470.netリニューアル開発テストバージョン
まだプロトタイピングの段階なんで、ベータとか以前のバージョンだけど、だいぶまとまってきたんで、いったん晒してみる。まだ実用レベルではないし、データも保全しない可能性が高い(DB設計からやり直す可能性がある)のでそのつもりでどうぞ。
Zend Frameworkとの格闘も落ち着いてきて、前向きな作業の割合が増えてきたけど、そうなると何となく後回しにしてきたUI周りの設計を本格的にやらなきゃいけなくなって、懸案事項が盛りだくさんだ。
あと、JavaScriptの基礎知識が不足しているのに、Google Maps+Yahoo UI Library+prototype.jsとかを無理矢理使っているんで、ひとまず動くように書いたきちゃないコードを、いまどきのコーディングスタイルに直す必要があるだろうなー。
あと、今回はものすごく富豪的な作り方をしているんで、それでどのくらい負荷的に耐えられるのかも検証する必要がある。DB設計的には、従来バージョンよりもずいぶんましにはなっているんだけど、データ取得系はたいていデータオブジェクトのプロパティに見せかけつつ、裏でがんがんクエリーを投げる仕組みにしちゃってるからなー。
修正
- コメントと評価の処理がおかしかった(保存および編集時の復元処理にバグがあった)のを修正
2006-06-26 [長年日記]
_ 「グループ」機能の導入
1470.netリニューアル開発テスト版に「グループ」機能を追加した。「グループ」はタグとまったく同じ機能を持ち、技術的には単に一つのメモに「タグ」と「グループ」という二つのタグ機能がついただけのものだが、いろいろと応用範囲が広いんじゃないかと思っている。
まず、従来MM/Memoで「メモ種別」として用意していた機能の代替として使える。この機能を使っている人はあまり多くなかったけれども、これはタグよりもキーワードの揺らぎを少なくすることによって、確実にメモをグルーピングするために用意した機能だ。
MM/Memoの「メモ種別」では文字数として2バイトしか使えないので、自由度がない代わりに、簡単なルールを決めたらほぼ確実にそのルール内でメモを分別できるようになる。また、その命名規則はタグの名前空間を汚さない。
たとえば、「ToDo」というタグを付けた場合、それが自分にとっての「ToDo項目」という意味なのか、それとも一般的な「ToDo」に関連する情報であることを意味しているのかが、タグだけでは区別できない。そこで、前者の「自分にとってのToDo項目である」という意味を表すタグを「グループ」に逃がすことによって、同じ「ToDo」というタグを使い分けることができるようになる。
また、ある本について購入前は「購入予定」グループに、購入後は「購入記録」グループに、読み終わった後は評価付きで「読了記録」グループに変更することによって、状態管理をグループを使って表現することも可能だろう。「後で読む」などもどちらかというとタグよりはグループに入れた方が管理しやすいように思う。
さらに、この「グループ」を活用して、いわゆるまとめサイトや人力検索系サイト的なものも表現できるのではないかと考えている。
たとえば「PHPのフレームワークに関する情報サイトを教えてください」というグループを作り、興味を持った人がそのグループに関連サイトをブックマークしていく。するとそのグループの新着情報や人気(登録数が多いURI)情報が、いわゆるまとめサイト的な情報となっていく。
もちろん同様のことはタグを使っても可能だが、タグの名前空間をそういう用途で汚してしまうのは、ユーザーにとってはあまりうれしくないだろう。タグの名前空間自体は通常の使い方で利用しつつ、そういう特殊な用途には同じ機能を持つ別の検索軸である「グループ」を使うことにより、タグの可能性が広がるのではないだろうか。
まあそういう使い方がうまく回るようにするためには、ユーザーインターフェースあるいはサイトナビゲーション的な工夫が必要になるだろうが、一応そういうことを考えて「グループ」という軸を導入してみたわけだ。
2006-06-27 [長年日記]
_ これからのフィードはどの形式を採用するべきか
1470.netリニューアル開発テスト版にはまだRSS出力機能は一つもつけていない。
というのは、新バージョンではURI以外の情報もいろいろ含んでおり、また一つのメモに複数のURIが所属することもできるため、単純なURI=1アイテム型のRSSで置き換えることができないから、ってことが表面上の大きな理由ではあるんだけど、それ以前の問題として、今から新しく作るアプリケーションではどの形式のフィードを採用するのがいいんだろう、という根本的なところでの疑問もあったりする。
昔はいろいろ考えるのが面倒だし、日本での採用事例も多い(=提供するフィードを利用するアプリケーション側で扱いやすい)しってんで、基本的にRSS 1.0を使ってきたんだけど、最近ではフィードをハンドリングするライブラリも充実してきたし、後者の理由はもはや大きな理由にはならないだろう。で、前者の理由に関しても、今回はどうせ位置情報とか日時情報とかの扱いが絡むんで、いろいろ調べたり考えたりしなければならないことが多いから、ついでにちゃんと考えてもいいんじゃないかと思ったりしている。
ポイントとしては、URL、位置情報、日時情報あたりをうまくまとめて扱いやすく、将来的な活用可能性が高く(フィードとしてだけでなく、フィード内に含まれる各データ自体もそれなりに再利用性を持つ)、さらに現時点でもそれなりに利用できる(現行のフィードリーダー等でちゃんと扱うことができる)ようにするには、どういうフィードデータを吐けばいいのか、というところ。これもそれPla(ggerで後付けで加工すれば何とでもなるじゃん)禁止。
自分でもぼちぼち調べていきますが、よさげな情報をお持ちの方、ご教示ください。
1470.netリニューアル開発テスト版のアカウントをお持ちの方は
関連しそうな情報を「これからのフィードはどの形式を採用するべきか」グループでメモしておいてもらえるとありがたいです。
2006-06-28 [長年日記]
_ MM/Memoからのインポート
MM/Memoからのデータインポートスクリプトを作って、試しに自分のメモをインポートしてみた。MM/Memoではできなかった*1解析とか検索とかがまっとうに動くので結構楽しい。
MM/Memoユーザーの方で、自分のデータもリニューアル開発テスト版の方にインポートして欲しい方は、リニューアル開発テスト版の方にアカウントを登録した上で、MM/MemoのユーザーID(数値)とリニューアル開発テスト版のアカウント名(英数字)を教えてください(ここのコメントにお願いします)。
ちなみに対応するのはURLとASINに関するメモのみで、位置情報(互換性がなくなった)やテレビ番組情報(リニューアル版では取り扱っていない)はインポートできません。あと、URLに関しては古いデータだとすでに情報が取れないURLも結構たくさんあるみたいです。
データがある程度そろった状態じゃないと分からないこともいろいろあるので、ご協力お願いします。
ちなみに
今回のはあくまでも主にテスト目的のインポートであって、正式移行時のインポートの機会はまた別に用意しますよ。
特に問題がなければ、今回移行したデータはそのまま正式稼働時にも残すつもりではいますが、大規模な仕様変更(特にデータ構造的に)があった場合は、データをリセットする可能性もありますので。
*1 SPAMまみれで巨大化したblogmapデータベースと一体化していて重いクエリーが流せなかった
2006-06-29 [長年日記]
_ PHPでmod_proxy_balancerのstickysessionは使えない?
Apache 2.2のmod_proxy_balancerを使ってみようと思ったんだけど、どうもうまく動かない。PHP標準のファイルハンドラーセッションを使っていたんで、セッション初期化時にセッションIDに.[route]を付与するようにしたんだけど、ロードバランス以前にセッション自体が生成されてくれない。
と思ったら、
だったのね。ファイルセッションハンドラを使っていたからstickysessionを使いたかったんだけどなー。PHPではmod_proxy_balancerのstickysessionはあんまり使えないってことなのか? DBセッションハンドラとか使うんだったら、アプリケーションレイヤーで対応できるから、ファイルセッションハンドラを使っているときこそ、この機能を使いたかったのに。stickysessionでセッションIDに「.」を使わない設定ってできないのかなー。
追記というか訂正(2006/9/5)
この記事にリファラーがあったんで、訂正を追記しておこう。
何もPHPの標準セッションIDを直接stickyにするためのキーとして使わなくても、別にstickyにするためのキーとなるIDを振るようにすればいいだけじゃん。PHP標準のセッションID以外のCookieだったら、別に「.」の使用制限もないわけだし。
ということで、この元記事はなかったものとしてスルーすることを推奨します。
2006-06-30 [長年日記]
_ すみません、データが飛びました
リニューアルテスト版周りのサーバー構成を大幅に変更しようと作業していたら、リニューアルサイト用DBのデータが吹っ飛んでしまいました。状況・原因ともによく分からないのですが、いくつかのテーブルが消えてしまい、データを復元するのが難しい状態になったため、初期データから入れ直しています。原因不明のため、またすぐにおかしくなる可能性もありますので、ご注意ください。
どうやら原因がわかった
今までレプリケーションスレーブ側にテストDBを作ってテストをしていたんだけど、そろそろ本番環境に近いものも用意しておこうかと、マスター側でDBをいじっているうちに、テストDBと同名のdropがいくつかレプリケーション側に流れちゃって……、ということらしい(レプリケーションがdropの不整合で止まっていた)。作業ミスでした、すみません。
_ 本番環境への移行を始めました
リニューアル版の本番環境への移行を始めました。MM/Memo以外の機能についてはいったん停止し、リニューアル版で該当する機能がある場合は、そのURLにリダイレクトをかけています。
また完成度は低いんですが、旧版を動かしながら新版を動かしていると、負荷的な悪影響が大きく、また管理も複雑になってしまうため、上記のようなミスが起こりやすくなるので、一気に新版に差し替えつつ、欠けている部分はおいおい埋めていく、といった形を取ることにします。
MM/Memoについては、まだ一通りの機能を新版に搭載するには時間がかかりそうなので、しばらく旧版のまま動かしておきます。
_ tDiaryでリバースプロキシを使った場合のRSS
リバースプロキシを使ったら、RSS内の絶対URLがリバースプロキシ越しにアクセスしたURL(http://localhost:8080/〜とか)になってしまった場合、tdiary.confに
@base_url="http://正しいルートURL/"
を追加すると直る。
_ ノードAPIごしで属性インデックスを使いたい場合
Hyper Estraierのノードマスタを使って作成したノードでは、もちろん属性インデックスは作成されないし、ノードAPIには属性インデックスを作成するコマンドは用意されていない。
属性インデックスは文書を登録する前に作成しておく必要があるので、ノードマスタから新しいノードを作成したら、ノードマスタをいったん終了し、コマンドラインからestcmdを使って該当のノードインデックス([ノードマスタディレクトリ]/_node/[ノード名])に属性インデックスを作成する必要がある。その後は、再びノードマスタを立ち上げて、ふつうにドキュメントを追加していくだけでいい。
というわけで、リニューアル版1470.netのユーザーごとのメモ検索(http://1470.net/user/[ユーザーID]/search/)で、検索結果をユーザーで絞るのにユーザーID属性に属性インデックスをつけてあげたら、検索がまともな速度で動くようになったよ。最初属性インデックスなしでやったら、2万ドキュメントくらいで目に見えて検索速度が落ちたけど、属性インデックスをつけたら2万ドキュメントくらいでもまだまだ余裕がありそう。
次にスケール的な壁にぶつかった場合は、P2Pで分散させることを考える必要が出てくるんだろうけど、それはどのくらい先のことだろう。うちではそこまで考える必要は出てこないかなー。
すみません、嘘でした
ノードマスターの機能として、属性インデックスを作成する機能がありました。Hyper Estraier開発者のmikioさんのコメントによれば、
_confファイルのattrindexをいじると、ノードを作成した時に自動的にインデックスが張られるようになりまっす。
ということでした。
ノードマスターを起動する前に、あらかじめ_confファイルに属性インデックスに関する設定を記述しておくことで、そのマスターで作られたノードに、指定した属性インデックスが作られるようになります。
そんなところに作成手段があったのかorz...。



_ てつや [Hyper EstraierをPHPから使用したくさまよって、ここにたどり着きました。P2Pからの利用でなく、コアA..]