内容


XML 问题 #10

对 XML 文档进行索引

由数千页面组成的几兆字节的大型文档在企业和政府界中并不罕见。作家和技术人员例行公事般地创作出以 SGML(标准通用标记语言)格式表示的大量产品说明书、规章需求和计算机系统文档。从技术角度来说,XML 是简化和特殊化的 SGML。在这样一种近似性的前提下,XML 文档也应该是有效的 SGML 文档。

不过,从文化角度来说,XML 是从其它方面进化而来的。一方面,XML 是 EDI 的继任者。另一方面,它也是 HTML 的继任者。因为与 SGML 具有的文化历史背景不同,所以 XML 所经历的是其自身的工具开发过程。它变得越来越普遍,因此应该会看到越来越多的(通常)非正式的 HTML 文档和(通常)正式的 SGML 文档一起朝着 XML 格式的方向迁移 -- 特别是使用例如 DocBook 这样的 XML 方言。

不过,在 XML 自身的文化中,它还没有培养出能够在大型 XML 文档中有效地定位内容的工具。象 UNIX 上的 grep 以及其它平台上类似工具等常规文件搜索工具完全可以读取 XML 文档的纯文本(除了可能存在的 Unicode 问题以外),但简单的 grep搜索(甚至复杂的 grep 搜索)会丢失 XML 文档的结构。

在搜索包含具有数千页面文档的文件内容时,您所知道的内容往往比可以在字、短语或规则表达式中指定的内容多得多。例如,到底那些农业报告中有哪些是 June Apple 小姐写的?象 grep 这样粗糙的工具通常会找到许多没有什么意义的事物。而且,象 grep 这样特别的工具虽然在它们执行任务时非常有效,但它们需要在每次执行搜索时检查大型文件的所有内容。如果是频繁的搜索,那么这种重复地搜索整个文件的方法,其效率是很低的。

扩展 indexer

根据上面所述的需要,我创建了公众域实用程序 xml_indexer 。这个 Python 模块可以用作运行时实用程序,也可以方便地由使用其服务的定制应用程序扩展。模块 xml_indexer 反过来依赖于我在以前 IBM developerWorks 文章中描述过的两个公众域实用程序的服务: indexerxml_objectify (请参阅 参考资料)。

xml_indexer 使用的“技巧”和 XPath 使用的“技巧”完全相同。不是将 XML 文档简单地作为文件系统中的“事物”对待,而是可以假装让 XML 文档中的层次节点看上去非常象是一个层次文件系统。为了索引,不需要一些语法将 XPath 和文件系统路径区分开来,而只是将 XML 节点作为它本身就是个文本文件来对待。幸运的是,我所设计的 indexer 有足够的灵活性,可以在索引文本中使用任意标识。让我们看一些搜索结果。

清单 1. 对 XML 节点执行的索引搜索
[D:\articles] indexer ibm
/articles/tutor/cryptology3.xml::/section[1]/panel[2]/body/text_column/p[1]
/temp/Benchmark/Data/addr2.xml::/person[4]/contact_info/email/@address
/temp/Benchmark/Data/addr2.xml::/person[2]/contact_info/email/@address
/tools/addr2.xml::/person[4]/contact_info/email/@address
/tools/addr2.xml::/person[2]/contact_info/email/@address
5 file matched wordlist: ['ibm']
Processed in 0.320 seconds (SlicedZPickleIndexer)

和使用 XPath 一样, @ 标记位于属性值前,方括号中包含了编号的兄弟节点。到达 XML 文档的文件系统路径在这种上下文中充当 XPath 轴 -- 大体上类似于一个名称空间。为进行比较,让我们对文件数据库执行类似的索引搜索(使用了一些额外的搜索术语以使结果列表保持合理)。

清单 2. 电子邮件消息的索引搜索
[D:\articles] indexer ibm python xml indexer
D:\archive\mail\messages\tenco.cp15.2001-03-06.13+50+35
D:\archive\mail\messages\tenco.cp15.2001-03-01.07+57+26
D:\archive\mail\messages\tenco.cp15.2001-02-28.23+25+26
3 file matched wordlist: ['ibm', 'python', 'xml', 'indexer']
Processed in 2.530 seconds (SlicedZPickleIndexer)

第一个搜索是对相当少量的测试数据执行的,而第二个搜索则对大约 100MB 的归档电子邮件消息(存储在文件系统中,一条消息一个文件)使用“产品”索引。依我看,只花几秒钟的时间搜索 100MB 的文件(搜索多个同时出现的字)已经算得上非常快了。

而且,既然这些搜索利用了不同的索引数据库(因为在 xml_indexer 的测试阶段它们就已完成),没理由不创建文本文件和 XML 节点的复合索引。在这种情况下,甚至有可能(可能往往很有用)将每个 XML 文件 同时作为节点集合和文本文件来索引。这样做之后,搜索结果将显示标识的两种类型,很明显,在每次 XPath 在其名称空间中出现的场合中,文件系统标识都出现。清单 3 提供了一个示例。

清单 3. 电子邮件消息的索引搜索
[D:\articles] indexer actresses
/temp/Benchmark/Data/addr_break.xml
/temp/Benchmark/Data/addr_break.xml::/person[3]/misc_info
2 file matched wordlist: ['actresses']
Processed in 0.070 seconds (SlicedZPickleIndexer)

创建索引

读者会注意到上面的那些示例使用 indexer 执行搜索,而根本没有提到过 xml_indexer 。这是因为我可以使用完全相同的索引搜索工具来搜索由 xml_indexerindexer 所创建的索引数据库。实际上, indexer 只是对 python indexer.py ... 的调用,带有一些以适合于 OS 方式传递的命令行参数。您可以使用 indexer 来创建或增强文本文件索引(运行 ' indexer --help ' 或 ' indexer /? ' 获得所需参数和开关的分类信息)。在向索引添加文件时,可以遍历目录。其它开关允许您将索引限于只添加其名称与模式(regex 或 glob)匹配的文件。

至少目前我可以使用简单一些的 xml_indexer.py 脚本来创建 XML 节点索引数据库。在写作本文的时候,我只能通过将文档名作为命令行参数指定,每次将单个 XML 文档的那些节点添加到索引数据库中。不过,在您读到这篇文章的时候,我可能已经增强了 xml_indexer.py 的命令行语法,使其看上去更类似于 indexer.py 的命令行语法。在使用它之前请先看一下 python xml_indexer.py --help 的输出。

指定 XPaths

为了向搜索结果提供 XPath 通配能力,我将一个 -filter 选项添加到 indexer 中,但在搜索结果中不支持 XPath 功能。这个选项有一个透明而有益的副作用,我可以对文件名“替换”使用同样的开关 -- 只是以防我只对匹配满足某些模式的文件感兴趣。

/filter 选项基本上可以照您希望的那样工作(为多个 shell 的不同引用语法进行了调整)。可以通过在过滤器中使用两个冒号来指定您只想获得 XPath 结果。

清单 4. 只返回 XPath 搜索结果
[D:\articles] indexer "/filter=*::*" actresses
/temp/Benchmark/Data/addr_break.xml::/person[3]/misc_info
1 file matched wordlist: ['actresses']
Processed in 0.050 seconds (SlicedZPickleIndexer)
清单 5. 只返回作为文件的 XML 文档
[D:\articles] indexer "/filter=*.xml" actresses
/temp/Benchmark/Data/addr_break.xml
1 file matched wordlist: ['actresses']
Processed in 0.050 seconds (SlicedZPickleIndexer)

标识为获得更为复杂的 XPath 指示符所需的子元素和顺序。

清单 6. 显示索引中的所有字匹配
[D:\articles] indexer symmetric
/tutor/cryptology1.xml::/section[2]/panel[8]/title
/tutor/cryptology1.xml::/section[2]/panel[8]/body/text_column/code_listing
/tutor/cryptology1.xml::/section[2]/panel[7]/title
/tutor/cryptology1.xml::/section[2]/panel[7]/body/text_column/p[1]
4 file matched wordlist: ['symmetric']
Processed in 0.100 seconds (SlicedZPickleIndexer)
清单 7. 将匹配限制在 title 元素中出现的内容
[D:\articles] indexer "-filter=*::/*/title" symmetric
/tutor/cryptology1.xml::/section[2]/panel[8]/title
/tutor/cryptology1.xml::/section[2]/panel[7]/title
2 file matched wordlist: ['symmetric']
Processed in 0.080 seconds (SlicedZPickleIndexer)

总结

xml_indexer 设计所得到的帮助大部分来自于反映在 indexer 设计中的面向对象原则。推翻 GenericIndexer 类(实际上,在其派生的 SlicedZPickleIndexer 中 -- 但人们可以轻松地将任何具体的 Indexer 类混合进去)中的少许方法,这样就有可能使用全新的一组标识和数据源。

那些希望在自己比较大型 Python 项目中使用 xml_indexer 的读者应该会发现其更深一层的专门化是相当简单的。我期待看到这些基本的索引类会对读者有所帮助。


相关主题

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文.
  • 可以 下载 xml_indexer 模块
  • 可爱的 Python #15: Developing a full-text indexer in Python 包含了有关 indexer 模块的常规背景讨论。
  • 请参阅 indexer 模块 本身。
  • 为了轻松地循环下降遍历 XML 节点,我利用了 xml_objectify 提供的高级 Python 化的接口。不过请注意,这一做法直到最近也不太实际。 xml_objectify 的较早版本使用 DOM 来读取 XML 文件,对于大型 XML 文档,它的处理速度缓慢得让人几乎无法忍受(部分归罪于 xml_objectify 处理该 DOM 所使用的方法)。Costas Malamas 提供了一种替代语法分析方法,这种方法使用 expat 语法分析器和面向流技术。这种新技术对于某些复杂的 XML 文档来说仍然有些短暂的影响,但在大多数情况下都可以正常工作,而且速度快得多。可以联机找到 xml_objectify
  • 通过仔细查看“IBM 认证开发者计划”的 XML 认证指导来提高您的 XML 技能。
  • 参阅 David Mertz 以前的“XML 问题”专栏文章:
    • XML 问题 #1介绍 Python xml_pickle 对象。
    • XML 问题 #2描述如何使用 Python 的 xml_objectify。
    • XML 问题 #3介绍 DocBook。
    • XML 问题 #4继续介绍如何使用 DocBook 构建旧的文档档案。
    • XML 问题 #5说明如何通过 XSLT 将 XML 文档转换成 HTML。
    • XML 问题 #6 比较了半打 XML 编辑器,同时特别着眼于那些适合于有大量文本的文档的工具。
    • XML 问题 #7 将 DTD 与 XML Schema 进行了权衡,并建议无论 W3C XML Schema 规范有多么成熟,开发者在什么情况下需要坚持使用 DTD。
    • XML 话题 #8 讨论了由计算机科学家概念化的数据 模型的抽象理论是如何帮助我们开发一些特殊的多表示的数据流。
    • XML 话题 #9 讨论了公众域 sql2dtd 和 sql2xml 实用程序;这些实用程序可以不依赖 RDBMS 生成可移植 XML 结果集。
static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML
ArticleID=54827
ArticleTitle=XML 问题 #10
publish-date=05012001