外部索引和搜索

引言

从版本 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 项目且这些项目相互关联,则可能需要允许从任何项目的文档中搜索所有项目中的单词。

为了实现这一点,所需做的就是将所有项目的搜索数据合并到一个索引中,例如,对于 searchdata.xml 分别生成在 project_A 和 project_B 目录中的项目 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 文件,它包含一个 <add> 标签,该标签包含多个 <doc> 标签,而 <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(JSONP),详见下一节。

从完整的搜索结果列表中,应返回范围 [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 (JSONP),它本质上是将 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> 标签中,以便在输出中突出显示它们。