BaseX - Недостаточно памяти при использовании включения xml в XQuery

Я пытался запросить базу данных BaseX, которая содержит более 1500000 элементов. Когда я запускаю этот запрос

for $item in collection('coll')//item
    return $item (: returns an xml element :)

он выполняется менее чем за секунду.

Но когда я пытаюсь вернуть результат в xml, я получаю ошибку «Out of main memory».

<xml>{
    for $item in collection('coll')//item
       return $item
}</xml>

Это то, что заставляет меня отказаться от собственного подхода xml db (то же самое происходит с другими базами данных, такими как eXistDB), поэтому, если у кого-то есть какая-либо информация об этой проблеме, это было бы чрезвычайно полезно.

Спасибо


person unicorn    schedule 21.08.2017    source источник


Ответы (2)


В BaseX 9.0 вы можете временно отключить копирование узлов с помощью параметра COPYNODE:

(# db:copynode false #) {
  <xml>{
    for $item in collection('coll')//item
    return $item
  }</xml>
}
person Christian Grün    schedule 20.02.2018
comment
Что временно означает в этом случае? - person unicorn; 13.03.2018
comment
Предполагается, что «временно» означает «в рамках прагмы». - person Christian Grün; 13.03.2018
comment
Если это имеет тот же эффект, что и оригинал, то есть возвращает завернутые узлы, мне интересно, почему это не поведение по умолчанию, поскольку оно не потребляет ресурсы ... - person unicorn; 14.03.2018
comment
Прагма обходит семантику спецификации XQuery, поэтому она была введена как дополнительная функция. Пример: let $x := <x/> return (# db:copynode false #) { <_>{ $x }</_>/x is $x } дает истину; без прагмы он дает false. - person Christian Grün; 14.03.2018

Из-за семантики XQuery все дочерние узлы необходимо скопировать, если они заключены в новый родительский узел. Это демонстрируется следующим запросом, который сравнивает идентичность исходного и скопированного узла. Это даст false:

let $node := <node/>
let $parent := <parent>{ $node }</parent>
return $parent/node is $node

Поскольку копирование миллионов узлов обходится дорого, это неизбежно приводит к ошибке нехватки памяти.

Если вы записываете результаты в файлы, вот практическое решение, позволяющее обойти это ограничение:

(:~ 
 : Writes element to a file, wrapped by a root node.
 : @param  $path      path to file
 : @param  $elements  elements to write
 : @param  $name      name of root node
 :)
declare function local:write-to(
  $path      as xs:string,
  $elements  as element()*,
  $name      as xs:string
) as empty-sequence() {
  file:write-text($path, '<' || $name || '>'),
  file:append($path, $elements),
  file:append-text($path, '</' || $name || '>')
};

local:write-to('result.xml', <result/>, 'root')

Чтобы предвосхитить критику: это явный прием. Например, подход конфликтует с различными параметрами сериализации BaseX, не заданными по умолчанию (результат не будет правильно сформирован, если необходимо вывести объявление XML и т. Д.).

person Christian Grün    schedule 21.08.2017
comment
Никогда не думал, что в этом случае копируются узлы ... Это все объясняет. Я попробую то, что вы предлагаете, и вернусь с комментариями. Большое тебе спасибо! - person unicorn; 21.08.2017