我正在使用Nokogiri/Ruby来解析一个非常大的XML文档(~300k行)。处理每条记录大约需要五分钟,我确定下面代码的最后一行占用了 99% 的时间。关于如何加快搜索速度的任何建议?这可能是系统内存(或缺少系统内存)的问题吗?
doc = Nokogiri::XML(File.read(ARGV[0]))
orders = doc.xpath("//order")
order = orders.xpath("//order[account_number=#{sap_account}]")
快速修复
使用根目录的完整路径而不是 //
尝试单个 XPath。
例:
order = doc.at("/full/path/to/order[account_number=#{sap_account}]")
//
扫描整个文档,因此在尝试提高性能时,首先要摆脱它。
如果您真的想加快速度,请使用 SAX 或读取器接口。
实际速度:阅读器界面
Reader 接口(以及 SAX)会更快,因为它不必将整个文档解析为 DOM;它只需一次线性地通过一个节点。这在牺牲便利性(没有查询和回溯)的情况下为您提供了速度。相反,您必须测试每个节点所需的条件。
下面是一个使用读取器接口的示例(它比 SAX 简单一些)。假设您有以下文件:
<orders>
<order account_number="1">
<item>Foo</item>
</order>
<order account_number="2">
<item>Bar</item>
</order>
<order account_number="3">
<item>Baz</item>
</order>
</orders>
假设您想按2
account_number
的顺序拉出<item>
。代码如下:
require 'nokogiri'
filename = ARGV[0]
sap_account = "2"
File.open(filename) do |file|
Nokogiri::XML::Reader.from_io(file).each do |node|
if node.name == 'order' and node.attribute('account_number') == sap_account
puts node.inner_xml
end
end
end
输出:
<item>Bar</item>
搜索一个或多个节点分解为步骤通常很有用,但看起来您确实可以在一个步骤中做到这一点:
doc = Nokogiri::XML(File.read(ARGV[0]))
order = doc.xpath("//order[account_number=#{sap_account}]")
如果该节点只能出现一次,请使用:
order = doc.at("//order[account_number=#{sap_account}]")
不同之处在于xpath
返回一个节点集,它是节点的集合。NodeSet 支持许多相同的方法,但它们可能会导致细微的差异,因为它们被应用于类似数组的结构而不是单个节点。 at
返回第一个匹配的节点,因此对返回的节点执行的任何进一步处理将仅适用于该节点,而不适用于其他节点。
xpath
是 XPath 特定版本的search
,具有匹配的 css
CSS选择器方法。 search
接受CSS和XPath选择器,并确定动态使用哪个。类似地,at
分别具有at_css
和at_xpath
的CSS和XPath推论。我倾向于使用search
和at
,并且只使用CSS和XPath变体,当我的XPath会被误认为CSS导致Nokogiri惊慌失措时。
Nokogiri 应该非常快速地搜索和找到//order[account_number=#{sap_account}]
,即使在 300K 行中,如果它有足够的内存可以玩的话。
如果没有,那么请认真考虑将 XML 导入数据库并在那里进行搜索。XML 并不是真正用作数据存储的,因此针对 XML 文件的处理可能会逆流而上,使您的生活更加困难。创建架构并将其导入到包含索引字段的数据库中可以大大加快处理速度。