検索結果をアクセスランキング順にソートする方法(1)2006年03月23日 10時44分46秒

Namazu には、検索結果をアクセスランキング順にソートして表示する機能はありません。 しかし、補助プログラムとフィールドソート機能を組み合わせることで、(いくらかの制限事項はあるものの)それを実現することは可能です。

NMZ.field.accessrank というフィールドに各文書のアクセス数を記録してあるならば、CGI パラメータの sort に field:accessrank:descending を設定すると、アクセス数の多い順に結果を表示することができます。

NMZ.head.ja のソート部分を以下のように修正します。

<option selected value="field:accessrank:descending">
アクセスランキング (高)
<option value="field:accessrank:ascending">
アクセスランキング (低)
<!--
<option selected value="score">スコア
-->

また、NMZ.result.normal.ja を以下のように修正します。

<dt>${namazu::counter}. <strong>
<a href="/cgi-bin/link.cgi?uri=${uri}">${title}
</a></strong> 
(アクセス数: ${accessrank})</dt>
<dd><strong>著者</strong>: <em>${author}</em></dd>
<dd><strong>日付</strong>: <em>${date}</em></dd>
<dd>${summary}</dd>
<dd><a href="/cgi-bin/link.cgi?uri=${uri}">
${uri}</a> (${size} bytes)<br><br></dd>

検索結果をアクセスランキング順にソートする方法(2)2006年03月23日 10時59分31秒

NMZ.field.accessrank に各文書のアクセス数があれば、アクセスランキング順にソートできることを(1)で示しました。
(2)では、NMZ.field.accessrank を作成する方法をしめします。

キーとして NMZ.field.uri を、値はアクセス数でSDBMで管理することにします。
SDBM から NMZ.field.accessrank を生成するには、次に示すdbupdate.pl を使います。
dbupdate.pl はインデックスのあるディレクトリで実行します。


#!/usr/bin/perl

use Fcntl;
use SDBM_File;
use Config;

my $flags = O_CREAT | O_RDWR | BINARY();

tie( %dbhash, 'SDBM_File', 'NMZ.sdbm.accessrank', $flags, 0666)
        or die "Cannot open database $!";

chmod 0666, 'NMZ.sdbm.accessrank.dir', 'NMZ.sdbm.accessrank.pag';

my $nmz_uri = "NMZ.field.uri";
my $nmz_access_rank = "NMZ.field.accessrank";

open(NMZ_URI, "< $nmz_uri") || die "Cannot open file $!";
binmode NMZ_URI;

open(NMZ_ACCESS_RANK, "> $nmz_access_rank.$$.tmp")
        or die "Cannot open file $!";
binmode NMZ_ACCESS_RANK;

open(NMZ_ACCESS_RANK_I, "> $nmz_access_rank.i.$$.tmp")
        or die "Cannot open file $!";
binmode NMZ_ACCESS_RANK_I;

chmod 0666, "$nmz_access_rank.$$.tmp", "$nmz_access_rank.i.$$.tmp";

my $offset = 0;
while(my $uri = <NMZ_URI>) {
    my $count;

    chomp $uri;
    if (exists($dbhash{$uri})) {
        $count = $dbhash{$uri};
    } else {
        $count = 0;
    }

    printf NMZ_ACCESS_RANK "%-10d\n", $count;
    print NMZ_ACCESS_RANK_I pack('N', $offset);

    $offset += 11;
}

close(NMZ_ACCESS_RANK_I);
close(NMZ_ACCESS_RANK);

rename("$nmz_access_rank.$$.tmp", "$nmz_access_rank");
rename("$nmz_access_rank.i.$$.tmp", "$nmz_access_rank.i");

close(NMZ_URI);

untie( %dbhash );

sub BINARY {
    return O_BINARY if $Config{osname} =~ /^(MS)?Win/;
}

検索結果をアクセスランキング順にソートする方法(3)2006年03月23日 11時22分44秒

(3)では、リンクをクリックした時に、アクセス数をカウントする部分を紹介します。

次の link.cgi を /cgi-bin/ に置きます。

  • 1行目のPerl のパスは各環境に合わせて書き換えます。
  • link.cgi には chmod +x で実行属性を付けます。
  • $index にはインデックスのパスを指定します。
  • Replace 部分は、.namazurc で設定した逆のものを設定します。(逆変換)
  • インデックスのあるディレクトリは、CGI の実行ユーザに対して書き込み許可が必要です。

#!/usr/bin/perl

use CGI;
use Fcntl;
use SDBM_File;
use Config;

sub safe_die($);

my $index = "/usr/local/var/namazu/index";

my $query = new CGI;
my $uri = $query->param('uri');
my $path = $uri;

# Replace
$path =~ s!^http://www.foo.bar.jp/~foo/!/home/foo/public_html/!s;

my $flags = O_CREAT | O_RDWR | BINARY();

tie( %dbhash, 'SDBM_File', "$index/NMZ.sdbm.accessrank", 
        $flags, 0666)
        or safe_die("Cannot open database $!");

chmod 0666, 'NMZ.sdbm.accessrank.dir', 'NMZ.sdbm.accessrank.pag';

if (exists($dbhash{$path})) {
    $dbhash{$path}++ if ($dbhash{$path} != 2147483647);
} else {
    $dbhash{$path} = 1;
}

my $nmz_uri = "$index/NMZ.field.uri";
my $nmz_access_rank = "$index/NMZ.field.accessrank";

open(NMZ_URI, "< $nmz_uri") or safe_die("Cannot open file $!");
binmode NMZ_URI;

open(NMZ_ACCESS_RANK, "> $nmz_access_rank.$$.tmp")
        or safe_die("Cannot open file $!");
binmode NMZ_ACCESS_RANK;

open(NMZ_ACCESS_RANK_I, "> $nmz_access_rank.i.$$.tmp")
        or safe_die("Cannot open file $!");
binmode NMZ_ACCESS_RANK_I;

chmod 0666, "$nmz_access_rank.$$.tmp", 
        "$nmz_access_rank.i.$$.tmp";

my $offset = 0;
while(my $uri = <NMZ_URI>) {
    my $count;

    chomp $uri;
    if (exists($dbhash{$uri})) {
        $count = $dbhash{$uri};
    } else {
        $count = 0;
    }

    printf NMZ_ACCESS_RANK "%-10d\n", $count;
    print NMZ_ACCESS_RANK_I pack('N', $offset);

    $offset += 11;
}

close(NMZ_ACCESS_RANK_I);
close(NMZ_ACCESS_RANK);

rename("$nmz_access_rank.$$.tmp", "$nmz_access_rank");
rename("$nmz_access_rank.i.$$.tmp", "$nmz_access_rank.i");

close(NMZ_URI);

untie( %dbhash );

print "Location: $uri\n\n";

sub BINARY {
    return O_BINARY if $Config{osname} =~ /^(MS)?Win/;
}

sub safe_die($)
{
    my ($msg) = @_;

    open(OUT, ">> $index/error.txt");
    print $msg . "\n";
    close(OUT);

    print "Content-Type: text/plain\n\n";
    print "Error: $msg\n";

    exit 1;
}

検索結果をアクセスランキング順にソートする方法(4)2006年03月23日 11時45分09秒

ここで示したアクセスランキング順にソートする方法は簡易なサンプルのため、以下のような制限があります。

  • エラー処理は十分ではありません。プログラムを参考に、必要なエラー処理を追加して使ってください。
  • mknmz, gcnmz 等、インデックスを書き換えた後は直ぐに dbupdate.pl を実行して NMZ.filed.accessrank を更新する必要があります。
  • ロックは行っていません。
  • カウンタをリセットするには、NMZ.sdbm.accessrank.* を削除してください。
  • SDBM を使用しているため、レコード数(文書数)が非常に多いと正常動作しないかもしれません。
  • SDBM を使用しているため、キーである uri の長さが約900文字以上には対応していません。
  • 文書数が多い場合、処理に時間がかかることが予想されます。
  • Windows に対応するように設計していますが、動作確認していません。バグがあり Windows では動作しないかもしれません。
  • 日本語のパス名には対応していません。