2013-03-04 3 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 элемента, а затем посмотреть на это точное место в другом документе?

У меня отсутствует встроенный инструмент сравнения xml в самой marklogic?

+0

Для записи я обнаружил, что вы можете оценить произвольную строку как xquery (включая xpath) в текущей области с помощью 'xdmp: value()'. –

ответ

3

Использование fn:name это плохая идея, потому что это может быть одураченным различиями в префиксов. Было бы лучше использовать fn:node-name. Я бы также избегал «//», где это возможно.

Возвращаясь к глубокому сравнению, это звучит как разность XML. В MarkLogic отсутствует инструмент XML diff, поэтому лучше всего настроить его как веб-службу REST-ish и использовать MarkLogic http://docs.marklogic.com/xdmp:http-post для его вызова. Существует немало инструментов XML diff.

Если вы хотите остановиться в XQuery, значит, решение будет медленным. Я бы начал с рекурсивной древовидной прогулки и fn:deep-equal. Всякий раз, когда вы обнаруживаете diff для простого элемента, вы можете остановить нисходящий, который вырезает дерево и ограничивает выполняемую работу. Вот очень грубый эскиз того, как это может работать. Это далеко от правильного 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

(Update: это, кажется, только принести ключи, чтобы создать их, я думаю, вам. d нужно использовать XSLT непосредственно This is a good how-to Небольшая таблица стилей генерации ключей на элемент/@ идентификатор будет возможно)

Можно даже добавить таблицу стилей в виде строки, и сэкономить немного времени ввода/вывода:...

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.

Смежные вопросы