I'm sorry, but that example still has a couple of dependencies. There are
database lookups that won't return anything for anyone but you, undeclared
namespaces, and what looks like template substitution (bad stuff: use external
variables instead).
Here is what I mean by a working test case: there are no external dependencies
at all. Anyone can copy and paste it into cq or qconsole to test it. Note the
use of semicolons to separate statements: each one is a separate XQuery
program, running in its own timestamp or making its own commits. That technique
is rarely seen in production code, but it's handy for test cases.
(: setup :)
xdmp:document-insert("/example.xml", <a/>) ;
(: updates :)
let $seq := for $i in 1 to 3 return <test id="{$i}"/>
for $e in $seq
order by $e/@id descending
return xdmp:node-insert-child(doc("/example.xml")/a, $e)
;
(: check results :)
doc("/example.xml")
=>
<a>
<test id="1"/>
<test id="2"/>
<test id="3"/>
</a>
I think this is similar to what you're encountering, and I think the hurdle is
understanding that XQuery is not a procedural programming language. There is no
explicit guarantee about when things are done inside a FLWOR expression. The
order-by only guarantees the order of the results, and in that sense the
database updates are not results at all.
So in this code the FLWOR on $seq yields results ordered by $e/@id descending -
but the 'return' expression is evaluated first, in the native order of $seq.
Sorting only affects the final sequence, which is an empty sequence anyway. The
sorting happens after the database updates do.
Here's another way to look at this, without any updates at all. It might be
easier to see what is happening.
let $seq := for $i in 1 to 3 return <test id="{$i}"/>
for $e in $seq
order by $e/@id descending
return text { $e/@id, xdmp:elapsed-time() }
=>
3 PT0.000288S
2 PT0.00025S
1 PT0.000199S
The ids are ordered just as we expected. But the elapsed-time values appear to
be reversed. This happens because the return expression was evaluated first for
id=1, then id=2, then id=3. The final results were then sorted by $e/@id
descending, reversing the order. Even though the order-by is in the middle of
the FLWOR expression, it happens last.
Getting the behavior you want is simple enough, though. Explicitly sort the
sequence and bind the result to a variable. Then perform the updates.
xdmp:document-insert("/example.xml", <a/>) ;
let $seq := for $i in 1 to 3 return <test id="{$i}"/>
let $seq-ordered :=
for $e in $seq
order by $e/@id descending
return $e
for $e in $seq-ordered
return xdmp:node-insert-child(doc("/example.xml")/a, $e)
;
doc("/example.xml")
=>
<a>
<test id="3"/>
<test id="2"/>
<test id="1"/>
</a>
A simpler, but more cryptic, form is:
for $e in (
for $e in (
for $i in 1 to 3 return <test id="{$i}"/>)
order by $e/@id descending
return $e
)
return xdmp:node-insert-child(doc("/example.xml")/a, $e)
Some of the parenthesis are unnecessary, but they may make the code easier to
read.
-- Mike
On 24 Sep 2012, at 03:41 , Dominic Beesley wrote:
> Hi Michael,
>
> Thanks for looking, whole script attached.
>
> Is a variable set in an outer loop, so last child of $a/xm:meta should still
> evaluate at run time to the same thing. I suspect what is happening is that
> the updates are not committed in the same order that they are placed in the
> transaction, which is a bit odd.
>
> let $tax := /xa:auth[@id='ae562ce5-6d71-1014-90bf-c1927c3ed365']
> let $tax2 := <x>{
> for $a at $ix in
> $tax/descendant-or-self::xa:auth[xm:meta/xm:field[@type='old-name']]
> return
> <a id="{$a/@id}" name="{ $a/xm:meta/xm:field[@type='name']
> }" index="{ $ix }">
> {
> ( for $mo in $a/xm:meta/xm:field[@type='old-name']
> return
> <old-name>{ normalize-space($mo)
> }</old-name>
> )
> ,
> (
> for $p in $a/ancestor::xa:auth
> return
> <p id="{ $p/@id }" />
> )
> }
> </a>
>
> }</x>
> let $journals-ids := (@@ids@@)
> for $sid in $journals-ids
> return
> ( concat('\n\nSOURCE-ID=', $sid ,'\n'),
> let $auths := (
> (: get abstract linked to concrete - id any :)
> for $a in /xa:auth[@source-id = $sid and (@type='commentary'
> or @type='commentary-doc')]
> return
> (
> $a,
> for $cal in $a//xa:link[ends-with(@link-type,'-CA')
> and @dest-id]
> return
> /xa:auth[@id=$cal/@dest-id]
> )
> )
> for $a in $auths
> return
> (
> (: delete any existing keyword links :)
> (
> for $ex in $a/xm:meta/xm:field[@type='subject-link']
> return
> xdmp:node-delete($ex)
> )
> ,
> concat('AUTH-ID=', $a/@id, '\n'),
> (
> for $sub in $a/xm:meta/xm:field[@type='keyword']
> let $csub := normalize-space($sub)
> let $keys := $tax2//a[old-name = $csub]
> return
> (
> concat('s=', $sub, '\n'),
> if ($keys) then
> for $k in $keys
> return
> concat(' - NEW=',
> string($k/@name), '\n')
> else
> ' - NO MATCH - \n'
> )
> )
> ,
> let $keysall := (
> for $sub in $a/xm:meta/xm:field[@type='keyword']
> let $csub := normalize-space($sub)
> return
> $tax2//a[old-name = $csub]
> )
> let $allpars := (for $k in $keysall return for $p in $k/p
> return $p/@id)
> let $keys := distinct-values(
> (: don't add id if there is already a lower
> level item in the keys set :)
> for $k in $keysall
> return
> if (not(
> index-of($allpars, $k/@id)
> )) then
> $k/@id
> else
> ()
> )
> return
> (
> if ($keys) then
> (
> for $k in (
> for $kk in $keys
> return
> $tax2//a[@id=$kk]
> )
> order by xs:integer($k/@index) descending
> return
> (
> concat(' - ACTUAL=', $k/@name, ' -
> ', $k/@id, ' - ', $k/@index, ' '),
> xdmp:node-insert-child($a/xm:meta,
> <xm:field
> type='subject-link'>
> <xa:link
> link-type='{ concat($a/@type, "-taxonomy-subject-law") }' dest-id='{ $k/@id
> }' />
> </xm:field>
> )
> )
>
> ) else ()
> )
> )
> )
>
> -----Original Message-----
> From: [email protected]
> [mailto:[email protected]] On Behalf Of Michael
> Blakeley
> Sent: 19 September 2012 17:22
> To: MarkLogic Developer Discussion
> Subject: Re: [MarkLogic Dev General] Multiple inserts, maintaining order
>
> The heart of the update expression is something like:
>
> xdmp:node-insert-child($a/xm:meta, $new-node)
>
> Each node-insert-child should add a node to the end of the existing children
> of $a/xm:meta, like a FIFO. But "$a" is undefined, so it's difficult to
> predict how this will evaluate. If you can provide a working test case,
> someone here can probably tell you what is going awry.
>
> -- Mike
>
> On 19 Sep 2012, at 08:42 , Dominic Beesley wrote:
>
>> Hi Damon,
>>
>> Thanks for the suggestion. I think something along those lines is the way
> I'll have to go, just seems a bit odd having to rebuild a whole, quite large
> item rather than being able to poke a few in at the end!
>>
>> Thanks
>>
>> Dom
>>
>> From: [email protected]
>> [mailto:[email protected]] On Behalf OfDamon
>> Feldman
>> Sent: 19 September 2012 14:20
>> To: MarkLogic Developer Discussion
>> Subject: Re: [MarkLogic Dev General] Multiple inserts, maintaining
>> order
>>
>> Dominic,
>>
>> You could instead build the sequence, merge it together and then replace
> the whole meta node. Un-tested code:
>>
>> Let $new-fields := for .. return <xm:field type='subject-link'>
>> <xa:link Let $old-meta := . Let $old-fields := $old-meta//xm:field Let
>> $new-fields := ($old-fields, $new-fields) Let $new-meta :=
>> <xm:meta> {
>> {$old-meta/@*} (: keep all old attributes :)
>> {$old-meta/node()[local-name(.) ne "field"] (: keep all nodes except
> fields :)
>> {$new-fields} (: add merged fields at the bottom :) } </xm:meta>
>>
>> If you need the xm:meta to have the fields in the middle somewhere, that
> can be done, but is trickier.
>>
>> Yours,
>> Damon
>>
>> From: [email protected]
>> [mailto:[email protected]] On Behalf OfDominic
>> Beesley
>> Sent: Wednesday, September 19, 2012 9:08 AM
>> To: 'MarkLogic Developer Discussion'
>> Subject: [MarkLogic Dev General] Multiple inserts, maintaining order
>>
>> Hello,
>>
>> I'm trying to insert multiple nodes into a document, the nodes are getting
> inserted ok but the order is jumbled up.
>>
>> I'm guessing this is something to do with how transactions are committed
> after the script has finished.
>>
>> Is there an easy way round this or a way of building a set of nodes in the
> correct order and inserting them in one go to the parent?
>>
>> Thanks
>>
>> Dom
>>
>> Snippet:
>>
>> for $k in (
>> for $kk in $keys
>> return
>> $tax2//a[@id=$kk]
>> )
>> order by xs:integer($k/@index) descending return (
>> concat(' - ACTUAL=', $k/@name, ' - ', $k/@id, ' - ',
> $k/@index, ' '),
>> xdmp:node-insert-child( $a/xm:meta,
>> <xm:field type='subject-link'>
>> <xa:link link-type='{
> concat($a/@type, "-taxonomy-subject-law") }' dest-id='{ $k/@id }' />
>> </xm:field>
>> )
>> )
>> _______________________________________________
>> General mailing list
>> [email protected]
>> http://developer.marklogic.com/mailman/listinfo/general
>
> _______________________________________________
> General mailing list
> [email protected]
> http://developer.marklogic.com/mailman/listinfo/general
>
> _______________________________________________
> General mailing list
> [email protected]
> http://developer.marklogic.com/mailman/listinfo/general
>
_______________________________________________
General mailing list
[email protected]
http://developer.marklogic.com/mailman/listinfo/general