Home

日記
Zend_Search_HyperEstraier設計中 その2Edit

いろいろいじり続けて、現在の構成は以下のような感じ。

  • 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とかしなくて済むように)した方がいいなー。

Published At2006-06-01 00:00Updated At2006-06-01 00:00

日記
Zend_Search_HyperEstraier設計中 その3Edit

だいたい以下のような形で固まりつつある。

  • 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周りのテストまで書き終わったんだけど、検索条件周りがまだいまいちすっきりしないんで、その辺をもうちょっと練ってみよう。

Published At2006-06-02 00:00Updated At2006-06-02 00:00

日記
lighttpd+fastcgi+php(Zend Framework)+のrewrite設定Edit

最初、

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は妥当な内容になるはず)けど、ちゃんと「?」以降だけを渡すようにした方がいいんだろうな。

Published At2006-06-05 00:00Updated At2006-06-05 00:00

日記
今朝渋谷駅南口側歩道橋でEdit

白いワンピースを着た女の子が、手を鳥のようにゆっくり羽ばたかせながら、歩道橋を歩いて渡っていく、という撮影をやっていたんだけど、あれはアイドルか何か? どう見ても既知外系にしか見えない絵面だったんだけど、そういう映画とかかなー。スタッフの人数が結構多かったから、エロ系ではなさそうなんだけど。

Published At2006-06-05 00:00Updated At2006-06-05 00:00

日記
145キロのデッドボールEdit

昨日久しぶりにバッティングセンターに行ったところ、145km/hのマシンがあったんで、話のたねにやってみた。すると、4、5球目にピッチングマシンの投げた球が、タイミング良く隣の人が打った打球にかすり、ほんのわずかに軌道が変わった球が、もろに俺に向かってやってきた。そんなのよけれねーよっ! というわけで、見事に左脇腹に当たった。しょせん軟球だし、145km/hとか書いてあっても実際にはそんなに出ていないとは思うけれども、いまだに当たったところがかなり痛い。ビデオとかに撮っていたら、その手のテレビ番組とかに送ってもいいくらい、レアな出来事だ。っつーか、今年はどうやら悪い方に当たり年らしい。

Published At2006-06-05 00:00Updated At2006-06-05 00:00

日記
Zendネームスペースの下に入れるのはやめたEdit

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に渡せるようにしておけば、悪影響は出ないかな?

Published At2006-06-06 00:00Updated At2006-06-06 00:00

日記
spycのパースって重いんだなEdit

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のパースってこんなに重い処理だったのか。

Published At2006-06-06 00:00Updated At2006-06-06 00:00

日記
配列が絡んだ__setの挙動Edit

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;

相当の処理だと信じていたんだけど。

Published At2006-06-06 00:00Updated At2006-06-06 00:00

日記
serializeとvar_exportどちらが速いのかEdit

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";

Published At2006-06-06 00:00Updated At2006-06-06 00:00

日記
PDO-MySQLでのUNIXソケット接続指定Edit

PDO-MySQLでホスト名(+ポート)じゃなくて、UNIXソケット経由で接続するときの設定をいつも忘れるんで、ここに書いておこう。

mysql:unix_socket=/path/to/socket;dbname=DBNAME;username=USER;password=PASS

マニュアルだとPDO_MYSQL DSNに書かれている。

Published At2006-06-07 00:00Updated At2006-06-07 00:00