2013-03-04 27 views
1

我有一个日志需求来存储数据库中某个文档(中等复杂)的某个部分发生更改时旧值和新值之间的差异。只有变更的数据应该报告。我当前的解决方案工作得很好,但我担心这不是最优的,并且在更新开始发生时可能会导致性能问题。在Marklogic中,我怎样才能高效地深入比较两个xml文档?

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

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树,但这是一个合理的复杂性和结构相当平坦,所以我不知道它会更有效率。

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

解决方案是沿着这些路线走,还是有更明显的东西我错过了?

有没有什么办法可以直接使用元素名称的字符串值构造xpath而无需使用谓词?我假设这会更有效,因为xpath评估通常不会持续这么长时间。

我可以,也许,提取一个元素的相对xpath,然后看看在另一个文档中的确切位置?

我是否缺少marklogic本身的内置xml比较工具?

+0

为了记录,我发现您可以使用'xdmp:value()'在当前作用域中将任意字符串评估为xquery(包括xpath)。 – 2013-03-07 16:40:54

回答

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

我认为这是值得尝试建立一个索引,并基准该方法。

我不深谙marklogic,但他们有什么我承认作为一个XSL键功能their API docs

(更新:这似乎只取钥匙来创建它们,我会想你”。 ð需要直接使用XSLT This is a good how-to元/ @ ID小样式表生成密钥是可行的)

你甚至可以添加样式作为一个字符串,并节省一点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.

相关问题