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, '&#13;&#10;'),
>                                       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, '&#13;&#10;'),
>>                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

Reply via email to