在Marklogic中,我如何有效地深入比较两个xml文档?

时间:2013-03-04 14:56:40

标签: xml xpath xquery marklogic

我有一个日志记录要求,用于在文档的(中等复杂的)部分在我们的数据库中更改时存储旧值和新值之间的差异。只应报告已更改的数据。我当前的解决方案工作得相当好,但我担心它不是最佳的,并且当更新开始在卷中发生时可能会导致性能问题。

我目前的解决方案看起来大概是这样的:

for $element in $data/section//element()[text()]
return
  if (not($old-data//*[fn:name() = fn:name($element) and text() = $element/text()])) then
    element log:difference {
       ...
    }
  else ()

我的问题是,探查器显示这需要(相对)长时间进行//*[fn:name() = fn:name($element)]构建导致的数千次比较。它只有几十毫秒但是有很多更新会加起来,感觉应该有办法避免它。

xml的结构定义得足够好,我可以确定一个文档中的字段与另一个文档中的字段具有相同的相对xpath,因此从技术上讲,我可以删除//的使用手动遍历xml树的费用,但这是一个合理的复杂性,结构相当平坦,所以我不确定它会更有效率。

此外,文档的这一部分中有一组有限的字段,因此手动比较每个字段(使用完全限定的xpath)将是一个选项,但我宁愿避免它,因为如果字段列表发生变化,最好不要在将来重新访问此代码。

解决方案是否会沿着这些方向发展,还是有一些我错过的更明显的解决方案?

有没有办法直接使用元素名称的字符串值构造xpath而不使用谓词?我认为这样会更有效率,因为xpath评估通常不需要这么长时间。

或许,我可以提取元素的相对xpath然后查看另一个文档中的那个精确位置吗?

我是否错过了marklogic本身的内置xml比较工具?

2 个答案:

答案 0 :(得分:3)

使用fn:name是一个坏主意,因为它可能会受到名称空间前缀差异的欺骗。最好使用fn:node-name。我会尽可能避免'//'。

回到深度比较,这听起来像是XML差异。 MarkLogic中没有内置XML diff工具,因此最好将其设置为REST-ish Web服务,并使用MarkLogic http://docs.marklogic.com/xdmp:http-post来调用它。那里有很多XML diff工具。

如果你想留在XQuery中,解决方案可能会更慢。我将从递归树步行fn:deep-equal开始。每当你找到一个简单元素的差异时,你就可以停止下降,这会修剪树并限制要完成的工作。这是一个非常粗略的草图,说明它可能如何工作。距离正确的LCS http://en.wikipedia.org/wiki/Diff还有很长的路要走,但它可能很有用。在我的笔记本电脑上运行时间不到10毫秒。

declare function local:diff(
  $a as node(), $b as node())
as element(diff)*
{
  if (deep-equal($a, $b)) then ()
  else if (empty($a/*) or empty($b/*)) then element diff {
    element a { $a }, element b { $b } }
  else
    let $seq-a := $a/*
    let $seq-b := $b/*
    let $count := max((count($seq-a), count($seq-b)))
    return
      for $x in 1 to $count
      return local:diff($seq-a[$x], $seq-b[$x])
};

let $a := xdmp:query-meters()
let $_ := xdmp:sleep(1)
let $b := xdmp:query-meters()
return local:diff($a, $b)

答案 1 :(得分:1)

我认为尝试构建索引并对该方法进行基准测试是值得的。

我并不精通marklogic,但他们在their API docs

中拥有我认为是XSL键功能的东西

(更新:这似乎只是获取密钥。为了创建它们,我猜你需要直接使用XSLT。This is a good how-to。一个小样式表生成元素/ @ id <的键/ strong>是可行的。)

您甚至可以将样式表添加为字符串,并节省一点I / O时间:

xdmp:xslt-eval(
  <xsl:stylesheet version="2.0"><xsl:key name="element_ids" match="element" use="@id"></xsl:stylesheet>,
  doc("input.xml")
)

如果每个元素都有一个可用作键的标识符,则可以在解析文件时构建索引,然后将该列表与存储的(早期)版本的键进行比较。从那里,您可以获得要处理的位置列表,并且由于索引,它们可以很快找到并访问。

如果你更愿意坚持使用XQuery,'map' function provides a similar interface.