外部インデックスと検索

はじめに

リリース 1.8.3 以降、Doxygen は外部インデックスツールと検索エンジンを使用して HTML を検索する機能を提供します。これにはいくつかの利点があります。

  • 大規模プロジェクトの場合、Doxygen は比較的単純なインデックス作成アルゴリズムを使用しているため、Doxygen の組み込み検索エンジンよりも大幅なパフォーマンス上の利点があります。
  • 複数のプロジェクトの検索データを 1 つのインデックスに結合できるため、複数の Doxygen プロジェクトを横断したグローバル検索が可能になります。
  • 検索インデックスに、Doxygen で生成されていない他の Web ページなどの追加データを追加できます。
  • 検索エンジンは Web サーバー上で実行する必要がありますが、クライアントはローカルで Web ページを閲覧できます。

誰もが独自のインデクサーと検索エンジンの作成を開始する必要がないように、Doxygen は各アクションのサンプルツールを提供しています。データインデックス作成用の doxyindexer と、インデックス検索用の doxysearch.cgi です。

データフローを次の図に示します。

外部検索データフロー
  • doxygen は生の検索データを生成します。
  • doxyindexer はデータを検索データベース doxysearch.db にインデックス化します。
  • ユーザーが Doxygen で生成された HTML ページから検索を実行すると、CGI バイナリ doxysearch.cgi が呼び出されます。
  • doxysearch.cgi ツールはデータベースに対してクエリを実行し、結果を返します。
  • ブラウザは検索結果を表示します。

設定

最初のステップは、Web サーバーを介して検索エンジンを利用できるようにすることです。 doxysearch.cgi を使用する場合は、CGI バイナリを Web サーバーから利用できるようにすることを意味します (つまり、http: で始まる URL を介してブラウザから実行できるようにします)。

Web サーバーのセットアップ方法は、このドキュメントの範囲外ですが、たとえば Apache がインストールされている場合は、Doxygen の bin ディレクトリから Apache Web サーバーの cgi-bin ディレクトリに doxysearch.cgi ファイルをコピーするだけで済みます。詳細については、Apache のドキュメントをお読みください。

doxysearch.cgi にアクセスできるかどうかをテストするには、Web ブラウザを起動して、バイナリへの URL を指定し、最後に ?test を追加します。

http://yoursite.com/path/to/cgi/doxysearch.cgi?test

次のメッセージが表示されるはずです。

Test failed: cannot find search index doxysearch.db

Internet Explorer を使用している場合は、ファイルのダウンロードを求められる場合があります。そのファイルにはこのメッセージが含まれます。

doxysearch.db を作成またはインストールしていないため、この理由でテストが失敗しても問題ありません。この修正方法については、次のセクションで説明します。

次のセクションに進む前に、上記の URL (?test 部分なし) を Doxygen の設定ファイルの SEARCHENGINE_URL タグに追加してください。

SEARCHENGINE_URL = http://yoursite.com/path/to/cgi/doxysearch.cgi

単一プロジェクトのインデックス

外部検索オプションを使用するには、Doxygen の設定ファイルで次のオプションが有効になっていることを確認してください。

SEARCHENGINE           = YES
SERVER_BASED_SEARCH    = YES
EXTERNAL_SEARCH        = YES

これにより、Doxygen は出力ディレクトリ (OUTPUT_DIRECTORY で設定) に searchdata.xml というファイルが生成されます。ファイル名 (と場所) は、SEARCHDATA_FILE オプションで変更できます。

次のステップは、生の検索データを効率的な検索のためのインデックスに入れることです。これには doxyindexer を使用できます。コマンドラインから実行するだけです。

doxyindexer searchdata.xml

これにより、doxysearch.db というディレクトリが作成され、その中にいくつかのファイルが作成されます。デフォルトでは、ディレクトリは doxyindexer が起動された場所で作成されますが、-o オプションを使用してディレクトリを変更できます。

doxysearch.db ディレクトリを doxysearch.cgi が配置されているディレクトリと同じディレクトリにコピーし、ブラウザを次のようにポイントしてブラウザテストを再実行します。

http://yoursite.com/path/to/cgi/doxysearch.cgi?test

次のメッセージが表示されるはずです。

Test successful.

これで、HTML 出力から単語と記号を検索できるようになるはずです。

複数プロジェクトのインデックス

複数の Doxygen プロジェクトがあり、これらのプロジェクトが関連している場合、いずれかのプロジェクトのドキュメント内からすべてのプロジェクトで単語を検索できるようにすることが望ましい場合があります。

これを可能にするために必要なのは、すべてのプロジェクトの検索データを単一のインデックスに結合することだけです。たとえば、project_A および project_B ディレクトリに searchdata.xml が生成される 2 つのプロジェクト A および B の場合、次を実行します。

doxyindexer project_A/searchdata.xml project_B/searchdata.xml

次に、結果の doxysearch.dbdoxysearch.cgi も配置されているディレクトリにコピーします。

searchdata.xml ファイルには絶対パスまたはリンクが含まれていないため、複数のプロジェクトからの検索結果を正しいドキュメントセットにリンクバックするにはどうすればよいでしょうか? ここで、EXTERNAL_SEARCH_ID および EXTRA_SEARCH_MAPPINGS オプションが役立ちます。

異なるプロジェクトを識別できるようにするには、各プロジェクトに EXTERNAL_SEARCH_ID を使用して一意の ID を設定する必要があります。

検索結果を正しいプロジェクトにリンクするには、EXTRA_SEARCH_MAPPINGS タグを使用してプロジェクトごとにマッピングを定義する必要があります。このオプションを使用すると、他のプロジェクトの ID からそれらのプロジェクトのドキュメントの (相対) 場所へのマッピングを定義できます。

したがって、プロジェクト A とプロジェクト B の場合、設定ファイルの関連部分は次のようになります。

project_A/Doxyfile
------------------
EXTERNAL_SEARCH_ID    = A
EXTRA_SEARCH_MAPPINGS = B=../../project_B/html

プロジェクト A の場合とプロジェクト B の場合

project_B/Doxyfile
------------------
EXTERNAL_SEARCH_ID    = B
EXTRA_SEARCH_MAPPINGS = A=../../project_A/html

これらの設定により、プロジェクト A とプロジェクト B は同じ検索データベースを共有でき、検索結果は正しいドキュメントセットにリンクされます。

インデックスの更新

ソースコードを変更した場合は、最新のドキュメントを再度取得するために doxygen を再実行する必要があります。外部検索を使用する場合は、doxyindexer を再実行して検索インデックスも更新する必要があります。このプロセスを簡単にするために、doxygendoxyindexer の呼び出しをスクリプトにまとめてラップすることができます。

プログラミングインターフェース

前のセクションでは、インデックス作成と検索にツール doxyindexerdoxysearch.cgi を使用することを前提としていましたが、必要に応じて独自のインデックスツールと検索ツールを作成することもできます。

このためには、3 つのインターフェースが重要です。

  • インデックスツールへの入力のフォーマット。
  • 検索エンジンへの入力のフォーマット。
  • 検索エンジンの出力のフォーマット。

次のサブセクションでは、これらのインターフェースについて詳しく説明します。

インデクサー入力フォーマット

Doxygen によって生成される検索データは、Solr XML インデックスメッセージ フォーマットに従います。

インデクサーへの入力は XML ファイルであり、複数の <field> タグを含む複数の <doc> タグを含む 1 つの <add> タグで構成されています。

メソッドの検索データとメタデータを含む 1 つのドキュメントノードの例を次に示します。

<add>
  ...
  <doc>
    <field name="type">function</field>
    <field name="name">QXmlReader::setDTDHandler</field>
    <field name="args">(QXmlDTDHandler *handler)=0</field>
    <field name="tag">qtools.tag</field>
    <field name="url">de/df6/class_q_xml_reader.html#a0b24b1fe26a4c32a8032d68ee14d5dba</field>
    <field name="keywords">setDTDHandler QXmlReader::setDTDHandler QXmlReader</field>
    <field name="text">Sets the DTD handler to handler DTDHandler()</field>
  </doc>
  ...
</add>

各フィールドには名前があります。次のフィールド名がサポートされています。

  • type: 検索エントリのタイプ。source、function、slot、signal、variable、typedef、enum、enumvalue、property、event、related、friend、define、file、namespace、concept、group、package、page、dir、module、constants、library、type、union、interface、protocol category、exception、class、struct、service、singleton のいずれかになります。
  • name: 検索エントリの名前。メソッドの場合、メソッドの修飾名、クラスの場合、クラスの名前などです。
  • args: パラメータリスト (関数またはメソッドの場合)
  • tag: このプロジェクトに使用されるタグファイルの名前。
  • url: このエントリの HTML ドキュメントへの (相対) URL。
  • keywords: エントリを代表する重要な単語。このようなキーワードを検索すると、このエントリは検索結果でより高いランクを取得する必要があります。
  • text: アイテムに関連付けられたドキュメント。単語のみが存在し、マークアップは存在しないことに注意してください。
注意
XML ファイルのサイズが大きくなる可能性があるため、SAX ベースのパーサーを使用して処理することをお勧めします。

検索 URL フォーマット

検索エンジンが Doxygen で生成された HTML ページから呼び出されると、いくつかのパラメータが クエリ文字列 を介して渡されます。

次のフィールドが渡されます。

  • q: ユーザーが入力したクエリテキスト
  • n: 要求された検索結果の数。
  • p: 結果を返す検索ページの数。各ページには n 個の値があります。
  • cb: コールバック関数の名前。パディング付き JSON に使用されます。次のセクションを参照してください。

検索結果の完全なリストから、範囲 [n*p - n*(p+1)-1] を返す必要があります。

クエリがどのように見えるかの例を次に示します。

http://yoursite.com/path/to/cgi/doxysearch.cgi?q=list&n=20&p=1&cb=dummy

これは、単語 'list' (q=list) のクエリを表し、20 件の検索結果 (n=20) を要求し、結果番号 20 (p=1) から開始し、コールバック 'dummy' (cb=dummy) を使用します。

注意
値は URL エンコード されているため、使用する前にデコードする必要があります。

検索結果フォーマット

前のサブセクションで示したように検索エンジンを呼び出すと、結果で応答する必要があります。応答のフォーマットは パディング付き JSON であり、基本的には関数呼び出しでラップされた JavaScript 構造体です。関数の名前は、コールバックの名前である必要があります (クエリの cb フィールドで渡されます)。

前のサブセクションで示した例のクエリでは、応答のメイン構造は次のようになります。

dummy({
  "hits":179,
  "first":20,
  "count":20,
  "page":1,
  "pages":9,
  "query": "list",
  "items":[
  ...
 ]})

フィールドには次の意味があります。

  • hits: 検索結果の総数 (要求された数よりも多い可能性があります)。
  • first: 返された最初の結果のインデックス: $\min(n*p,\mbox{\em hits})$
  • count: 返された結果の実際の数: $\min(n,\mbox{\em hits}-\mbox{\em first})$
  • page: 結果のページ番号: $p$
  • pages: ページの総数: $\left\lceil\frac{\mbox{\em hits}}{n}\right\rceil$
  • items: 結果ごとの検索データを含む配列。

items 配列の要素がどのように見えるかの例を次に示します。

{"type": "function",
 "name": "QDir::entryInfoList(const QString &nameFilter, int filterSpec=DefaultFilter, int sortSpec=DefaultSort) const",
 "tag": "qtools.tag",
 "url": "d5/d8d/class_q_dir.html#a9439ea6b331957f38dbad981c4d050ef",
 "fragments":[
   "Returns a <span class=\"hl\">list</span> of QFileInfo objects for all files and directories...",
   "... pointer to a QFileInfoList The <span class=\"hl\">list</span> is owned by the QDir object...",
   "... to keep the entries of the <span class=\"hl\">list</span> after a subsequent call to this..."
 ]
},

このようなアイテムのフィールドには、次の意味があります。

  • type: 生の検索データの "type" という名前のフィールドにあるアイテムのタイプ。
  • name: 生の検索データの "name" および "args" という名前のフィールドにあるアイテムの名前 (パラメータリストを含む)。
  • tag: 生の検索データの "tag" という名前のフィールドにあるタグファイルの名前。
  • url: 生の検索データの "url" という名前のフィールドにあるドキュメントへの (相対) URL の名前。
  • "fragments": 検索された単語を含むテキストの 0 個以上のフラグメントの配列。これらの単語は、出力で強調表示するために、<span class="hl"> および </span> タグでラップする必要があります。