0

This is the first time I've run into the Xquery (3.1) error Content for update is empty and a search on Google returns nothing useful.

If I run this simple query to identify nested /tei:p/tei:p:

for $x in $mycollection//tei:p/tei:p
return $x

I get XML fragments like the following:

<p xmlns="http://www.tei-c.org/ns/1.0"/>
<p xmlns="http://www.tei-c.org/ns/1.0">Histoires qui sont maintenant du passé (Konjaku monogatari shū). Traduction, introduction et
commentaires de Bernard Frank, Paris, Gallimard/UNESCO, 1987 [1re éd. 1968] (Connaissance de
l'Orient, Série japonaise, 17), p. 323. </p>
<p xmlns="http://www.tei-c.org/ns/1.0">Ed. Chavannes, Cinq cents contes et apologues extraits du Tripitaka chinois, Paris, t. 4,
1934, Notes complémentaires..., p. 147.</p>
<p xmlns="http://www.tei-c.org/ns/1.0"/>
<p xmlns="http://www.tei-c.org/ns/1.0">Ed. Chavannes, Cinq cents contes et apologues extraits du Tripitaka chinois, Paris, t. 4,
1934, Notes complémentaires..., p. 129.</p>

i.e. some with text() and others empty

I am trying to "de-duplicate" the /tei:p/tei:p, but the following attempts return the same aforementioned error:

for $x in $mycollection//tei:p/tei:p
return update replace $x with $x/(text()|*)


for $x in $mycollection//tei:p/tei:p
let $y := $x/(text()|*)
return update replace $x with $y

I don't understand what the error is trying to tell me in order to correct the query.

Many, many thanks.

edit:

for $x in $mycollection//tei:p[tei:p and count(node()) eq 1]
let $y := $x/tei:p
return update replace $x with $y

I also tried this, replacing parent with self axis, which resulted in a very ambiguous error exerr:ERROR node not found:

for $x in $mycollection//tei:p/tei:p
let $y := $x/self::*
return update replace $x/parent::* with $y

solution:

for $x in $local:COLLECTIONS//tei:p/tei:p
return if ($x/(text()|*))
        then update replace $x with $x/(text()|*)
        else update delete $x
jbrehr
  • 775
  • 6
  • 19
  • Are you aware that the expression `for $x in EXPR return $x` is precisely equivalent to the expression `EXPR`? – Michael Kay Sep 05 '19 at 06:46
  • Yes indeed, I posted it like that because I had where statements in the `for` in my original code and stripped them out for debugging... – jbrehr Sep 05 '19 at 10:16

1 Answers1

1

The error message indicates that $y is an empty sequence. The XQuery Update documentation describes the replace statement as follows:

update replace expr with exprSingle

Replaces the nodes returned by expr with the nodes in exprSingle. expr must evaluate to a single element, attribute, or text node. If it is an element, exprSingle must contain a single element node...

In certain cases, as shown in your sample data above, $y would return an empty sequence - which would violate the rule that expr must evaluate to a single element.

To work around such cases, you can add a conditional expression, with an else clause of either an empty sequence () or a delete statement:

if ($y instance of element()) then 
    update replace $x with $y
else 
    update delete $x

If your goal is not simply to workaround the error, but to arrive at a more direct solution for replacing "double-nested" elements such as:

<p><p>Mixed <hi>content</hi>.</p></p>

.... with:

<p>Mixed <hi>content</hi>.</p>

... I'd suggest this query, which takes care not to inadvertently delete nodes that might somehow have slipped in between the two nested <p> elements:

xquery version "3.1";

declare namespace tei="http://www.tei-c.org/ns/1.0";

for $x in $mycollection//tei:p[tei:p and count(node()) eq 1]
let $y := $x/tei:p
return
    update replace $x with $y

Given a $mycollection such as this:

<text xmlns="http://www.tei-c.org/ns/1.0">
    <p>Hello</p>
    <p><p>Hello there</p></p>
    <p>Hello <p>there</p></p>
</text>

The query will transform the collection to be as follows:

<text xmlns="http://www.tei-c.org/ns/1.0">
    <p>Hello</p>
    <p>Hello there</p>
    <p>Hello <p>there</p></p>
</text>

This is the expected result for the query, because only the 2nd <p> element had the nested <p> that could be cleanly stripped off. Obviously, if you can assume your content meets simpler patterns, you can remove the and count(node()) eq 1 condition.

Joe Wicentowski
  • 5,159
  • 16
  • 26
  • I did the second suggestion as an update query and it had no effect. I've posted the exact query in an edit to my post. I thought that `(text()|*)` retrieved any possible nodes following `/tei:p/tei:p` – jbrehr Sep 04 '19 at 20:36
  • I see your post was edited but I'm not sure how to integrate the conditional expression. To clarify: there are no nodes between any nested `/tei:p/tei:p`. There may be text + node following `/tei:p/tei:p` ...or it may be empty. This is why I used `(text()|*)`. – jbrehr Sep 04 '19 at 20:42
  • 1
    A unfortunate shortcoming of your question is that it is not fully self-contained. It refers to data that is not present. Therefore it is difficult for people trying to answer to anticipate all issues. I suggest adjusting your approach. – Joe Wicentowski Sep 04 '19 at 21:09
  • I've posted a solution that worked. Your `if...then...else` idea prompted me to think differently: replace or delete instead of just replace. Hopefully these responses will be useful to those who might search later for the error. – jbrehr Sep 04 '19 at 21:19
  • 1
    The `delete` had occurred to me too - but I didn't mention it before, since it wasn't clear from `text()|*` that you wanted to delete the empty `

    ` elements. I now see that's what you were after when you proposed the `self::*` variant. Overall, I'd suggest next time showing a minimal sample of source data, both (a) before and (b) after the transformation, + (c) what you are trying to achieve and (d) what you are actually seeing. This question required some mind reading! Take a little more time to phrase the question so someone without your data can see enough of the picture to help you.

    – Joe Wicentowski Sep 04 '19 at 21:31