Updates
This article summarizes the update features of BaseX.
Updated: Multiple target nodes can now be specified in a single rename
/replace
/insert
expression.
Updates in BaseX are based on the XQuery Update Facility (XQUF). This article aims to provide a quick and basic introduction. First, the basic update primitives and functions are presented. Next, the challenges are addressed that arise due to the functional semantics of the language.
BaseX offers important enhancements to the specification:
- The
update
expression provides a compact and convenient syntax to perform main-memory updates. - Additional Built-In Functions exist to perform database and user updates.
- Update-enabled variants of standard functions are available in the Update Functions module.
- With
MIXUPDATES
, you can combine updating and non-updating expressions in a query. - By default, the targets of the
rename
/replace
/insert
expressions must be single nodes. We have lifted this restriction, meaning that you can specify multiple nodes as targets without having to resort to additional FLWOR expressions.
Expressions
An update expression consists of one or more target nodes (the nodes we want to alter) and (depending on the expression type) additional information like nodes to be inserted, a QName, etc., and optional modifiers. You can find a few examples and additional information below.
Insertions
The insert
expression enables you to add new nodes to existing nodes. Several modifiers are available to specify the exact insert location:
- into: The default: Nodes are added as last child of the target node.
- as first into/as last into: Nodes are added as first or last child of the target node.
- before/after: Nodes are inserted as siblings of the target node
The following query inserts <phone/>
and <mail/>
nodes into all name
elements of a document or database:
insert nodes (<phone/>, <mail/>) into //name
Enumerated ID attributes are added to the name
elements of a resource:
for $name at $id in //name
return insert node attribute { 'id' } { $id } into $name
Deletions
With the delete
expression, nodes can be removed from a resource.
The following query deletes all attributes and comments from a resource:
delete nodes (.//attribute(), .//comment())
Replacements
With the replace
expression, existing nodes can be replaced by other nodes. The source and target nodes can be of different type.
This query replaces all note
elements with empty <hint/>
elements:
replace node //note with <hint/>
With the extension value of
, you can replace the value of an attribute or the descendants of an element with a new string value (i.e., a single text node). If the string value is empty, no text node is generated.
This query deletes all descendants of note
and replaces them with the supplied placeholder
text:
replace value of nodes //note with 'placeholder'
Renames
The rename
expression allows you to change the name of elements, attributes and processing instructions. Nodes on the descendant
or attribute
axis of the target are not affected.
This query renames all village
elements to town
:
rename nodes //village as 'town'
Main-Memory Updates
The following expressions create copies of nodes that are modified using the updating expressions already presented.
update
The update
expression is a BaseX-specific convenience operator for the bulky copy/modify/return
construct. Similar to the XQuery 3.0 map operator, the nodes resulting from the first expression are copied and bound to the context item one at a time. With the expressions in curly braces, the nodes are modifies, and the updated node is returned as result.
The following query iterates over the items of a database and adds ID attributes to copied instances of the item
elements:
for $item at $id in db:get('data')//item
return $item update {
insert node attribute id { $id } into .
}
If multiple nodes are supplied as input, the updates will subsequently be performed on each node:
db:get('data')//item update {
delete node text()
}
It is easy to chain subsequent update expressions:
<root/> update {
insert node <child/> into .
} update {
insert node "text" into child
}
copy/modify/return
copy $c := doc('example.xml')//originalNode
modify rename node $c as 'copyOfNode'
return $c
A copy of the originalNode
element is created, renamed and returned; the original document will not be updated.
In the following example, multiple update operations are performed on the copied node:
Query:
copy $c :=
<entry>
<title>Transform expression example</title>
<author>BaseX Team</author>
</entry>
modify (
replace value of node $c/author with 'BaseX',
replace value of node $c/title with concat('Copy of: ', $c/title),
insert node <author>Joey</author> into $c
)
return $c
Result:
<entry>
<title>Copy of: Transform expression example</title>
<author>BaseX</author>
<author>Joey</author>
</entry>
Instead of the main-memory <entry>
element, a database node can be supplied:
copy $c := (db:get('example')//entry)[1]
...
In this case, the database node remains untouched, as all updates are performed on the node copy.
Entire documents can be copied and modified:
copy $doc := doc("zaokeng.kml")
modify (
for $point in $doc//*:Point
return insert node (
<extrude>1</extrude>,
<altitudeMode>relativeToGround</altitudeMode>
) before $point/*:coordinates
)
return $doc
transform with
The transform with
expression was added to the current XQuery Update 3.0 working draft. It is a simplified version of the update
expression (it is limited to single input nodes and cannot be chained):
<xml>text</xml> transform with {
replace value of node . with 'new-text'
}
Functions
Built-In Functions
The following custom updating functions are available:
- With the Database Functions, you can perform updates on databases and database resources.
- The User Functions offer a thread-safe way to add, modify and delete users.
XQUF itself provides a single function fn:put()
for serializing nodes to secondary storage:
fn:put
Signature | fn:put( $node as node(), $uri as xs:string? := (), $params as (element(output:serialization-parameters) | map(*))? := () ) as empty-sequence() |
---|---|
Summary | Serializes and writes $node to the specified $uri . The $options argument contains serialization parameters (see fn:serialize ). Further notes:
If you want to write intermediate results to files, it is more flexible to use |
Examples | Creates a node.xml file with the serialized representation of <node/> . |
User-Defined Functions
Functions that performs updates need to be marked with an %updating
annotation:
declare %updating function local:add($target, $node) {
insert node $node into $target
};
<node/> update {
local:add(., <sub/>)
}
If update operations are defined in an anonymous function, it may be necessary to call the function with an additional updating
keyword:
let $add := %updating fn($target, $node) {
insert node $node into $target
}
return <node/> update {
updating $add(., <sub/>)
}
Concepts
In addition to the simple expression, XQUF introduced updating expressions:
- All existing expressions are simple expressions. If such an expression is evaluated, the result is a sequence of items.
- Updating expressions, which are presented in this article, result in a list of update primitives that are added to the Pending Update List.
Pending Update List
Updating statements are not executed immediately, but are first collected as update primitives within a set-like structure, the so-called Pending Update List (PUL). After the evaluation of the query, and after some consistency checks and optimizations, the update primitives will be applied in the following order:
- Backups, Binary resources:
db:alter-backup
,db:create-backup
,db:put-value
,db:put-binary
- XQuery Update:
insert before
,delete
,replace
,rename
,replace value
,insert attribute
,insert into first
,insert into
,insert into last
,insert
,insert after
,fn:put
- Documents:
db:add
,db:put
,db:rename
,db:delete
,db:optimize
,db:flush
, - Users:
user:grant
,user:password
,user:drop
,user:alter
,user:create
- Databases:
db:copy
,db:drop
,db:alter
,db:create
- Backups:
db:restore
,db:drop-backup
If an inconsistency is found, an error message is returned and all accessed databases remain untouched (ensuring atomicity). For the user, this means that updates are only visible after the end of a snapshot.
It may be surprising to see db:create
in the lower part of this list. This means that a newly created database cannot be accessed by the same query, which can be explained by the semantics of updating queries: all expressions can only be evaluated on databases that already exist while the query is evaluated. As a consequence, db:create
is mainly useful in the context of Command Scripts, or Web Applications, in which a redirect to another page can be triggered after having created a database.
For example, the query…
insert node <b/> into /doc,
/doc/* ! (rename node . as 'renamed')
…applied on the document…
<doc> <a/> </doc>
…results in the following document:
<doc> <renamed/><b/> </doc>
Despite explicitly renaming all child nodes of <doc/>
, the former <a/>
element is the only one to be renamed. The <b/>
element is inserted within the same snapshot and is therefore not yet visible to the user.
Returning Results
By default, it is not possible to mix different types of expressions in a query result. The root expression of a query must be a sequence of updating expressions. But there are two ways out:
- The BaseX-specific
update:output
function bridges this gap: it caches the results of its arguments at runtime and returns them after all updates have been processed. The following example performs an update and returns a success message:
insert node <notes/> into doc('factbook')/mondial,
update:output("Update successful.")
- With
MIXUPDATES
, all updating constraints will be turned off. Returned nodes will be copied before they are modified by updating expressions. An error is raised if items are returned within a transform expression.
If you want to modify nodes in main memory, you can use main-memory updates.
Effects
Original Files
In BaseX, all updates are performed on database nodes or in main memory. By default, update operations do not affect the original input file (the info string “Updates are not written back” is output to indicate this). The following solutions exist to write XML documents and binary resources to disk:
- Updates on main-memory instances of files that have been retrieved via
fn:doc
orfn:collection
will be propagated back to disk ifWRITEBACK
is turned on. This option can also be activated on command line via-u
. Make sure you back up the original documents before running your queries. - Functions like
fn:put
orfile:write
can be used to write single XML documents to disk. Withfile:write-binary
, you can write binary resources. - The
EXPORT
command can be used write all resources of a databases to disk.
Indexes
Index structures are discarded after update operations when UPDINDEX
is turned off (which is the default). More details are found in the article on Indexes.
Error Messages
Along with the Update Facility, a number of new error codes and messages have been added to the specification and BaseX. All errors are listed in the XQuery Errors overview.
Please remember that the collected updates will be executed after the query evaluation. All logical errors will be raised before the updates are actually executed.
Changelog
Version 11.0- Added: Support for multiple target nodes in a single
rename
/replace
/insert
expression.
- Updated:
db:put-binary
is executed before XQuery Update expressions. - Updated: update: Curly braces are now mandatory.
- Updated: Built-in Functions: serialization parameters
- Added: transform with
- Updated: update was extended.
- Added:
MIXUPDATES
option for Returning Results in updating expressions - Added: information message if files are not written back
- Added: update convenience operator