cl.itemlistをsaryで検索

たつをさんと、とくひろさんのエントリを読んでいて
私もAjaxな検索をくっつけてみようかなと思って書くことにしました。
私の開発環境は、あいかわらずDebian Sarge Linuxです。

Saryに興味があったのでSaryを使用。
初めに、Saryをソースからインストールしなくても何とかなるかもと思い
apt-get installしました。

# apt-get install sary

スルスルとSaryがインストールされました。
最新版のSaryではないようです。ちょっと心配。でも気にしない。

次にcl.confのhtmlテンプレートに以下のJavascriptとformを挿入。
たつをさんのコードを、ほぼそのまま流用しました。
3文字より少ないクエリの場合はクライアントサイドで
空表示をするように変更しました。それだけ。

<script type="text/javascript">
    var xmlhttp=false;
    /*\@cc_on \@*/
    /*\@if (\@_jscript_version >= 5)
    // JScript gives us Conditional compilation, we can cope with old IE versions.
    // and security blocked creation of the objects.
    try {
	 xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch (e) {
	 try {
	     xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
	 } catch (E) {
	     xmlhttp = false;
	 }
    }
    \@else
	 xmlhttp=false
    \@end \@*/
    if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
	 try {
	     xmlhttp = new XMLHttpRequest();
	 }
	 catch (e) {
	     xmlhttp=false
	 }
    }
</script>

<script type="text/javascript">
    function countatest_popup(id, key) {
	 if (!document.getElementById) return;
	 var element = document.getElementById(id);
	 if (key.length < 3) {element.innerHTML = ""; return;}
	 key = encodeURI(key); // UTF-8?
	 if (!xmlhttp) return;
	 xmlhttp.open("GET", "sarysearch.cgi?"+key, true);
	 xmlhttp.onreadystatechange=function() {
	     if (xmlhttp.readyState==4 && xmlhttp.status == 200) {
		 element.innerHTML = xmlhttp.responseText;
	     }
	 }
	 xmlhttp.send(null)
    }
</script>

<form name="ol_form" method="GET"action="clsearch.cgi" style="margin:0;padding:0">
<input name="key" size="20" onkeyup="countatest_popup('countatest', this.value);" onfocus="countatest_popup('countatest',this.value);" type="text">
<input value="search" type="submit">
<span id="countatest"></span>
</form>


さらにsarysearch.cgiという名前のPerlプログラムを書いて設置。
このcgiが忙しく働くわけです。

#!/usr/bin/perl

#cl.itemlistをsaryでajax検索。perlで。
#3文字以下のクエリには件数だけを返す仕様
#結果表示がモタモタするのが嫌いならsleepを消す。
use strict;
use warnings;

my $sary_path = "/usr/bin/sary";
my $file_name = "./cl.itemlist";
#ミスタイプの修正を考慮(変わりにイライラするかも)
sleep(1);
print "Content-Type: text/html;\n\n";
#見た目はここで指定。別にいいじゃん。
print '<div class="ajax_sary">';
my $key = ( $ENV{'QUERY_STRING'} );
$key =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack("H2", $1 )/eg;
#saryでヒット数カウント(要mksary cl.itemlist)
my $count = `$sary_path -i -c  "$key" $file_name`;
print '*クエリの英字は大文字小文字を別として扱います。<br />';
print '○<b style="background-color:#ffff66">' . $key . '</b>';
#utf8文字列をデコード
unless ( utf8::is_utf8($key) ) {
    utf8::decode($key);
}
#4文字以上のクエリにのみプレビューを返す。
if ( ( $count > 0 ) && ( length($key) > 3 ) ) {
    print 'のヒット数 : ' . $count . '箇所<br />';
    print '○最近の記事<br />';
    #saryで検索(要mksary cl.itemlist)
    my @sary_result = `$sary_path  "$key" $file_name`;
    #検索ノイズを大雑把に消す準備
    my $tag_regex_ = q{[^"'<>]*(?:"[^"]*"[^"'<>]*|'[^']*'[^"'<>]*)*(?:>|(?=<)|$(?!\n))}; #'}}}}
    my $comment_tag_regex = '<!(?:--[^-]*-(?:[^-]+-)*?-(?:[^>-]*(?:-[^>-]+)*?)??)*(?:>|$(?!\n)|--.*$)';
    my $regex  = qq{$comment_tag_regex|<$tag_regex_};
    my $space  = '\x20';
    my $zspace = '(?:\xA1\xA1)';
    my $noise  = '(?:' . $space . '|' . $zspace . ')';
    my $limit;
    foreach (@sary_result) {
	 my ( $link, $title, $body ) = split( '\t', $_ );
	 #リンク抽出
	 $link =~ s|.+href="(.+?)".+|$1|;
	 #カテゴリ表示除去
	 $title =~ s|\[.+?\]$||g;
	 #リンク再構築
	 $link = "<a href=\"$link\">$title</a>";
	 print $link. '<br />';
	 #検索ノイズを大雑把に消す
	 $body =~ s|$regex||gi;
	 $body =~ s|$noise||gi;
	 unless ( utf8::is_utf8($body) ) {
	     utf8::decode($body);
	 }
	 my $snip;
	 #本文中に$keyがあった場合
	 while ( $body =~ m|(.{0,20})$key(.{0,20})|g ) {
	     $snip = $snip . '...' . $1 . '<b style="background-color:#ffff66">' . $key . '</b>' . $2;
	 }
	 #本文中に$keyがなかった場合(タイトルにあるとき)
	 unless ($snip) {
	     if ( $body =~ m|^(.{1,40})| ) { $snip = $1; }
	 }
	 if ( utf8::is_utf8($snip) ) {
	     utf8::encode($snip);
	 }
	 print $snip. '...' . '<br /><br />';
	 $limit++;
	 #検索結果のプレビューは3件まで。
	 if ( $limit >= 3 ) {
	     last;
	 }
    }
    print '○これ以上は検索結果からお探し下さい';
}
elsif ( $count > 0 ) {
    #3文字以下のクエリには件数だけ返す
    print 'のヒット数 : ' . $count . '箇所<br />';
}
else {
    print 'はヒットしませんでした。<br />';
}
print '</div>';


検索窓に0〜2文字までのクエリが入力された場合には、
クライアント側で空表示の処理が行なわれて、
3文字クエリには、Saryによってヒットした箇所の数だけを返します。
4文字以上のクエリには、ヒット箇所の数と、プレビューを数件返します。

ショボいとこ多数。
Saryが大文字小文字を区別するようになってて不便。うーん。
Saryを2回実行しているのが無駄。うーん。
タグの除去はcl.itemlistの生成段階で除去すべきか。うーん。
それにしても、このコード汚くないか? うーん。

まぁ、今後、自分が一番頻繁に使いそうです。


投稿者:としのり  日時:23:59:59 | コメント | トラックバック |
blog comments powered by Disqus