xquerysaxonxquery-update

Invoking an XQuery Updating function results in the error If any subexpression is updating, then all must be updating


My XML document contains a record of the movement of an object. The XML document consists of a series of observations. Each observation contains the lat/long location of the object, the lat/long of the observing sensor, and a date/time stamp.

<Track-History>
    <Track-ID>XYZ</Track-ID>
    <Observation>
        <Target-Latitude>10.3</Target-Latitude>
        <Target-Longitude>20.8</Target-Longitude>
        <Observer-Latitude>40.0</Observer-Latitude>
        <Observer-Longitude>50.0</Observer-Longitude>
        <DateTime>20230202T071700.00</DateTime>
    </Observation>
    <Observation>
        <Target-Latitude>15.1</Target-Latitude>
        <Target-Longitude>25.2</Target-Longitude>
        <Observer-Latitude>40.0</Observer-Latitude>
        <Observer-Longitude>50.0</Observer-Longitude>
        <DateTime>20230202T071800.00</DateTime>
    </Observation>
</Track-History>

I want to fuzz the locations of the object by rounding the decimal lat and long values. So I created this updating function:

declare updating function f:fuzzPoint($lat as element(), $long as element())
{
    replace node $lat with 
        element {name($lat)} {round(number(data($lat)))},
    replace node $long with 
        element {name($long)} {round(number(data($long)))}
};

When I invoke that function:

{f:fuzzPoint($obs/Target-Latitude, $obs/Target-Longitude)}

I get this error message:

If any subexpression is updating, then all must be updating

Oxygen XML gives a red squiggly line under the function arguments, so apparently the arguments must be updating. Yes? If so, how to make the arguments updating?

Below is my complete XQuery Update program.

declare namespace f = "function";

declare variable $Track-History := doc('Track-History.xml');
declare variable $track-history-points := (for $i in $Track-History//Observation return [$i/Target-Latitude, $i/Target-Longitude]);
declare variable $AOR := (); (: should be a sequence of points, fake it for now :)

declare function f:isInside($points, $polygon) as xs:boolean
{
    true()
};

declare updating function f:fuzzPoint($lat as element(), $long as element())
{
    replace node $lat with 
        element {name($lat)} {round(number(data($lat)))},
    replace node $long with 
        element {name($long)} {round(number(data($long)))}
};

if (f:isInside($track-history-points, $AOR)) then
    for $obs in $Track-History//Observation return
      replace node $obs with
      <Observation>
            {f:fuzzPoint($obs/Target-Latitude, $obs/Target-Longitude)}
            {$obs/Observer-Latitude}
            {$obs/Observer-Longitude}
            {$obs/DateTime}
       </Observation>
else
   replace node $Track-History/* with
      <Track-History/>

Solution

  • Inside an update expression – replace node $obs with ... – it’s not allowed to perform other updates. The code works if you modify f:fuzzPoint such that it returns the new element nodes:

    declare function f:fuzzPoint($lat as element(), $long as element()) {
      element { name($lat) } { round(number(data($lat))) },
      element { name($long) } { round(number(data($long))) }
    };
    

    The updating keyword in the function declaration makes sense if you move the update operation inside the function body:

    declare updating function f:replace($obs as element()) {
      let $lat := $obs/Target-Latitude
      let $long := $obs/Target-Longitude
      return replace node $obs with <Observation>{
        element { name($lat) } { round(number(data($lat))) },
        element { name($long) } { round(number(data($long))) },
        $obs/Observer-Latitude,
        $obs/Observer-Longitude,
        $obs/DateTime
      }</Observation>
    };
    
    if (f:isInside($track-history-points, $AOR)) then
      for $obs in $Track-History//Observation return f:replace($obs)
    else
       replace node $Track-History/* with
          <Track-History/>