非常慢的xpath搜索(ruby / nokogiri)

时间:2013-11-07 23:15:41

标签: ruby xpath nokogiri

我正在使用Nokogiri / Ruby来解析一个非常大的XML文档(~300k行)。处理每条记录大约花了五分钟,我确定下面代码中的最后一行占用了99%的时间。有关如何加快搜索的任何建议?这可能是系统内存(或缺乏系统内存)的问题吗?

doc = Nokogiri::XML(File.read(ARGV[0]))
orders = doc.xpath("//order")

order = orders.xpath("//order[account_number=#{sap_account}]")

2 个答案:

答案 0 :(得分:3)

快速修复

使用root的完整路径而不是//尝试单个XPath。

示例:

order = doc.at("/full/path/to/order[account_number=#{sap_account}]")

//扫描整个文档,因此在尝试提高性能时首先要摆脱它。

如果确实希望加快速度,请使用SAX或Reader接口。

实际速度:阅读器界面

Reader接口(以及SAX)将更快,因为它不必将整个文档解析为DOM;它将简单地一次一个节点线性传递文档。这为您提供了方便牺牲的速度(无需查询和无回溯)。相反,您必须测试每个节点的所需条件。

这是使用Reader接口的示例(比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>

假设您想要使用<item> account_number的顺序提取2。这是代码:

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>

答案 1 :(得分:1)

虽然将节点或节点的搜索分解为步骤通常很有用,但看起来你可以在一个节目中执行此操作:

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,NodeSet是节点的集合。 NodeSets支持许多相同的方法,但它们可能会产生细微差别,因为它们被应用于类似数组的结构而不是单个节点。 at返回第一个匹配的节点,因此您对返回的节点执行的任何进一步处理将仅适用于该节点而不适用于其他节点。

xpathsearch的XPath特定版本,具有匹配的css CSS选择器方法。 search接受CSS和XPath选择器,并确定动态使用哪些。同样,at分别具有at_cssat_xpath的CSS和XPath推论。我倾向于使用searchat并且只使用CSS和XPath变体,当我将XPath误认为CSS导致Nokogiri发疯时。

Nokogiri应该能够快速搜索并找到//order[account_number=#{sap_account}],即使在300K行中也是如此,因为它有足够的内存可供使用。

如果没有,那么请认真考虑将XML导入数据库并在那里进行搜索。 XML并不真正意味着用作数据存储区,因此针对XML文件的处理可能会违背流程并使您的生活更加艰难。创建模式并将其导入带有索引字段的数据库可以大大加快处理速度。