グローバルサーチの機能拡張

グローバルサーチの機能拡張

- Tatsuya Shirai の投稿
返信数: 82

http://moodle.org/mod/forum/discuss.php?d=123643

 一応,グローバルサーチは最低限は動作するようになったと思います.

 ここから先はオリジナル以上の機能を要望することになると思いますので,フォーラムを変更します.


2009/06/03に”新機能要望フォーラム”から”開発者フォーラム”に移動しました.

思ったよりも正常に機能しています.大きな要望というよりは小さな修正と改良で済みそうですので,こちらのフォーラムが適当かと判断しました.

技術的にも踏み込んだ内容になりそうです.

Tatsuya Shirai への返信

テキストファイルの拡張子がtxtのみ

- Tatsuya Shirai の投稿

 検索対象のファイルを拡張子で判断しています.
 現状では,テキストファイルとしてTXT,HTM,HTMLのみが対象とされています.

 他にもテキストファイルはソースプログラムなど沢山あります.

 mimetypeを設定していますので,拡張子からtext/plain形式のファイルは全てTXTと同様に扱うように拡張すれば,もう少し簡単に検索対象を増やせそうです.

Tatsuya Shirai への返信

Re: グローバルサーチの機能拡張

- Tatsuya Shirai の投稿

 「一体,どこにアクセスすればインデックスの作成ができるのか?」

 全く分かりません.ブロックの設定からダイレクトにジャンプできると便利ではないでしょうか.

Tatsuya Shirai への返信

検索結果がリソースの時にファイル名(せめて種類)が知りたい

- Tatsuya Shirai の投稿

 検索結果がファイルを指し示すリソースの場合,タイプがresourceであることと,リソースの名前(タイトル)とコース名は分かりますが,その指し示しているファイルの実際の名前はリンクをクリックするまで分かりません.

 検索結果の一覧表示の状態でファイル名が分かると「あ,明らかにそれは探しているファイルとは別だ」ということが分かる場合があるでしょう.少なくとも拡張子が分かれば,「いま探しているのはPDFだからこれは違う」ということも分かります.無駄にリソースを開く必要がなくなります.

Tatsuya Shirai への返信

Re: 検索結果がリソースの時にファイル名(せめて種類)が知りたい

- Tatsuya Shirai の投稿

 リソースのインデックス作成時に,ファイル名をadditionalkeyとして追加のフィールドで追加するようにしてみました.タイプはkeyword.トークン化されずに保存され,検索対象です.でも,発見してくれない...

 一応,めどは立ちました.単に表示するだけではなく,ファイル名での検索も可能です.ただ,parser(解析器)が日本語非対応なのが痛いですね.”表1.doc”を探そうとしても解析器は勝手に”表”にしてしまう.”表*"とワイルドカードで指定すれば良いのですけれども,検索に妙に時間が掛かる.

 近いうちにパッチを公開します.

#とても良い勉強になってしまった...

Tatsuya Shirai への返信

Re: 検索結果がリソースの時にファイル名(せめて種類)が知りたい

- Tatsuya Shirai の投稿

パッチを添付します.

この機能拡張を行った場合,グローバルサーチのインデックスファイルを作り直す必要があります.


Tracker(MDL-19458)に報告しました.

Tatsuya Shirai への返信

検索時の注意点

- Tatsuya Shirai の投稿
先に公開したパッチを当てても、意外と日本語のキーワードで目的のコンテンツを発見できないものです。

たとえばいま探し出したいファイルの名前がindex.htmlだったとします。
このファイル名はインデックスに格納される際にkeyword型のフィールドを用いますので、検索対象ではあるがトークン化されません。

それに対して検索を行う際にユーザが入力した検索キーワードは、そのまま検索語として用いられるのではなく、APIを介してフォーマット化されます。その際に、解析器を通してトークン化されてしまいます。Moodleのグローバルサーチではデフォルトの解析器として、”Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8_CaseInsensitive”が設定されています。UTF-8対応ではありますが、日本語非対応のLatin語圏向けのパーサです。

(注)以下の例は、雰囲気(特性)を伝えるためのものであって、完全に正確な解析結果に基づいている訳ではありません。

たとえば index.html と検索キーワードを入力したとします。これは index と html に分解され、デフォルトの論理演算が OR なので"index OR html"のような検索語が生成されているようです。ところでKeyword型のフィールドはトークン化されていないので、index.htmlは indexでも、htmlでもヒットしません。したがって検索結果にindex.htmlをファイル名として持つリソースは上がらない。検索キーワードを ワイルドカードを利用してindex*とすると、index.htmlが検索結果に現れます。
一方、日本語の例。いま"表1.doc"というファイル名のファイルを指し示すリソースがあったとします。このドキュメントのfilenameフィール ドもトークン化されませんので無事に"表1.doc”が格納されます。そして検索キーワードとして"表1"と入力したとします。これはパーサにより"表" のみになっていました。トークン化されている他のフィールドには"表"というキーを持つドキュメントがあり、それらが検索結果に現れて来ますが、表 1.docは出てこない。先の"index.html"の例に倣うならば"表1*"で"表1.doc"を発見できそうなものですが、実際にはダメです。" 表*"と
入力する必要があります。"表1*"よりも"表*"の方が余分な項目も検索結果に含まれてしまい、目的のファイルに到達するのに余計な時間が必要です。


日本語向けの解析器を組み込まないと、UnStore型フィールドのようにトークン化を要求するフィールドのインデックス作成時、それと検索キーワードのパージング、の2ヶ所で期待と異なる挙動を示しそうです。この問題に関しては、

・Lucene(Zend Search)向けのトークン化フィルタの日本語対応版を用意する、
・外部の解析器を用いる

の二つの解決方法が考えられます。いずれも以前に紹介させて頂いたtwk氏のWebページに解決法が書いてありました。これを参考にさせて頂くと、意外と簡単に解決するかも知れません。mecabは確かにCPUパワーを食いそうです。
最大評点: お役立ち度: ★★★★★★★ (1)
Tatsuya Shirai への返信

Re: 検索時の注意点

- Tatsuya Shirai の投稿

 まだ調査の途中ですので自信は無いのですが,インデックス作成時にトークン化された単語はmoodledata/searchフォルダ内の*.stiというファイル(複数存在)に格納されているようです.これをエディタで見てみると,「技術的にまだまだ使えないものも多いでしょうがどうせ来たので買いました」が一つのトークンであるなど,やはり日本語のトークン化はデフォルトの解析器ではうまくできていないと言わざるを得ないですね.

Tatsuya Shirai への返信

Re: 検索時の注意点

- Tatsuya Shirai の投稿

 標準のパーサで検索キーワードは"表1 白井達也 PIC16F84"がどのようにトークン化されるのか,APIを通して解析された結果をこちらに示します.やはり"表1"(全角)は"表"の1文字に,"白井達也"はそのままですが,"PIC16F84"(半角)は"pic"と小文字かつ数字は取り去られてしまいました."pic"と"pic16f84"の二つのトークンでインデックス化してくれたら"PIC"でも"PIC16F84"でも検索できて,後者だと"PIC16F877"は排除できて検索結果が絞り込みやすいのですけれども...

 グローバルサーチに関して分かったことをこちらにまとめています.解析する方は参考にして下さい.

Tatsuya Shirai への返信

では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

 これを表示可能にしてみました.効果として,ワイルドカード指定を行った場合は(予想外の副作用でしたが)該当するトークン全てが表示されます.したがってどのようなトークンがインデックスキーとしてデータベースに登録されているのかを知ることもできます.ああ,でも気を付けないと,予想外の文章がインデックス化されているとそれが見えてしまいますね...(例) ”白井はいつもレポートの提出が遅い”がトークン化されてしまっている,など--;)

 この問題は教師以上でないと見えないなどの工夫を考えましょう....とりあえず実行例のみ示します.

 この例では"index.html 白井"というキーワードでグローバルサーチを行っています.トークン化されたクエリは"index"と"html"と"白井"です.これは問題なし.

 できればindex.htmlは分離しないで欲しい.Googleなどの検索エンジンを真似て"index.html"としても(index.html)としても{index.htem}としても分離されてしまう.何かマジックキーワードがあるのだろうか? それどころか,(index.html)" としたらエラーが出ました...私のせいでは無いと思うのですけれども...(半角カッコは必須,後ろだけダブルクオートを付けた)

添付 TokenizedSample1.jpg
Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

 次はワイルドカードです.検索キーワード"白井*"です.

 こうするとトークン化されてデータベースに保存されているインデックスキーのうちの"白井*"にマッチするものがクエリオブジェクトに全て格納されます.多分,これを OR 条件で結びつけて,どれかにマッチすれば検索ヒットとするのでしょう.そういう仕組みなのかぁ.

 でも,”白井先生のロボット工学は半分以上死んでいましたけれども”という長い長いトークンは人目に晒されるととても恥ずかしい^^; やはり手を打たなくてはいけないだろうか.

#しかも「白井先生は本当にイイ人だ」は私が自分のブログに付けたタイトルです.

添付 TokenizedSample2.jpg
Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

 fs_moodleではもう少し手を加えてセキュリティ上の対策を施して公開します.

 とりあえず実験をしようという方のために現状の(無防備な)パッチを公開します.

 調査は結構大変だったのですが,修正は意外とあっさりとしたものです.

(1) search/Zend/Search/Lucene.php

 function find()

    public function find($query)
    {
        if (is_string($query)) {
            $query = Zend_Search_Lucene_Search_QueryParser::parse($query, 'UTF-8');
        }

        if (!$query instanceof Zend_Search_Lucene_Search_Query) {
            throw new Zend_Search_Lucene_Exception('Query must be a string or Zend_Search_Lucene_Search_Query object');
        }

        $this->commit();

        $hits   = array();
        $scores = array();
        $ids    = array();

        $query = $query->rewrite($this)->optimize($this);
// (Shirai150): グローバルサーチにおいて検索キーワードがどのようにトークン化されたのかを表示する機能の追加 (2009/06/18)
// (Shirai150): ここから追加
        global $tokenized_strings;
        $tokenized_strings = array();
        foreach ($query->getQueryTerms() as $term) {
            $tokenized_strings[$term->text] = $term->text;
        }
// (Shirai150): ここまで追加

        $query->execute($this);

        $topScore = 0;

        foreach ($query->matchedDocs() as $id => $num) {

(2) search/query.php

  /// prints all the results in a box

    if ($sq->is_valid()) {
        print_box_start();
       
        search_stopwatch();
        $hit_count = $sq->count();
       
        print "<br />";
       
        print $hit_count.' '.get_string('resultsreturnedfor', 'search') . " '".s($query_string)."'.";
// (Shirai150): グローバルサーチにおいて検索キーワードがどのようにトークン化されたのかを表示する機能の追加 (2009/06/18)
// (Shirai150): ここから追加
        if (isset($tokenized_strings)) {
            print ' (';
            foreach ($tokenized_strings as $token) {
                print '['.s($token).']';
            }
            print ')';
        }
// (Shirai150): ここまで追加
        print "<br />";
       
        if ($hit_count > 0) {

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Umeda Junichi の投稿

開発者フォーラムの為、こちらへの投稿でよいのか不安ではありますが、投稿させて頂きます。

こちらの環境の問題かもしれませんが、glossary(用語集)の検索結果のみタイトルが意図しない表示になっているようです。(現在、fs_moodle3.13.02を利用しております)

添付のように、1つ前のタイトルが入った検索結果に基づいた表示になってしまっています。
(タイトルを含んだ検索結果がリストに表示される前に、glossaryがリストに表示された物は、
タイトルは表示されません)

ソースを確認してみましたが、原因追及できるレベルの解析ができませんでしたので、投稿させていただきました。

ご確認のほうよろしくお願い致します。

添付 title.JPG
Umeda Junichi への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿
申し訳ありません! 問題点が認識できずにいます^^;
グローバルサーチはまだ用語の共通認識が確立していないので説明が難しいですね。。。もう少し具体的に説明して頂けると助かります!

#なお、スコアが全てゼロなのは私のケアレスミスです。

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Umeda Junichi の投稿

白井様

いつも有難うございます。

表現が悪く申し訳ありませんでした。

タイトルという表記がわかりにくかったと思われます。正確にはファイル名の項目でした。

画像にももう少し加工を加えて見ました。

添付 title.JPG
Umeda Junichi への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

 了解です! ありがとうございます.

 これはおかしいですね,明らかに私が拡張した機能のバグです.ファイル名の出力に対応させたのはリソースだけで,glossaryは関係ありませんからね^^; うん,なんとなく原因が予想できます.スコアが全てゼロで表示されてしまう件も含めて,後ほどfs_moodleのバグを報告します.

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

 寄り道をしていたら遅くなってしまいました.

 原因はforcach文内で使いまわす変数(クラスのインスタンス)の初期化を行っていなかったことです.解決法があまりスマートではありませんが,問題の現象の再現とそれが改善することを確認しました.

seach/querylib.php, private function process_results()

                    //and store it
                    $resultdocs[] = clone($resultdoc);
// (Shirai149): ここから追加
// $resultdoc->filename(Optional)がクリアされないと困るので.効率は悪い.
                    unset($resultdoc);
                    $resultdoc  = new SearchResult();
// (Shirai149): ここまで追加
                }
                $realindex++;
            } else {
               // lowers total_results one unit
               $this->total_results--;
            }
        }

        $totalpages = ceil($this->total_results/$this->results_per_page);


        return $resultdocs;
    }


(蛇足)スコアが全てゼロ

 上記修正箇所の少し上のところに以下のようなコードがあります.赤文字で示した行を書き忘れていたのが,確か原因でした.もし抜けていたら追加して下さい.次のfs_moodleではこれらの問題は全て解決済みです.

//                  $resultdoc->courseid = $hit->course_id;
//                  $resultdoc->userid  = $hit->user_id;
// (Shirai149): ここから追加
                    $resultdoc->score   = $hit->score;
                    $doc = $hit->getDocument();
                    $fieldnames = $doc->getFieldNames();
                    foreach ($fieldnames as $fieldname) {
                        $resultdoc->$fieldname = $hit->$fieldname;
                    }

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Umeda Junichi の投稿

白井様

上記2箇所修正して正常に表示される事確認できました。

いつも早急な対応有難うございます。

Umeda Junichi への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

 いえいえ,協力いただいて助かります. 

 グローバルサーチは将来,CMS(コンテンツの方)として強力なパワーを発揮すること間違い無しですが,まだまだ荒削りです.開発者へフィードバックしなくてはならない問題点や提案も多くあります.バグ出し大歓迎です!

(今後の課題):機能の拡張に限定

  • フォーラムの添付ファイルもインデックス化(実は,いまはまだ未対応)
  • リソースの’フォルダ’以下のファイルのインデックス化(セキュリティやパフォーマンスを考えると慎重に)
  • 課題の提出物へのリンクが正しくない(課題そのものへのリンクはOK)
  • RTF(リッチテキストファイル)の外部コンバータによるインデックス化
  • 日本語に対応した解析器の選定,チューニング,使用許諾

ご協力頂ける方を募集中.

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Umeda Junichi の投稿

白井様

もう一つグローバルサーチを利用して問題になっている点があります。

glossaryに対する検索結果ですが、グローバルのロールで管理者に登録されているユーザ(管理ユーザー)でグローバルサーチを行った場合は、glossary内の検索結果ついても表示されます。

同様のグローバルサーチを、グローバルのロールでコース作成者の権限を持ったユーザ(後の説明の為、ユーザAとします)で行うと、glossary内の検索結果は表示されません。(同ユーザをグローバルロールの管理者に登録した場合、glossaryの検索結果が表示される事確認済み)

検索対象となっている用語集では、ローカルに割り当てられたロールで管理者としてユーザAと管理ユーザが登録されています。その他、生徒として多数のユーザが登録されています。

管理上の理由でグローバルロールで生徒は割り当てていませんが、用語集のローカルで生徒に割り当てられたユーザでログインした場合も、glossary内の検索結果が表示されず、あまり有意義に利用できていません。

必要なロールの割り当てがありましたらご教示頂けないでしょうか?

設定が悪いのかユーザー毎で検索結果がまったく表示されなかったりなど全体の検索としてはまだ試行錯誤状態です。

Umeda Junichi への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

 ずばり,現在のグローバルサーチに欠如しているポイントを突く指摘です.ビックリしました.

 グローバルサーチのブロックは確かにコースに設置されますが,実際にはあれは単なる入り口としてしか現在は機能していません.どのコースからグローバルサーチの画面が呼び出されたのか,全く考えられていません.
 検索結果を表示して良いかどうか,その権限をもつユーザなのか,のチェックはsearch/documents/glossary_document.php(Wikiならばwiki_document.php)のfunction glossary_check_text_access()です.

 たとえば以下のようにロールをチェックしています.

    if (!$entry->approved && $user != $entry->userid && !has_capability('mod/glossary:approve', $context) && !has_capability('mod/glossary:manageentries', $context)) {
        return false;
    }

has_capability()でロールの権限をチェックするのですが,第三引数にコースIDが指定されていない,つまりグローバルロールでのチェックとなっています.コースでのロールではありません.

 fs_moodleでは,これに類した問題が生じる可能性を感じ,さらに具体的にコースの編集権限を持つかどうか判断する必要のある機能拡張を行ったので,search/query.phpを呼ぶ際にコースIDをパラメータとして渡すように改良済みです.
 コースIDの引渡しは絶対に必要になる機能ですよね.

 オリジナルではゲストでもグローバルサーチを実行できます(これはTrackerに報告済みです).もしかしたらそれは仕様なのかも知れません.
 まだ誰のためのグローバルサーチなのか,セキュリティを含めて追い込みが足りていませんね.

 コースIDを引き渡すようにする改善に関しては近いうちにTrackerに提案する予定でした.
 それに先行して,(ちょっと時間は掛かりますが)これらの権限のチェックをコースごとのロールでチェックするように改善して行こうと思います.もう少しお待ち下さい.

#凄いですね,実用的にグローバルサーチを活用しているなんて! うちは私が趣味で動かしているだけです^^;

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

 おや,当方では学生でも用語集の検索結果が表示されますね.

 もしかしたらデフォルトのロールの設定値が異なるのかも知れません.Moodle1.7から1.8, 1.9とアップグレードをしてきましたので.

 具体的に,

var_dump(!$entry->approved);
var_dump($user != $entry->userid);
var_dump(!has_capability('mod/glossary:approve', $context));
var_dump(!has_capability('mod/glossary:manageentries', $context));

を表示しますと,頭の一つと最後の一つがfalseになりますね.つまり学生ロールでありながらmod/glossary:manageentriesがtrueのようです.条件が全てANDですので一つでも条件を外せば突き崩せそうです.

 $entry->approvedは一つ一つの登録された用語の承認のことのようですね.未承認かつ記入したユーザではなく,承認やエントリの管理の権限を持たないならば表示できない,という条件のようです.でも,だとすると承認されたエントリであれば無条件に表示されそうなものですが...もしかして追加した用語の承認がされていない,ということはないでしょうか?

(当方では,確か,入力されたら全て自動的に承認される設定になっていたと思います.学生に好き勝手に使わせるために)

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Umeda Junichi の投稿

返信遅くなってしまい、申し訳ありません。

こちらの環境でも"デフォルトで認証する"の設定になっております。
いくつかロール等を変更して試してみたいと思います。

Umeda Junichi への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

了解しました.では別の条件で撥ねられている可能性がありますね.

もうちょっとこちらも調べてみます.

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Umeda Junichi の投稿

関係あるかどうかわかりませんが、少し気になってるポイントはあります。

サイト名→検索→統計 の情報欄で「インデックスデータベースの状態」を見ると、
「ドキュメント'用語集'」の項目が0になってしまっています。
(glossaryを検索対象としている管理者でも同じなのであまり関係ないとは思われますが・・・)


尚、先ほど頂いたコードを追加してみたところ、表示されるユーザ/されないユーザ共に

var_dump(!$entry->approved);     →bool(true)
var_dump($user != $entry->userid); →bool(false)
var_dump(!has_capability('mod/glossary:approve', $context)); → bool(true)
var_dump(!has_capability('mod/glossary:manageentries', $context)); →bool(true)

となりました。追加した場所が悪かったのかもしれません・・・

Umeda Junichi への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

 むむぅ,当方では管理者でも学生でも,ドキュメント’用語集’は568と表示されますね.チャットのみゼロ.ラベルが5というのはおかしいなぁ.なんとなく納得できない数字ですね.

 4つの条件のうち一つはfalseですから,私が示したif文(この直前にvar_dump()×4を追加して確認しました)は条件を満たさないので,これでは無さそうですね.でも,entry->approvedがfalse(!なのでtrue)なのは気になりますね.当方ではこれはtrueでした.


 うーん,function glossary_check_text_acces()は以下のような二つの条件チェックしか行っていないので,後者のifが成り立たないならばtrueをリターンするはずです.

function glossary_check_text_access($path, $itemtype, $this_id, $user, $group_id, $context_id){
    global $CFG;
   
    // get the glossary object and all related stuff
    $entry = get_record('glossary_entries', 'id', $this_id);
    $glossary = get_record('glossary', 'id', $entry->glossaryid);
    $context = get_record('context', 'id', $context_id);
    $cm = get_record('course_modules', 'id', $context->instanceid);
    // $cm = get_coursemodule_from_instance('glossary', $glossary->id, $glossary->course);
    // $context = get_context_instance(CONTEXT_MODULE, $cm->id);

    if (!$cm->visible && !has_capability('moodle/course:viewhiddenactivities', $context)) {
        return false;
    }
   
/* デバッグするならばここから追加
var_dump(!$entry->approved);
var_dump($user != $entry->userid);
var_dump(!has_capability('mod/glossary:approve', $context));
var_dump(!has_capability('mod/glossary:manageentries', $context));
ここまで追加 */

    //approval check : entries should be approved for being viewed, or belongs to the user unless the viewer can approve them or manage them
    if (!$entry->approved && $user != $entry->userid && !has_capability('mod/glossary:approve', $context) && !has_capability('mod/glossary:manageentries', $context)) {
        return false;
    }
   
    return true;
}

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

 思い切ってインデックスを作成し直してみて頂けないでしょうか.

 php で,search/indexer.phpをダイレクトに実行できるはずです.(fs_moodleですので)

 なお,バージョンは最新版(fs_moodle3.14.00)をお使い下さい.

 色々とバグフィクスされています.その中の何かが影響しているのかも知れません.
(fs_moodle3.15.00は未公開です.土曜日あたりに公開を予定しています)

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Umeda Junichi の投稿

fs_moodle3.14.00へのアップデート、indexの再作成(統計より)実施し成功を確認
(index作成中ではglossaryで97件ほど検出していることが表示される)

その上で、先ほどのコードを埋め込んで見ましたが、先ほどのコードの結果が何も表示されません。

glossary_check_text_accessの関数はどこから読み込まれているのでしょうか?
(検索かけてみたのですが呼び出している箇所が特定できなかったもので・・・申し訳ありません(--;

検索結果についても同様で、管理ユーザー以外でglossaryが表示されない状況です。

また、PHPで直接indexer.phpを実行してみたところ、次の様なエラーが表示されました。
Fatal error: Call to undefined function mb_convert_encoding() in D:\moodle\lib\f
s_moodle\fs_index.php on line 242

Umeda Junichi への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

 例のコードは教師以外のロール(学生など)でないと実行されない関数の中にありますので,たとえば学生ロールでチャレンジしてみて下さい.

 あと,エラーに関しては,サーバにmbstringが組み込まれていない恐れがあります.mb_convert_encoding()はあちこちで使用しています.indexer.phpを実行したときはエラー表示レベルが抑制されていないため出力されたのでしょう.是非ともmbstringのインストールを薦めます.
(私が綴り間違いしていなければ...ですが)

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Umeda Junichi の投稿

長々と対応して頂いて、本当に申し訳ありません。

上記の確認を行いました。

システムロールで学生に登録したユーザでログイン後,フロントページに設置したグローバルサーチで検索→コードの結果/glossary表示されず。

管理ユーザーでログイン後同様の操作、コードの結果表示されず/glossaryの内容は表示される。

管理者等以外で実施されるコードと言う事は、このglossary_check_text_accessがうまく読み込まれていない為に今の症状が起きているのでしょうかね...

mbstringに関してはPHP mbstring Extentionは組み込まれておりました。(Pathの設定などの問題でしょうか、現時点ではとりあえず後回しで良さそうです)

Umeda Junichi への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

 それは貴重な情報ですね.

 では,glossary_check_text_access()を呼んでいる親であるfunction can_display()の段階でreturn falseされている可能性がありますね.(search/querylib.php)

  function can_display()では以下の順番でチェックを行います.return trueで表示され,return falseで非表示です.

  1. まず管理者相当の権限を持つユーザ(doanything)であれば,return trueします.
  2. ヒットしたコンテンツの存在するコースのユーザではない,かつゲストならばそのコンテンツの存在するコースがゲストアクセスを許可していないならば(つまりコースに入ってそのコンテンツを見る権限がない)return false
  3. もしコンテンツの存在するコースが非表示なコースの場合は,ユーザに非表示のコースを見る権限がないならばreturn false
  4. そして最後にcheck_text_access()でチェック.

この4番目が呼び出されていないということは,2か3の条件を満たしてしまっているということです.

どちらだろう.でも一つ関係があるのかどうか分かりませんが,気になるのはグローバルサーチブロックをフロントページに設置しているという点ですね.これがどういう影響を与えるのか分かりませんが,私はまだチェックしたことがありません.ちょっと試してみます.

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

seach/querylib.phpのfunction can_display()で調べてみましょうか.

 以下のようにコードを追加してみて頂けませんか? これは2の条件のチェックです.管理者の場合には表示されませんが,学生ロールの場合は表示されると思います.


        // first check course compatibility against user : enrolled users to that course can see.
        $myCourses = get_my_courses($user->id);
        $unenroled = !in_array($course_id, array_keys($myCourses));
       
        // if guests are allowed, logged guest can see
        $isallowedguest = (isguest()) ? get_field('course', 'guest', 'id', $course_id) : false ;
       
echo '--can_display()--<br />';
echo 'path:';var_dump($path); echo '<br />';
echo 'unenroled:';var_dump($unenroled); echo '<br />';
echo 'isguest():';var_dump(isguest()); echo '<br />';
echo 'get_field():';var_dump(get_field('course', 'guest', 'id', $course_id)); echo '<br />';
echo 'isallowedguest:';var_dump(!$isallowedguest); echo '<br />';
        if ($unenroled && !$isallowedguest){
            return false;
        }


当方の環境では,

--can_display()--
path:string(12) "mod/glossary"
unenroled:bool(false)
isguest():bool(false)
get_field():string(1) "0"
isallowedguest:bool(true)

こんな出力です.学生ロールです.(検索結果が10個あれば10個表示されます.pathのところで判断して下さい.


Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Umeda Junichi の投稿

ご確認ありがとうございます。

頂きました、コードを埋め込み試したところ、下記の値が返って着ました。

白井様の結果とも、glossaryの場合のみ、unenroledの値が異なる様です。

--can_display()--
path:string(12) "mod/glossary"
unenroled:bool(true)
isguest():bool(false)
get_field():string(1) "0"
isallowedguest:bool(true)
--can_display()--
path:string(12) "mod/resource"
unenroled:bool(false)
isguest():bool(false)
get_field():string(1) "0"
isallowedguest:bool(true)
Umeda Junichi への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

        if ($unenroled && !$isallowedguest){
            return false;
        }

この条件を満たしてしまっているのが原因ですね.特に$unenroledがtrueである点がおかしいですね.可能性の一つはcourse_idが正しく取得できていないことです.

        // first check course compatibility against user : enrolled users to that course can see.
        $myCourses = get_my_courses($user->id);
        $unenroled = !in_array($course_id, array_keys($myCourses));
       
        // if guests are allowed, logged guest can see
        $isallowedguest = (isguest()) ? get_field('course', 'guest', 'id', $course_id) : false ;
       
echo '--can_display()--<br />';
echo 'path:';var_dump($path); echo '<br />';
echo 'unenroled:';var_dump($unenroled); echo '<br />';
echo 'isguest():';var_dump(isguest()); echo '<br />';
echo 'get_field():';var_dump(get_field('course', 'guest', 'id', $course_id)); echo '<br />';
echo 'isallowedguest:';var_dump(!$isallowedguest); echo '<br />';
echo 'course_id:';var_dump($course_id); echo '<br />';
        if ($unenroled && !$isallowedguest){
            return false;
        }

まだ先は長いかも知れませんが^^;とりあえずコースIDを表示してみましょう.ちなみに用語集はグローバルな用語集ですか,それともコース内限定の用語集ですか?(すみません,うろ覚えで質問しています)

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Umeda Junichi の投稿

用語集に関する情報は私の記載が不足していたかもしれません。申し訳ありません。

用語集はグローバル用語集になっており、フロントページのメインメニューに登録されております。

頂いたコードの部分+内容から読み取れる部分で下記のコードを追加してみました。  

echo 'course_id:';var_dump($course_id); echo '<br />';
echo 'array_keys:';var_dump(array_keys($myCourses)); echo '<br />';
echo 'unenroled?:';var_dump(!in_array($course_id, array_keys($myCourses))); echo '<br />';

結果は下記の通りです。

--can_display()--
path:string(12) "mod/glossary"
unenroled:bool(true)
isguest():bool(false)
get_field():string(1) "0"
isallowedguest:bool(true)
course_id:string(1) "1"
array_keys:array(5) { [0]=> int(2) [1]=> int(6) [2]=> int(5) [3]=> int(3) [4]=> int(4) }
unenroled?:bool(true)
--can_display()--
path:string(12) "mod/resource"
unenroled:bool(false)
isguest():bool(false)
get_field():string(1) "0"
isallowedguest:bool(true)
course_id:string(1) "3"
array_keys:array(5) { [0]=> int(2) [1]=> int(6) [2]=> int(5) [3]=> int(3) [4]=> int(4) }
unenroled?:bool(false)

in_arrayが配列内の検索をし在る無しを確認する関数の様なので、コースIDが見つからないということでしょうか。

Umeda Junichi への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

なるほど!

 グローバル用語集はフロントページ,つまりコースID=1に属しているので,$myCourseには含まれないということですか.

 ポリシーの問題になりそうですね.私はグローバル用語集を用いたことがないので分からないのですが,グローバルの場合はゲストでも用語集を検索可能なのですか? それとも設定やロールのパーミッション次第でしょうか.それによって対処が変わりますね.


ちょっと乱暴ですが...用語集に限らず,フロントページに属するコンテンツはログインユーザ(ゲストも含む?)ならば表示可能とする改良です.

search/querylib.php, function can_display()

    private function can_display(&$user, $this_id, $doctype, $course_id, $group_id, $path, $item_type, $context_id, &$searchables) {
        global $CFG;
      
      /**
      * course related checks
      */
      // admins can see everything, anyway.
      if (has_capability('moodle/site:doanything', get_context_instance(CONTEXT_SYSTEM))){
        return true;
      }
// (Debug020): グローバルサーチでグローバル用語集のエントリを表示できないバグ (2009/07/02)
// (Debug020): ここから追加
        if (($course_id == SITEID) && isloggedin()) return true;
// (Debug020): ここまで追加

        // first check course compatibility against user : enrolled users to that course can see.
        $myCourses = get_my_courses($user->id);
        $unenroled = !in_array($course_id, array_keys($myCourses));

動作確認はしていません.ちょっと試して頂けませんか? もしかしたら少し乱暴かも知れません.

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

やはり少し不安になってきました.こちらの改造の方がセキュリティは高いでしょう.

その代わり,依然として検索結果が表示されない可能性があります.

    private function can_display(&$user, $this_id, $doctype, $course_id, $group_id, $path, $item_type, $context_id, &$searchables) {
        global $CFG;
      
      /**
      * course related checks
      */
      // admins can see everything, anyway.
      if (has_capability('moodle/site:doanything', get_context_instance(CONTEXT_SYSTEM))){
        return true;
      }

        // first check course compatibility against user : enrolled users to that course can see.
        $myCourses = get_my_courses($user->id);
        $unenroled = !in_array($course_id, array_keys($myCourses));
       
        // if guests are allowed, logged guest can see
        $isallowedguest = (isguest()) ? get_field('course', 'guest', 'id', $course_id) : false ;
       
        if ($unenroled && !$isallowedguest){
// (Debug020): グローバルサーチでグローバル用語集のエントリを表示できないバグ (2009/07/02)
// (Debug020): ここから追加
            if ($course_id != SITEID)
// (Debug020): ここまで追加
            return false;
        }

先ほどの改良とはまったく別物ですので,気をつけて下さい.先のコードが残っていたら上記コードは意味を持ちません.

こちらでOKならばこれでTrackerに報告します.

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Umeda Junichi の投稿

ありがとうございますm(__)m

無事に、用語集の内容も検索される事が確認取れました。

フロントページに設置された物だったため、ややこしい事になってしまっていたんですね。

無事に、先ほどの改良を施し検索が出来る事が確認取れました。

Umeda Junichi への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Tatsuya Shirai の投稿

良かったですね!

ところで改良は,短い方(if の条件が一つの方)で大丈夫でした?

Tatsuya Shirai への返信

Re: では,実際にどのようなトークンで検索を行っているのか?

- Umeda Junichi の投稿

はい、後者の修正で問題なく動作しました。

勝手ながら、一行を置き換えた事を判りやすくしたかったので下記の様に少し修正して組み込みました。この状態でグローバルサーチで用語集も検索できる事確認取れました。

if ($unenroled && !$isallowedguest){
    // (Debug020): グローバルサーチでグローバル用語集のエントリを表示できないバグ  (2009/07/02)
    // return false; (コメントアウト)
    // (Debug020): ここから追加
    if ($course_id != SITEID) return false;
    // (Debug020): ここまで追加
}


本当に助かりました。今後ともよろしくお願い致します。

Tatsuya Shirai への返信

Zend Search Luceneのインデックスファイルを閲覧するツールは無いのか

- Tatsuya Shirai の投稿

 何か無いでしょうか.一応,検索して調べてみたのですが見当たりません.

 果たしてどのようなインデックスが作成されているのか.ファイルの中身はバイナリファイルです.頑張ればテキストエディタで読めなくは無いのですが...

 第二ステップ(第三ステップ?)として分かち書きなど日本語対応のテキスト解析の規則を導入したりした場合に,インデックスファイルを参照したくなるのは必至です.

 検索キーワードとして’*'を入力したら全てのデータがヒットして表示されるだろう,と試したらサーバが無反応になりました--;. でも,find('*')で全データを検索し,それを整形して出力するというのが,(特別なツールがもし無いならば)唯一の思いつく選択肢です.ただ,明らかにメモリを浪費しますよね...やはり無理か.

Tatsuya Shirai への返信

Re: Zend Search Luceneのインデックスファイルを閲覧するツールは無いのか

- Tatsuya Shirai の投稿

 ”Zend Search LuceneはApacheのLuceneと互換である”(ApacheのLuceneが何かを知らない)という記述を思い出して探してみたところ,LukeというJavaアプリケーションがLuceneのインデックスを閲覧可能らしいことを知りました.
http://www.getopt.org/luke/

 とりあえずここからスタートしますが,もしもっと便利なものをご存知の方,情報提供をよろしくお願いします.

Tatsuya Shirai への返信

Re: Zend Search Luceneのインデックスファイルを閲覧するツールは無いのか

- Tatsuya Shirai の投稿

 とりあえず,"A standalone minimal JAR, containing Luke and Lucene (~850kB)"で閲覧可能であることは確認しました.日本語も化けません.これはminimalですので,次は"A standalone full JAR, containing Luke, Lucene, Rhino JavaScript, plugins and additional analyzers (~2MB):"を試してみます.

Tatsuya Shirai への返信

Re: Zend Search Luceneのインデックスファイルを閲覧するツールは無いのか

- Tatsuya Shirai の投稿
報告を忘れていました。インデックスファイルの閲覧は、Lukeで間違いないでしょう。多くの方に活用されていますし、Zend Frameworkのドキュメントでも触れられていました。
多少、意味不明な機能もありますが、全体的に使いやすく、あっと言う間に使い方をマスターしました。
UTF-8対応ですので日本語の文字列もきちんと表示できます。
Tatsuya Shirai への返信

Re: Zend Search Luceneのインデックスファイルを閲覧するツールは無いのか

- Tatsuya Shirai の投稿

 インデックス化されたデータはmoodledata/searchフォルダ以下にLuceneのデータファイルとして保存されており,その中身を見るにはLukeなどのツールが必要です.でも,その情報の一部はmdl_block_search_documentsにも記録されていました.なぜかuseridは記録されていませんが,courseidやtitle,itemptypeといった有用な情報が登録されています.あと,Luceneのデータベースのidと思われる情報も含まれていますので,MoodleのデータベースとLuceneのデータベースの連携にも役立ちそうです.
 パッと見た感じでは,Moodle上で更新されたコンテンツをsearch/cron.phpで更新する際に,まずLuceneのデータベースから更新された情報を削除し,それから改めて登録し直したりしています.(追加の場合は単に追加すれば良いですし,削除された場合も同様)

Tatsuya Shirai への返信

小さなバグ(dataモジュールのauthorが氏名逆に登録される)

- Tatsuya Shirai の投稿

小さなバグです.

 他のモジュールのインデックスファイルへの登録方法と一致していない.ほかはfullname()を使用しているのに対して,こちらは欧米風で固定です.フランスの方々は気付かないでしょうね.

class function DataSearchDocument, function __construct()

        if ($record['userid']){
            $user = get_record('user', 'id', $record['userid']);
        }
// (Debug005): グローバルサーチのインデックス作成時に,dataブロックのauthorがfullname()を使っていないバグ (2009/06/03)
// (Debug005): ここからコメントアウト
//
      $doc->author = (isset($user)) ? $user->firstname.' '.$user->lastname : '' ;
// (Debug005): ここから追加
        $doc->author = (isset($user)) ? fullname($user) : '' ;
// (Debug005): ここまで追加
        $doc->contents  = $record['content'];
        $doc->url       = data_make_link($record['dataid'], $record['id']);

時間ができたらTrackerに報告します.なお,この修正はインデックスを作成するまで効果が見えません.上記修正の後にインデックスを作り直してください.

Tatsuya Shirai への返信

Re: 小さなバグ(dataモジュールのauthorが氏名逆に登録される)

- Tatsuya Shirai の投稿

これと全く同じバグがglossary(search/documents/glossary_document.php)にもありました.

glossaryの方は2箇所,修正が必要です.

Tatsuya Shirai への返信

グローバルサーチ修正

- S. Yamane の投稿
元スレッドのグローバルサーチのマルチバイト化がmoodle-weekly-19.tgzにとりいれられましたね。日本からのパッチで多くのマルチバイト圏に貢献できるのは素晴らしい。

http://download.moodle.org/stable19/CHANGES
http://tracker.moodle.org/browse/MDL-19342

でもヴァレリーさん、白田さんの名前を書き間違ってる...。
S. Yamane への返信

Re: グローバルサーチ修正

- Tatsuya Shirai の投稿

> でもヴァレリーさん、白田さんの名前を書き間違ってる...。

あ,いや,あはは,白井です^^;  でも,ヴァレリーさんの"tatsuva"には驚きました.

 グローバルサーチはグングンと動きが良くなってきていますよ.

 もう,出るわ出るわ,今日の夕方からのデバッグの結果はここに書くと混乱するので控えています.課題の提出ファイルをインデックス化する箇所は全く動いていなかったようです.そこを動かしたら明白なバグがいくつか判明しました.ほとんどがケアレスミスで,エラーメッセージをOnにしていれば一目でわかるタイプのものですね.

 いまはコマンドラインからインデックス作成のプロセスが走らせられるようになったのでデバッグも絶好調です.さらにLuceneのデータベースもLukeで細かくチェックできることが判明したので,アラ探しも加速中です.

 なんとか明日あたりに更新された部分をマージして,それ以外の修正箇所をパッチにしたものをまたTrackerに報告します.それで更新されれば皆さんにもメリットがあるでしょう.

Tatsuya Shirai への返信

Re: グローバルサーチ修正

- S. Yamane の投稿

> あ,いや,あはは,白井です^^; でも,ヴァレリーさんの"tatsuva"には驚きました.

うう、失礼しました。
コミッターの Valery Fremaux さん同様に興奮して間違えました恥ずかしい

先月三重大のBOFで、なかなか取り入れられない日本発のパッチがあるという話がありましたが、今回すぐに安定版に取り入れられたことがインパクトの大きさを示しています。

S. Yamane への返信

Re: グローバルサーチ修正

- Tatsuya Shirai の投稿

 いまMoodle1.9.5+のコードをザッと確認しました.なるほど,時間と共に消えてしまうChange logだけけではなく,ソースコードの方にも,

>* @contributor Tatsuva Shirai 20090530

と,あちこちに入っていますね.これを修正お願いするのもなんなので,これからは海外ではTatsuvaをニックネームとしましょう(笑).音も近いです(どうもタツヤは発音し難そうでしたから).

 コードも,こちらから提案したものをアレンジし直して適用して頂いていますし,私が見落としていた部分も随分と修正されているようです."animateしよう"とTrackerにコメントしてあったのを「ア,アニメですか?」と驚いたのですが(きちんと辞書で調べ直して知識を修正しました^^;),本気度が上がってきたようですね.

 私は(Kagoya先生も書いているように)てっきりグローバルサーチはMoodle1.9からの機能だと思っていたのですが,Moodle1.8にもsearchはありますね.でも,まだ実験的な状態にあるのは事実です.本家がMoodle1.9への適用のみを行っているように,fs_moodleも申し訳ありませんがMoodle1.9以降のみの対応とさせて頂きます.単なるケアレスミスのバグも多いので両方とも対応するとなると結構,大変です.安定するまではマージできないですね.でも,結構,独立している機能ですからフォルダをコピーするだけで動いてしまうかも?

Tatsuya Shirai への返信

Re: グローバルサーチ修正

- Tatsuya Shirai の投稿
ここまでのところのバグと,コマンドラインでインデックスの再作成と更新を可能とする改良を合わせてMDL-19426に投稿しました.今回も素早く改善してくれると良いですね.
Tatsuya Shirai への返信

追加されたブログの情報をインデックス化(更新)する際の小さなバグ

- Tatsuya Shirai の投稿

 search/documents/user_document.phpのfunction user_single_document()に極めて単純なケアレスミスを見つけました.

 どうやら一括してインデックスを作成する際には呼ばれないようです.手動でインデックスの更新(後述)する際に利用されるようです.

(Debug006):グローバルサーチのブログのインデックス作成において,get_records()の戻り値が配列(ただしレコードは一つ)であることを見落としているバグ (2009/06/03)
(Debug007):グローバルサーチのブログのインデックス作成において,postの場合のユーザIDを$post->useridではなく$user->idから得ようとして失敗するバグ (2009/06/03)

function user_single_document($id, $itemtype) {
    if ($itemtype == 'user'){
        if ($user = get_record('user', 'id', $id)){
            $userhash = get_object_vars($user);
            return new UserSearchDocument($userhash, $user->id, 'user', null);
        }
    } elseif ($itemtype == 'post') {
        if ($post = get_records('post', 'id', $id)){
// (Debug006):グローバルサーチのブログのインデックス作成において,get_records()の戻り値が配列(ただしレコードは一つ)であることを見落としているバグ (2009/06/03)
// (Debug006): ここから追加
            $post = $post[$id];
// (Debug006): ここまで追加
            $texts = array();
            $texts[] = $post->subject;
            $texts[] = $post->summary;
            $texts[] = $post->content;
            $post->description = implode(" ", $texts);
            $posthash = get_object_vars($post);
// (Debug007):グローバルサーチのブログのインデックス作成において,postの場合のユーザIDを$post->useridではなく$user->idから得ようとして失敗するバグ (2009/06/03)
// (Debug007): ここから修正
//
          return new UserPostSearchDocument($posthash, $user->id, 'post', null);
            return new UserPostSearchDocument($posthash, $post->userid, 'post', null);
// (Debug007): ここまで修正
        }

Tatsuya Shirai への返信

実験的: インデックスファイルの更新作業

- Tatsuya Shirai の投稿

 サーバのコンソールでsearch/cron.phpを実行(searchフォルダにて php cron.php)するとダイレクトに実行してはいけない(Direct access to this script is forbidden.)といったエラーが発生して実行できません.

 いえ,どうしても実行したいという訳ではありませんでした.このスクリプトは一体なんだろう?と思っただけです.それに実際,これを実行して安全なのかも分かっていません.ところで実行可能にするのはとても簡単です.

<?php
       (中略:コメントのみなので)

    require_once('../config.php');

    if (!defined('MOODLE_INTERNAL')) {
        die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
    }

    require_once("$CFG->dirroot/search/lib.php");

    if (empty($CFG->enableglobalsearch)) {
        mtrace('Global searching is not enabled. Nothing performed by search.');
    }
    else{
       include("{$CFG->dirroot}/search/cron_php5.php");
    }
?>

この赤いコードを一行,追加するだけです.元々はたとえばadmin/cron.phpなどから呼ばれることを想定しているのだと思います.

 これでsearchフォルダ内でphp cron.phpを実行可能になります.これを行うと,既に構築されているインデックスファイルを更新してくれます.一瞬で終わるところから考えて,インデックスを最後に作成してから追加されたコンテンツをデータベースから探し,それだけをインデックス化しているようです.

 いま現在,グローバルサーチで辛うじて用意されているインデックスの作成法は,コースに配置したグローバルサーチブロックの"Go"を押して表示される”検索”の画面で,”統計”をクリックして表示される”インデックスの作成(実)”(でしたか?)をクリックする,という遠い道のり以外にありません.しかもこれを実行すると,いまあるインデックスは全て削除されてゼロから作り直しを始めますので長時間,サーバに負荷が掛かり続けます.

 さて,これで更新のみが行えるようになったようなのですが,まだ安全である保障はありません.実際に,こちらの更新の作業で明らかになったバグもあります.

Tatsuya Shirai への返信

Re: 実験的: インデックスファイルの更新作業

- Tatsuya Shirai の投稿

 なぜ,cron.phpに手を出したかと良いますと,インデックスの作成がサーバにとって非常に負担の高い作業だからです.Webブラウザを介してindexer.phpを実行するということは,apacheを介してスクリプトを実行することになります.メモリの制限もありますし,スクリプトのコールからページ作成完了(スクリプトの終了)までに長時間を要する(当方の環境では10分以上)ためにリバースプロキシなどを通しているとタイムアウトが発生してしまうといった問題がありました.admin/cron.phpもそうですが,開始から終了まで時間の掛かる処理はサーバサイドのプロンプトで直接,PHPスクリプトを実行した方が負担が少ないですよね.

 残念ながらインデックスの作成をゼロから行う方法が分かりません.search/cron.phpはあくまでインデックス群の更新であって,moodledata/searchフォルダ内を空にした状態ではエラーを発して停止してしまいます.では,search/indexer.phpを直接,phpで実行できないか?とも考えたのですが,これもダメです.相手がブラウザであることを前提としていますのでHTMLが画面に表示されるくらいは,まぁ良いのですが,Javascriptを画面に表示して停止です.

 もしゼロからインデックスを作成することをサーバ上で成功された方は是非とも方法を教えて下さい.

Tatsuya Shirai への返信

Re: 実験的: インデックスファイルの更新作業

- Tatsuya Shirai の投稿

 試しているうちに,意外と簡単に動いてしまいました.

 これもcron.phpの改造と同様に,安全性は全く保障されません.もしかしたら外部からWeb経由で勝手に実行されてしまう可能性もあります.あくまで実験用です.

 search/indexer.phpに対して以下の修正を行うことで,ログインしていない状態で,searchフォルダにてindexer.phpをダイレクトに実行可能です.

/// only administrators can index the moodle installation, because access to all pages is required

// (FS_TEST): ここから追加
    $rundirect = false;
    if (isset($_SERVER['REMOTE_ADDR'])) {
// (FS_TEST): ここまで追加
    require_login();
// (FS_TEST): ここから追加
    } else {
        $rundirect = true;
    }
// (FS_TEST): ここまで追加

   
    if (empty($CFG->enableglobalsearch)) {
        error(get_string('globalsearchdisabled', 'search'));
    }
   
// (FS_TEST): ここから追加
    if (!$rundirect) {
// (FS_TEST): ここまで追加
    if (!has_capability('moodle/site:doanything', get_context_instance(CONTEXT_SYSTEM))) {
        error(get_string('beadmin', 'search'), "$CFG->wwwroot/login/index.php");
    }
// (FS_TEST): ここから追加
    }
// (FS_TEST): ここまで追加
   
/// confirmation flag to prevent accidental reindexing (indexersplash.php is the correct entry point)

    $sure = strtolower(optional_param('areyousure', '', PARAM_ALPHA));
   
// (FS_TEST): ここから追加
    if ($rundirect) $sure = 'yes';
// (FS_TEST): ここまで追加
    if ($sure != 'yes') {
        mtrace("<pre>Sorry, you need to confirm indexing via <a href='indexersplash.php'>indexersplash.php</a>"
              .". (<a href='index.php'>Back to query page</a>).</pre>");
   
        exit(0);
    }

 うーん,ログインしていないのにデータベースにアクセスできてしまうのも不思議な話です.せめてログインだけでも対話的にできるようにした方が良いですね...(でも,cron.phpもログインしないで処理できていますね,考えてみたら)

 

Tatsuya Shirai への返信

Re: 実験的: インデックスファイルの更新作業

- Tatsuya Shirai の投稿

 やはりこの改造では甘かったです.

 管理者権限でログインをしていないので,

    if (!has_capability('moodle/site:doanything', get_context_instance(CONTEXT_SYSTEM))) return;

この手のチェックを通れず,コースファイルへのアクセスができませんでした.再考が必要です.


再考しました.どうやらうまくいきそうです.やはりadmin/cron.phpにヒントがありました.さらに以前の改造よりもシンプルになりました.

<?php

(中略)

//this'll take some time, set up the environment
@set_time_limit(0);
@ob_implicit_flush(true);
@ob_end_flush();

    /**
    * includes and requires
    */
    require_once('../config.php');
    require_once($CFG->dirroot.'/search/lib.php');

//require_once("debugging.php");

    ini_set('include_path', $CFG->dirroot.DIRECTORY_SEPARATOR.'search'.PATH_SEPARATOR.ini_get('include_path'));

/// only administrators can index the moodle installation, because access to all pages is required

// (FS_TEST): ここから追加
    if (isset($_SERVER['REMOTE_ADDR'])) {
// (FS_TEST): ここまで追加
    require_login();
// (FS_TEST): ここから追加
    } else {
        $_GET['areyousure'] = 'yes';
        define('FULLME', 'cron');
/// emulate normal session
        $SESSION = new object();
        $USER = get_admin();      /// Temporarily, to provide environment for this script

/// ignore admins timezone, language and locale - use site deafult instead!
        $USER->timezone = $CFG->timezone;
        $USER->lang = '';
        $USER->theme = '';
        course_setup(SITEID);
    }
// (FS_TEST): ここまで追加
   
    if (empty($CFG->enableglobalsearch)) {
        error(get_string('globalsearchdisabled', 'search'));
    }
   
    if (!has_capability('moodle/site:doanything', get_context_instance(CONTEXT_SYSTEM))) {
        error(get_string('beadmin', 'search'), "$CFG->wwwroot/login/index.php");
    }
   
/// confirmation flag to prevent accidental reindexing (indexersplash.php is the correct entry point)

    $sure = strtolower(optional_param('areyousure', '', PARAM_ALPHA));
   
    if ($sure != 'yes') {
        mtrace("<pre>Sorry, you need to confirm indexing via <a href='indexersplash.php'>indexersplash.php</a>"
              .". (<a href='index.php'>Back to query page</a>).</pre>");
   
        exit(0);
    }

これでコマンドラインから実行しても管理者としてログインしたのと同じに動作して,インデックスファイルを再作成してくれます.

Tatsuya Shirai への返信

実験的: インデックスファイルの再構築

- Tatsuya Shirai の投稿

 カレントディレクトリの移動にも対応しました.これでindexer.phpをフルパス指定してどこからでも実行できます.これで多分,ファイナルでしょう.

 なお,Windows系OSがサーバでならばphp-win.exeを使うことでメッセージ表示無しで実行できます.タスクに登録するならばこちらですね.
 逆にメッセージを見ながら進捗状況をWeb版同様にチェックしたいならば,php.exeを使い,標準出力をファイル(foo.html)にリダイレクトし,それをWebブラウザで表示しながら「更新ボタン」を押し続ければ,ほぼリアルタイムに状況を監視できます.

//this'll take some time, set up the environment
@set_time_limit(0);
@ob_implicit_flush(true);
@ob_end_flush();

    /**
    * includes and requires
    */
// (Shirai146): グローバルサーチのインデックスの再構築(search/indexer.php)をコマンドラインでも実行可能とする改良 (2009/06/05)
// (Shirai146): ここから追加
    if (!isset($_SERVER['REMOTE_ADDR']) && isset($_SERVER['argv'][0])) {
        chdir(dirname($_SERVER['argv'][0]));
    }
// (Shirai146): ここまで追加
    require_once('../config.php');
    require_once($CFG->dirroot.'/search/lib.php');

//require_once("debugging.php");

    ini_set('include_path', $CFG->dirroot.DIRECTORY_SEPARATOR.'search'.PATH_SEPARATOR.ini_get('include_path'));

/// only administrators can index the moodle installation, because access to all pages is required

// (Shirai146): ここから追加
    if (isset($_SERVER['REMOTE_ADDR'])) {
// (Shirai146): ここまで追加
    require_login();
// (Shirai146): ここから追加
    } else {
        $_GET['areyousure'] = 'yes';
        define('FULLME', 'cron');
/// emulate normal session
        $SESSION = new object();
        $USER = get_admin();      /// Temporarily, to provide environment for this script

/// ignore admins timezone, language and locale - use site deafult instead!
        $USER->timezone = $CFG->timezone;
        $USER->lang = '';
        $USER->theme = '';
        course_setup(SITEID);
    }
// (Shirai146): ここまで追加
   
    if (empty($CFG->enableglobalsearch)) {
        error(get_string('globalsearchdisabled', 'search'));
    }
   
    if (!has_capability('moodle/site:doanything', get_context_instance(CONTEXT_SYSTEM))) {
        error(get_string('beadmin', 'search'), "$CFG->wwwroot/login/index.php");
    }

 

Tatsuya Shirai への返信

実験的: インデックスファイルの更新作業

- Tatsuya Shirai の投稿

 こちらはsearch/cron.phpをコマンドラインからでも実行可能とする改良です.indexer.phpと同様にサーバ上のどこからでも実行できます.

 indexer.phpはインデックスを全て削除してゼロから作り直すのに対して,cron.phpは前回,作成あるいは更新した後に変更(追加/削除)されたコンテンツに対するインデックスのみを更新します.したがって処理の負荷はindexer.phpよりも圧倒的に少ないです.ただし,先にindexer.phpでインデックスファイルを作成しておかないと実行してもエラーで終了します.

 いま現在,admin/cron.phpからは呼ばれていないようですので,インデックスファイルを最新状態に保ちたいのであればsearch/cron.phpを定期的に実行する必要があるでしょう.タスクなどに追加すると良いでしょう.なお,出力は単なるテキストですので,indexer.phpのようにWebブラウザを用いて閲覧する必要はありません.ですが,タスクで実行するならばphp-win.exeを使うべきでしょう.

 // (Shirai147): グローバルサーチのインデックスの更新(search/cron.php)をコマンドラインでも実行可能とする改良 (2009/06/05)
// (Shirai147): ここから追加
    /**
    * includes and requires
    */
    if (!isset($_SERVER['REMOTE_ADDR'])) {
        if (isset($_SERVER['argv'][0])) {
            chdir(dirname($_SERVER['argv'][0]));
        }
        require_once('../config.php');
        define('FULLME', 'cron');
/// emulate normal session
        $SESSION = new object();
        $USER = get_admin();      /// Temporarily, to provide environment for this script

/// ignore admins timezone, language and locale - use site deafult instead!
        $USER->timezone = $CFG->timezone;
        $USER->lang = '';
        $USER->theme = '';
        course_setup(SITEID);
    }
// (Shirai147): ここまで追加
    if (!defined('MOODLE_INTERNAL')) {
        die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
    }

Tatsuya Shirai への返信

なお,これらの修正は

- Tatsuya Shirai の投稿

 search/indexer.php, search/cron.phpの修正は本日公開予定のfs_moodle3.13.01のMoodle1.9.5+版には組み込んでおきますので,そのまま利用可能なはずです.

Tatsuya Shirai への返信

Re: なお,これらの修正は

- Tatsuya Shirai の投稿

 search/cron.phpを解析しているのですが,どうも挙動が怪しい.メカニズムは少しずつ分かってきたのですが...新しく追加されたコンテンツを追加する(add.php)のは仕組みが分かるのですが,更新されたコンテンツを更新(update.php)するメカニズムが不十分のような気がします.

 しばらくはsearch/cron.phpではなく,search/indexer.phpでインデックスをゼロから作り直した方が安全そうです.

Tatsuya Shirai への返信

リソース(単一のファイル)のインデックス作成時にresource_get_physical_file()を呼ぶ際の引数の順番を間違えている些細な(?)バグ

- Tatsuya Shirai の投稿

 具体的にどのような影響があるのか良く分かっていないが,ともかくおかしい.

 search/documents/resource_document.php

function resource_single_document()

    if ($resource){
        $coursemodule = get_field('modules', 'id', 'name', 'resource');
        $cm = get_record('course_modules', 'id', $resource->id);
        $context = get_context_instance(CONTEXT_MODULE, $cm->id);
        if ($resource->type == 'file' && @$CFG->block_search_enable_file_indexing){
// (Debug008): グローバルサーチのリソースにて対象がファイルの場合にresource_get_physical_file()の引数の順番を間違えているためインデックスが正常に作成されないバグ (2009/06/04)
// (Debug008): ここから修正
//
          $document = resource_get_physical_file($resource, true, $context->id);
            $document = resource_get_physical_file($resource, $context->id, true);
// (Debug008): ここまで修正
            if (!$document) mtrace("Warning : this document {$resource->name} will not be indexed");
            return $document;
        } else {
            return new ResourceSearchDocument(get_object_vars($resource), $context->id);
        }
    }
    mtrace("null resource");
    return null;
} //resource_single_document

Tatsuya Shirai への返信

Re: リソース(単一のファイル)のインデックス作成時にresource_get_physical_file()を呼ぶ際の引数の順番を間違えている些細な(?)バグ

- Tatsuya Shirai の投稿

 通常の処理ではなく,cron.phpで更新(update)時に呼ばれるのかも知れません.

 気になっているのは,Lukeでインデックスファイルを探してみても,itemtypeがfileのdocumentが一つも見当たらないこと.itemtypeがtxtとhtmlはdoctypeがresourceのものにいくつも存在する.つまりファイルへリンクを貼ったリソースのファイルの中身をxpdfやantiwordでテキスト化していながら,それがデータベースに登録されていないっぽいことが,いま疑っている問題点です.

Tatsuya Shirai への返信

Re: リソース(単一のファイル)のインデックス作成時にresource_get_physical_file()を呼ぶ際の引数の順番を間違えている些細な(?)バグ

- Tatsuya Shirai の投稿

 リソースのインデックスファイルへの登録は,search/documents/resource_document.php内のfunction resource_get_content_for_index()で行っている.SQLでtypeがfileではないリソースをMoodleのデータベースから探し出して,それを配列$documentsに追加していく.次にtypeがfileのリソースを探し出して,それをresource_get_physical_file()を呼んでファイルの種類に応じてプラグインを呼び,中身をテキスト化してdocument化して$documentsに追加する.

 プラグインを呼び出していることはログメッセージによって確認できている.一括処理の場合はresource_get_physical_file()の引数,$getsingleがfalseなので,引数として渡された$documentsに$documents[]=で追加していく.アドレス渡し(?)なので呼び出し元の関数の方で追加されたdocumentを参照できるはず.$getsingleがtrueの場合はdocumentをreturnするだけで$documents[]=としないので,万が一,$getsingleだと勘違いされたら問題なのだが,そういう感じでは無さそう.うーむ,未だ謎です.


間違いがありました.コマンドラインからindexer.phpを実行できるように改造したのですが,やはりログインのプロセスをバイパスしたのは早計でした.has_capability()のチェックがsearch/documents/physical_???.php内にありました.つまり外部コンバータが今回は全く呼ばれていなかった.単純に私だけのミスです.


ログインプロセスを安全にバイパスする方法は解決しました.

Tatsuya Shirai への返信

提出された課題がファイル(uploadsingle/upload)であってもテキスト扱いされる問題のありがちなバグ

- Tatsuya Shirai の投稿

search/documents/assignment_document.php, function assignment_get_content_for_index()

 こういうバグは久しぶりに見ると新鮮です.ゼロから作るのは大変だったと思います.それに関しては諸手を挙げて感謝.

// (Debug010): グローバルサーチの課題の種類がuploadsingle/uploadであってもtextと勘違いされてファイルのインデックス化が行われないバグ (2009/06/04)
// (Debug010): ここから修正
//
              if ($submitted->source = 'text'){
                if ($submitted->source == 'text'){
// (Debug010): ここまで修正
                    $submission->description = $submitted->data;
                    $submission->description = preg_replace("/<[^>]*>/", '', $submission->description); // stip all tags
                    $documents[] = new AssignmentSearchDocument(get_object_vars($submission), $cm->id, 'submission', $assignment->course, $submission->userid, $context->id);
                    mtrace("finished online submission for {$submission->authors} in assignement {$assignment->name}");
// (Debug010): ここから修正
//
              } elseif ($submitted->source = 'files'){
                } elseif ($submitted->source == 'files'){
// (Debug010): ここまで修正
                    $SUBMITTED = opendir($submitted->path);
                    while($entry = readdir($SUBMITTED)){
                        if (ereg("^\.", $entry)) continue; // exclude hidden and dirs . and ..
                        $path = "{$submitted->path}/{$entry}";
                        $documents[] = assignment_get_physical_file($submission, $assignment, $cm, $path, $context_id, $documents);
                        mtrace("finished attachement $path for {$submission->authors} in assignement {$assignment->name}");
                    }
                    closedir($submission->path);                           
                }

道理で,課題のファイルのインデックス化が行われないわけです.でも,これ,ものすごい処理時間になりそうなので,バグのままの方が良かったりして.

プログラムの課題だと,皆,ほぼ同じ内容のファイルをアップロードしますから,80名くらい,同じキーワードにマッチして鬱陶しいかも.あ,それでインデックス化するテキストファイルの種類を拡張子がtxtのものだけに制限しているのかな?(CとかCPPとかは無視)

Tatsuya Shirai への返信

2009/06/05現在のグローバルサーチの最新パッチ

- Tatsuya Shirai の投稿

 今日は時間がありませんので,Trackerに報告するのは来週になりそうです.

 とりあえず2009/06/03版のMoodle1.9.5+のsearchフォルダ以下に対するパッチを添付します.

 これでfs_moodle以外でもsearch/indexer.phpやsearch/cron.phpをコマンドラインから実行したり,その他,まだ未報告のバグ(特に課題関係)が修正されます.

Tatsuya Shirai への返信

些細なミス(学生ロールでのアクセス可否のチェック:Wiki)

- Tatsuya Shirai の投稿

うーん,結構,ボロボロとバグがありそうですね,この辺りは.

とりあえずWikiは直したけれども,あとresourceもバグがありますね.多分,デバッグをする人は教師ロールなんでしょうね.

心配なのは,これはアクセス可否のチェックなので,もしかしたら学生が見えてはいけないページ等が見えてしまわないだろうか.

function wiki_check_text_access()

function wiki_check_text_access($path, $itemtype, $this_id, $user, $group_id, $context_id){
    global $CFG;
   
    // get the wiki object and all related stuff
// (Debug016): グローバルサーチの検索結果にWikiのページが含まれる時の学生ロール等(教師以上を除く)でのアクセス可否のチェックでワーニングが出るバグ (2009/0612)
// (Debug016): ここから修正
//
  $page = get_record('wiki_pages', 'id', $id);
    $page = get_record('wiki_pages', 'id', $this_id);
//  $entry = get_record('wiki_entries', 'id', $page->wiki);
    $wiki = get_record('wiki', 'id', $page->wiki);
//  $course = get_record('course', 'id', $entry->course);
    $course = get_record('course', 'id', $wiki->course);
// (Debug016): ここまで修正
    $context = get_record('context', 'id', $context_id);
    $cm = get_record('course_modules', 'id', $context->instanceid);
    if (empty($cm)) return false; // Shirai 20090530 - MDL19342 - course module might have been delete

Tatsuya Shirai への返信

些細なミス(学生ロールでのアクセス可否のチェック:Resource)

- Tatsuya Shirai の投稿

 こちらはリソース.この修正を行ったら学生ロールで表示されるリソースが減ったような気がする.だとするとまずいですね.

function resource_check_text_access()

function resource_check_text_access($path, $itemtype, $this_id, $user, $group_id, $context_id){
    global $CFG;
   
    include_once("{$CFG->dirroot}/{$path}/lib.php");
   
    $r = get_record('resource', 'id', $this_id);
    $module_context = get_record('context', 'id', $context_id);
    $cm = get_record('course_modules', 'id', $module_context->instanceid);
    if (empty($cm)) return false; // Shirai 20090530 - MDL19342 - course module might have been delete
    $course_context = get_context_instance(CONTEXT_COURSE, $r->course);
// (Debug016): グローバルサーチの検索結果にリソースが含まれる時の学生ロール等(教師以上を除く)でのアクセス可否のチェックでワーニングが出るバグ (2009/0612)
// (Debug016): ここから追加
    $course = get_record('course', 'id', $r->course);
// (Debug016): ここまで追加

    //check if course is visible
    if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $course_context)) {
        return false;
    }

    //check if user is registered in course or course is open to guests
    if (!$course->guest && !has_capability('moodle/course:view', $course_context)) {
        return false;
    }

Tatsuya Shirai への返信

Re: 些細なミス(学生ロールでのアクセス可否のチェック:Resource)

- Tatsuya Shirai の投稿
 緊急度が高いと思われるので,この2点は速やかにTracker (MDL-19477) に報告しました.セキュリティレベルを高めにして.
最大評点: お役立ち度: ★★★★★★★ (1)
Tatsuya Shirai への返信

Re: 些細なミス(学生ロールでのアクセス可否のチェック:Resource)

- Tatsuya Shirai の投稿

この修正も,アッと言う間に修正して頂けました.

気持ちいいですね.

Tatsuya Shirai への返信

検索オプションで,どのモジュールを検索しますか? のプルダウンメニューに[[modulenameplural]]と表示される

- Tatsuya Shirai の投稿

 この問題はTracker(MDL-19546)に報告しました.

 言語ファイルにも関わる内容です.

http://www.suzuka-ct.ac.jp/mech/moodle/mod/wiki/view.php?id=320&page=Debug018

添付した画面は英語モードでの表示例です.

添付 modulenameplural.jpg
Tatsuya Shirai への返信

フォーラム投稿の添付ファイルのインデックス化

- Tatsuya Shirai の投稿

 実はフォーラム投稿の添付ファイルのインデックス化は行われていないのですよね.勿体無い.

 でもブログの添付ファイルのインデックス化は実装されています.そこで見よう見真似で実装して見ました.一応,まぁ,それらしく動いているような気がします.search/cron.phpでもうまく動くかどうかはまだ分かりませんが.

 リスクがありますし,修正箇所が非常に多いのでこちらにパッチは公開しません.時間ができたらTrackerに提案してみます.次に公開するfs_moodleには組み込まれていますが,デフォルトでは無効にしておきますのでfsconfig.phpでdefine()を有効にして下さい.


このような感じに,タイトルの頭に”ファイル:"という文字列が付くので判別し易いですね.

添付 ForumAttachments.jpg
Tatsuya Shirai への返信

コースIDを引き渡せば,簡単に元のコースに戻ることができる

- Tatsuya Shirai の投稿

 現在,グローバルサーチはどのコースに設置したブロックから呼び出されても,コースIDを貰っていないためHOMEに属するサービスとして動作します.これは長い目で見ると勿体無い.つまりグローバルサーチを誰でも実行できてしまって良いのか,という問題にも関わります.あるコースに設置したグローバルサーチはそのコースに属するメンバ以外は利用できないとか,あるいは表示される検索結果はそのコース内のコンテンツに限る,といった制限ができません.

 もう一つ,どうでも良いような,でも重要な問題があります.元のコースに戻れない(笑い).ブラウザの”戻る”ボタンで戻ろうにも,必死になって検索を行っていると履歴の遥か彼方に元のコースのページは消え去っています.
 フッターに表示されているのは「HOME」.他のブロックやりソース同様に,コースからコンテンツに移動しても元のコースへのリンクが表示されていれば迷子にならずに元の作業に戻れます.また頭から辿ってコースに戻るのはイヤです.

 という訳で,グローバルサーチにコースIDを引き渡す改良を行いました.コースに配置されたブロックから検索ページに移動する時,標準検索から検索オプションのページに移る時,検索オプションから標準検索のページに移る時,この3つのタイミングでコースIDを渡すだけですから簡単な改良です.あとはコースIDを元にして$COURSEをデータベースから取得し,print_footer($COURSE);とするだけでOKです.

 簡単な改良かつ独立していますので,Tracker(MDL-19638)に報告しました.

Tatsuya Shirai への返信

インデックスデータベースの状態の表示が少し変?

- Tatsuya Shirai の投稿

 Umedaさんの指摘によって気付いたのですが,統計のページに表示されるインデックスデータベースの状態表示が少し変です.インデックス化されたドキュメントの数を表示していると思われるのですが,実際のデータベースの情報と一致しないものがあります.(さらにいうと,ブログのドキュメントの数を表示し忘れている--;)

 search/indexlib.phpの中のclass IndexInfoのコンストラクタの中でデータベースから数を調べています.

            foreach($types as $type) {
                $c = count_records(SEARCH_DATABASE_TABLE, 'doctype', $type);
                $this->types[$type] = (int)$c;
            }

doctypeが$typeなレコードの数を取得しています.さて,Lukeで実際のデータベースのレコード数を調べて見ました.

 課題 :264(264)
 チャット :0(なし)
 データベース :15(15)
 フォーラム :1455(2284
 用語集 :568(568)
 ラベル :5(79
 レッスン :5(5)
 リソース :143(143)
 Wiki :1098(1098)
 ブログ: (601)

フォーラムとラベルだけ数が一致しません.

 でも,このcount_records()という関数はLuceneの関数ではなく,Moodleの関数のようですね.ということは,mdl_block_search_documentsを調べた結果ですね.(define('SEARCH_DATABASE_TABLE', 'block_search_documents');)

 Luceneのドキュメント数とMoodleに登録されたドキュメント数に不一致があるということですね.憶測ですが,Luceneにインデックスを登録する際に同時にmdl_block_search_docuemntsにも記録を行う.ところが何らかのミスによりLuceneにしか登録していない.そのような不整合が生じ,それが何らかの悪影響を与えている可能性がありますね.

添付 IndexInfo.jpg
Tatsuya Shirai への返信

Re: インデックスデータベースの状態の表示が少し変?

- Tatsuya Shirai の投稿

 ビンゴですね.mdl_block_search_documentsをdoctype=labelで調べたらレコードは5個しかありませんでした.では,どのタイミングでmdl_block_search_documentsを書き込んでいるのかを調べてみましょう.


search/indexlib.phpのfunction addDocument()でinsert_record()していますね.ところで同じ名前の関数がsearch/Zend/Search?Lucene.phpの中でも定義されています.ただし,class Zend_Search_Luceneのプロシージャとして.間違って呼んでいたりしないだろうかね.でも,ラベルとフォーラムだけおかしいというのも合点がいきませんが.

 

Tatsuya Shirai への返信

Re: インデックスデータベースの状態の表示が少し変?

- Tatsuya Shirai の投稿

たとえばsearch/indexer.php(インデックスをゼロから作成する場合)では,

                            if ($documents){
                                foreach($documents as $document) {
                                    $counter++;
                                   
                                    //object to insert into db
                                    $dbid = $dbcontrol->addDocument($document);
                                   
                                    //synchronise db with index
                                    $document->addField(Zend_Search_Lucene_Field::Keyword('dbid', $dbid));
                                   
                                    //add document to index
                                    $index->addDocument($document);
                                   
                                    //commit every x new documents, and print a status message
                                    if (($counter % 2000) == 0) {
                                        $index->commit();
                                        mtrace(".. $counter");
                                    }
                                }
                            }

こんな感じで登録していますね.多分,最初の青い行がmdl_block_search_documentsへの書き込みで,そこで得られたidを追加して(紐付けして)ピンクの行でLuceneに登録していると思われる.こういう位置関係にある以上,青い行で追加し損ねてピンクの行が成功するとは考え難いですね.$documentがnullでない限りはinsert_record()でmdl_block_search_documentsに何らかのデータを書き込みますから.こちらの方が数が少ないとは考え難い.

 あれ? Lukeではdoctypeがlabelのドキュメントの総数は79と表示されているけれども,6以降が表示できません.'Show All Docs'で一覧表示しても5までですね.どういうことだろう.ラベルの数は5個のわけないのに.context_idが空欄のものもあるし,ちょっと妙ですね.コースidが13のデータしか表示されないのも奇妙です.

Tatsuya Shirai への返信

Re: インデックスデータベースの状態の表示が少し変?

- Tatsuya Shirai の投稿

おや,失礼しました.

インデックスを再作成したら直りました.何がいけなかったのでしょう.

しばらく様子を見てみます.

Tatsuya Shirai への返信

cron.phpが異常終了する現象を確認

- Tatsuya Shirai の投稿

 割り当て可能なメモリを使い切ってcron.phpがsearch関係の処理の途中で異常終了する現象を確認しました.特に何か変更した記憶はありませんが,9月30日に予定されていたバックアップ処理以降,バックアップ処理が行われていないことに気付きました.

 フォーラムなどのモジュールの処理の後にsearch関係の処理があるらしく,ダイジェストーメールや未読チェックなどの処理は無事に終わっていたようです.この問題はグローバルサーチ機能をOnにしているサイトで発生する恐れのある現象です.また,グローバルサーチ機能のデバッグおよび機能の拡張を行ったfs_moodle固有の問題である恐れもあります.

 これがfs_moodle固有の問題か,それとも違うのか.他にもグローバルサーチ機能をOnにしているサイトの方,特にMoodle1.9.5+の最新版(9月30日以降?)を使用しているサイトの方,手入力でmoodle/admin/cron.phpを実行して,最後まで処理が完遂するか調べてご報告いただけ無いでしょうか.

 私はこれから原因解明に取り掛かります.


<!--StartFragment-->Checking forum module for deletions.
  Delete: Your E-mail Address (database id = 511673, index id = 0, moodle instance id = 3743)
  Delete: Your E-mail Address (database id = 511672, index id = 1, moodle instance id = 3743)
  Delete: Your E-mail Address (database id = 511671, index id = 2, moodle instance id = 3743)
  Delete: Your E-mail Address (database id = 511670, index id = 3, moodle instance id = 3743)
  Delete: Your E-mail Address (database id = 511674, index id = 4, moodle instance id = 3743)

が延々と続き,最後は,

<!--StartFragment-->  Delete: Your E-mail Address (database id = 510486, index id = 1090, moodle instance id = 3743)
  Delete: Your E-mail Address (database id = 510491, index id = 1091, moodle instance id = 3743)
<br />
<b>Fatal error</b>:  Allowed memory size of 50331648 bytes exhausted (tried to allocate 44 bytes) in <b>C:\xampplite\htdocs\mech\moodle\search\Zend\Search\Lucene.php</b> on line <b>997</b><br />

このように止まる.メモリ割り当ては過剰にも500MBを割り当てていますが,タスクマネージャで確認していても50MBを超えた気配はありませんでした.

Tatsuya Shirai への返信

Re: cron.phpが異常終了する現象を確認

- Tatsuya Shirai の投稿

 何となくおかしいと思ったとおり,確保されているPHP用のメモリサイズが48MBでした.先の書き込みのログにある数字,50331648を1024*1024で割ると48.つまりいくらphp.iniで大きなメモリを割り当てていても(それが無謀な数字でも^^;),48MBに”縮小”されています.本来は16MB程度に割り当ててあるのを48MBに拡張するつもりだったのでしょう.

 search/cron_php5.phpで,

 try{
    // overrides php limits
    $maxtimelimit = ini_get('max_execution_time');
    ini_set('max_execution_time', 300);
    $maxmemoryamount = ini_get('memory_limit');
//  ini_set('memory_limit', '48M');

この行をコメントアウトすることで,とりあえず最後までcron.phpが動作することを確認しました.このcron_php5.phpですが,たとえば以下のように修正することで,管理者によって望んだ通りの挙動になると期待できます.

<?php
/*
* Moodle global search engine
* This is a special externalized code for cron handling in PHP5.
* Should never be called by a php 4.3.0 implementation.
*/
// (Debug030): ここから追加
function return_bytes($val) {
    if (is_int($val)) return $val;
    $val = trim($val);
    $last = strtolower($val[strlen($val)-1]);
    switch($last) {
        // 'G' は PHP 5.1.0 以降で使用可能です
        case 'g':
            $val *= 1024;
        case 'm':
            $val *= 1024;
        case 'k':
            $val *= 1024;
    }

    return $val;
}
// (Debug030): ここまで追加

try{
    // overrides php limits
    $maxtimelimit = ini_get('max_execution_time');
    ini_set('max_execution_time', 300);
    $maxmemoryamount = ini_get('memory_limit');
// (Debug030): ここからコメントアウト
//
  ini_set('memory_limit', '48M');
// (Debug030): ここから追加
    $temp_maxmemory = '48M';
    if (return_bytes($maxmemoryamount) < return_bytes($temp_maxmemory)) {
        ini_set('memory_limit', $temp_maxmemory);
    }
// (Debug030): ここまで追加

    mtrace("\n--DELETE----");

 要するに,get_ini()で取得したmemory_limitの値が,ソースで指定した'48M'よりも小さい時のみリミットを拡大する.最後にメモリリミットを元に戻すのですが,ここは手を加える必要は無いでしょう.もし48Mよりも大きな値を最初からphp.iniで割り当ててあれば縮小される恐れがなくなります.
 とりあえずこれで解決できた,ということで良いでしょうか.でも,cron.phpが最後まで完了できなかったということを検知するのは不可能ではありません.メールで管理者に通知するなどの機能は欲しいところですね.

#function return_bytes()はPHPマニュアルのサンプルに一行だけ私が追加したものです.このswitch-case文,カッコいいですね.