外部索引和搜索

简介

从 1.8.3 版本开始,Doxygen 提供了使用外部索引工具和搜索引擎搜索 HTML 的功能。 这有几个优点

  • 对于大型项目,它比 Doxygen 的内置搜索引擎具有显著的性能优势,因为 Doxygen 使用的索引算法相当简单。
  • 它允许将多个项目的搜索数据组合到一个索引中,从而可以跨多个 Doxygen 项目进行全局搜索。
  • 它允许向搜索索引添加其他数据,即其他非 Doxygen 生成的网页。
  • 搜索引擎需要在 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,则可以将 doxysearch.cgi 文件从 Doxygen 的 bin 目录复制到 Apache Web 服务器的 cgi-bin 目录。 有关详细信息,请阅读 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 的两个项目 A 和 B,运行

doxyindexer project_A/searchdata.xml project_B/searchdata.xml

然后将生成的 doxysearch.db 复制到 doxysearch.cgi 所在的目录。

searchdata.xml 文件不包含任何绝对路径或链接,那么如何将多个项目的搜索结果链接回正确的文档集? 这就是 EXTERNAL_SEARCH_IDEXTRA_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 文件,它由一个包含多个 <doc> 标签的 <add> 标签组成,而 <doc> 标签又包含多个 <field> 标签。

下面是一个 doc 节点的示例,其中包含一个方法的搜索数据和元数据

<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>标签中,以便在输出中突出显示。