Reputation: 23
This code is stopped in its tracks because there is technically no "updating expression" - probably because it is hiding in an eval string and the interpreter doesn't see it.
The examples that I have reviewed that use xquery update facility have a fixed number of update statements, but I'd like to handle a variable number of update statements in an updating function.
I may be way off (please tell me if I am) in how I've approached this solution but I tried to create a string with all the update statements comma delimited and then xquery:eval the string to do all the updates in one return statement.
I think this should work because the string looks like the statements that I would hard code if I knew how many.
e.g.
return (replace value of node quantity with '1',
replace value of node market with 'new_market')
Stopped at C:/Program Files (x86)/BaseX/webapp/product.xqm, 109/7:
[XUST0002] Function body is no an updating expression.
(looks like a typo in the error message btw)
(: example new data values
$req - could be any number of nodes with new values
<request>
<data>
<ID>1</ID>
<quantity>1</quantity>
<market>new_market</market>
</data>
</request>
:)
(: Old data that matches ID and needs replacing
<csv>
<record>
<ID>1</ID>
<quantity>3</quantity>
<market>old_market</market>
</record>
<csv>
:)
declare
%rest:path("updy/{$db}/{$resource}")
%rest:POST("{$req}")
%updating function page:updy($db,$resource,$req as document-node())
{
let $input := concat("db:open('",$db,"','",$resource,"')")
for $node_name in $req/request/data/(* except ID)/name()
let $updating_record := query:eval($input)/csv/record[ID=$req/request/data/ID]
for $updating_fields in $updating_record//*[name()=$node_name]
let $to_value := $req/request/data/*[name()=$updating_fields/name()]/data()
let $eval_string := string-join(concat("replace value of node ",$updating_fields/name()," with '",$to_value,"'")," , ")
return xquery:eval($eval_string))
};
The top is code that I'm trying to get to work from a rest call. The bottom is code that I'm trying to test to see if I can get this to work. This is set to return just the eval string then switch comments there to try and run it.
declare %private updating function local:updy($db,$resource,$req as element())
{
let $input := concat("db:open('",$db,"','",$resource,"')")
for $node_name in $req//data/(* except ID)/name()
let $updating_record := xquery:eval($input)/csv/record[ID=$req//data/ID]
for $updating_fields in $updating_record//*[name()=$node_name]
let $to_value := $req//data/*[name()=$updating_fields/name()]/data()
let $eval_string := string-join(concat("replace value of node ",$updating_fields/name()," with '",$to_value,"'")," , ")
(: return $eval_string :)
return xquery:eval($eval_string)
};
(: example new data values :)
(: $req - could be any number of nodes with new values :)
let $req :=
<request>
<data>
<ID>1</ID>
<quantity>1</quantity>
<market>new_market</market>
</data>
</request>
(: Old data that matches ID and needs replacing :)
let $input :=
<csv>
<record>
<ID>1</ID>
<quantity>3</quantity>
<market>old_market</market>
</record>
</csv>
let $dbadd := db:create("ProductTest", $input, "exportDataTest.csv")
return local:updy("ProductTest","exportDataTest.csv",$req)
I'm adding the finished code below because with the help I received here I was able to get it working properly.
declare
%rest:path("updz/{$db}/{$resource}")
%rest:POST("{$req}")
%updating function page:updz($db,$resource, $req as document-node()) {
let $input := concat("db:open('",$db,"','",$resource,"')")
let $the_id := $req//data/ID
let $update_string :=
for $node_name in $req//data/(* except ID)/name()
let $updating_record := xquery:eval($input)/csv/record[ID=$req//data/ID]
for $updating_fields in $updating_record//*[name()=$node_name]
let $to_value := $req//data/*[name()=$updating_fields/name()]/data()
return string-join(concat("replace value of node ", $input,"/csv/record[ID='",$the_id,"']//",$updating_fields/name()," with '",$to_value,"'")," , ")
let $final_update_string := concat("(",string-join($update_string,","),",update:output(<response><status>1</status></response>))")
(: return $final_update_string :)
return xquery:eval-update($final_update_string)
};
Upvotes: 0
Views: 303
Reputation: 6229
The reason for the error message (with the unexpected typo; thanks for the hint) is that your function that contains the xquery:eval
call is annotated as %updating
. The xquery:eval
function, however, is a non-updating function.
The XQuery Update Recommendation postulates that each expression must be either updating or read-only. As the argument of xquery:eval
can vary during query execution…
for $query in ('123', 'delete node <a/>')
return xquery:eval($query)
…there is a second function called xquery:eval-update
, which can be used for evaluating queries that contain updating expressions. If you know that the incoming queries will be non-updating, simply get rid of the %updating
annotation.
One more comment: If you create a database in a query, you cannot request its contents in the same query (a single XQuery is a single transaction). Please check out the short documentation on the Pending Update List or have a look into the full specification.
Upvotes: 2