MarkLogic技术问题:如何将多文档节点保存为单个XML文件
解决MarkLogic Qconsole中保存所有文档节点到单个XML文件的问题
我看你现在的问题是只能保存最后一个<record>节点到文件,核心原因有两个:一是XML要求必须有唯一的根元素,你生成的多个<record>没有被包裹在一个根节点里;二是xdmp:save的调用方式不对,没有正确把所有节点作为整体传入。
咱们一步步来调整你的代码:
1. 替换不安全的xdmp:value节点构造方式
你现在用xdmp:value拼接字符串来生成<meta>节点,这种做法存在注入风险,而且效率不高。直接用XQuery的元素构造语法更安全可靠:
(: 替换原来的local:ifx函数 :) declare function local:ifx($mnem as xs:string, $val as node()?) as node()* { if(fn:exists($val)) then <meta name="{$mnem}" content="{$val/text()}"/> else () }; (: 替换原来的local:forx函数 :) declare function local:forx($mnem as xs:string, $vals as node()*) as node()* { for $val in $vals return <meta name="{$mnem}" content="{$val/text()}"/> };
2. 修正主逻辑的根元素包裹
你的$XML2016是一组<record>节点,需要把它们全部包裹在一个根元素(比如<records>)里,这样才能生成合法的XML文件。
3. 正确调用xdmp:save
xdmp:save的语法是xdmp:save($path as xs:string, $content as node()*),你之前的写法没有把内容传给它,所以只保存了空或者最后一个节点。
修改后的完整代码
(: validate condition and create meta nodes :) declare function local:ifx($mnem as xs:string, $val as node()?) as node()* { if(fn:exists($val)) then <meta name="{$mnem}" content="{$val/text()}"/> else () }; (: Loop and create meta nodes for multiple values :) declare function local:forx($mnem as xs:string, $vals as node()*) as node()* { for $val in $vals return <meta name="{$mnem}" content="{$val/text()}"/> }; declare function local:ETL($nodes as node()*) as node()*{ for $n in $nodes let $v_id := $n/rec:record/meta:Metadata/meta:id let $v_ba := for $elem in $n/rec:record/meta:Metadata/meta:fundingSource return if (fn:string-length($elem/meta:BugetCode/meta:code) = 1) then fn:concat("0", $elem/meta:BugetCode/meta:code) else $elem/meta:BugetCode/meta:code return <record url="dbfeed.iadb" mimetype="text/html" last-modified="NA"> <metadata> {local:ifx("id", $v_id)} {local:forx("ba", $v_ba)} </metadata> </record> }; (: find all documents within a year :) let $docs := cts:search( fn:doc(), cts:and-query(( cts:element-value-query(xs:QName("meta:Collection"), "EDS"), cts:field-range-query("rd", ">=", "2016-01-01"), cts:field-range-query("rd", "<", "2017-01-01") )) ) let $all_records := <records>{local:ETL($docs)}</records> return xdmp:save("/output/all-data-2016.xml", $all_records)
关键修改点说明
- 用直接元素构造替代
xdmp:value:避免了字符串拼接的安全问题,同时XQuery引擎能更好地优化执行。 - 添加
<records>根元素:把所有生成的<record>节点包裹起来,生成合法的XML结构。 - 正确传递内容给
xdmp:save:将包裹后的完整XML节点作为第二个参数传入,这样就能一次性保存所有节点了。
现在你再运行这个查询,就能在/output/all-data-2016.xml里看到所有的<record>节点了。
内容的提问来源于stack exchange,提问作者thichxai
相关产品推荐
相关产品推荐

