3.6.3: 検索システム

さて, いよいよ検索システムに取り掛かります. 3.6.2 の議論を踏まえてこんなインタフェースにすることにします. ファイル名の前の数字は確信度で, 検索結果がどれぐらい検索要求に近いかを示します.

        $ msearch
        昨日のことですが, 
        基礎研に猿が現れました. 
        ^D
        10 inbox/1
        8 inobx/2
        ...
形態素解析 + 不要語除去 shell script の名前は japanese.sh とし, $GETAROOT/data/$handle/ に置くことにします. スクリプトの属性名を jma とすれば, ci.conf は次の様になります.
handle: my-inbox.walkuere.geta.hatoyama.hitachi.co.jp
short-name: my-inbox
jma:p: japanese.sh

japanese.sh は ChaSen を呼んだ後, 「する」および句読点を除くフィルタを付け, さらに第 3 フィールドだけを残します. この例では awk を使いましたが, ChaSen の -f オプションを使うともっとエレガントにできます (perl で第 3フィールドだけプリントしても良いですね). なお, inbox2freqfile で ChaSen を呼んでいる所も japanese.sh を呼ぶ様に修正し(awk を抜くのを忘れない様に), WAM を作り直さなければなりません. japanese.sh ができたら忘れないうちにインストールしておきます.

        $ install -c -m 755 japanese.sh $GETAROOT/data/$handle/

作成するプログラムの, 形態素解析器との通信部分のアウトラインは以下の通り(*12).

        jma = ci_value(handle, "jma");

        pipe(fds);
        switch (fork()) {
        case 子プロセス:
                close(fds[0]);
                dup2(fds[1], 1);
                exec(jma);
                exit(1);
        case 親プロセス:
                close(fds[1]);
                f = fdopen(fds[0]);
                while (fgets(buf, sizeof buf, f)) {
                        chop(buf);
                        printf("解析結果 = [%s]\n", buf);
                }
        }
        wait(子プロセス);
        close(fds[0]);

これで, 入力を単語に分割することができるようになります. あとは各々の単語について, ID に変換し, その ID で WAM を引き, 記事の name にして印刷すれば良いわけです. これは 3.6.1 でもうやりました.

        if (id = wam_name2id(w, WAM_COL, s3)) {        /* 登録されている単語 */
                if ((elem_num = wam_get_vec(w, WAM_COL, id, &vec, XR_HOSTORDER))>0) {
                for (i=0; i<elem_num; i++) {
                        e = &vec->elems[i];
                        name = wam_id2name(w, WAM_ROW, e->id);
                        printf("%s\n", name);
                }
        }

こうして完成したプログラムが msearch.c です. このプログラムは, WAM の列を書き出しているだけなので, 同じ記事が何回も現れます. そして, その出現回数はその記事が含む検索単語の数に等しくなります. そこで, その回数で検索結果を並べ直すことで, (ある意味で)検索意図により近い結果から順にならぶと考えられます. 行を, その出現回数の順に並べ直すのは sort と uniq を使って簡単にできます. では, さっそく試してみましょう.

        $ cc msearch.c -o msearch -I$GETAROOT/include -L$GETAROOT/lib -lwam -lgetamu
        $ ./msearch | sort | uniq -c | sort -nr
        昨日, 
        猿が現れました
        ^D
           7 inbox/2
           7 inbox/1
           4 inbox/4
           4 inbox/3
        ...
これでは面倒なのでフィルタを1枚かぶせることにします. 以下の様な Msearch というシェルスクリプトを作ります.
#!/bin/sh
./msearch $1 | sort | uniq -c | sort -nr

これで,
        $ ./Msearch
        昨日, 
        猿が現れました
        ^D
で検索ができるようになります.

検索結果に subject: や 本文のヘッドラインを付けて表示したりすればより使い易くできます. さらに, 文字列の代わりに, 検索したい文字列を含んだファイルのパス名や, inbox の記事番号などを与えることができる様に拡張すれば, 例題を与えて類似したメールを検索できることになります. これらの拡張機能の実装については演習問題として残しておきます.


*12: ChaSen のクライアント/サーバライブラリを使えばもっと簡単です.