1

I have the following xml:

<?xml version="1.0" encoding="UTF-8"?>
<publication>
    <scientificPublication>
        <id>2347823476</id>
        <book>
            <chapter>
                <author>Tom</author>
                <genre>Java</genre>
                <genre>Webservice</genre>
            </chapter>
        </book>
        <book>
            <chapter>
                <author>Mike</author>
                <genre>C</genre>
                <genre>Machine Learning</genre>
            </chapter>
        </book>
        <book>
            <chapter>
                <author>William</author>
                <genre>JavaScript</genre>
                <genre>Frontend</genre>
            </chapter>
        </book>
    </scientificPublication>
</publication>

I need to filter the xml using XQuery, so in the end, I have the following resoult:

<?xml version="1.0" encoding="UTF-8"?>
<publication>
    <scientificPublication>
        <id>2347823476</id>
        <book>
            <chapter>
                <author>Tom</author>
                <genre>Java</genre>
                <genre>Webservice</genre>
            </chapter>
        </book>
    </scientificPublication>
</publication>

So basically my idea, was to find all the sibling nodes from the first book, and then remove them with the function fn:remove((item,item,...),position). So I found all the nodes that need to be removed with the following xpath:

publication/*/book[not(./*[(genre='Java')and(genre='Webservice')])]

And then I try to remove them from the xml using the code:

declare function local:process-xml($element) {
   let $orig := $element
   let $toFilter := $element/*/book[not(./*[(genre='Java')and(genre='Webservice')])]

   let $el := local:filter($orig, $toFilter) 
   return $el
};

declare function local:filter($elements, $toFilter) {
    for $i in $toFilter
        let $pos := position()
        remove($lements, $pos)
    return $elements
};

When I try to execute this, I get an error saying that he stopped, expecting a 'return' on the second function. Anyone knows what I'm doing wrong in this case?

Alexander Rumanovsk
  • 2,043
  • 4
  • 21
  • 33

2 Answers2

2

Your whole approach does not feel very functional in nature. Removing elements using BaseX is done best using XQuery Update.

So in your case you simply delete all book elements using the following function:

declare function local:process-xml($element) {
  $element update delete node .//book[not(./*[(genre='Java')and(genre='Webservice')])] 
};
dirkk
  • 6,160
  • 5
  • 33
  • 51
  • This will remove the elements from the database. In My case, retrieve the full xml from the database, and then from this point, I have to filter the result, leaving the original unchanged inside the database. – Alexander Rumanovsk Jun 08 '20 at 14:48
  • Nope, it will not change the database at all. It is a non-updating expression, denoted by the "update" keyword. You can find out more about it in the link to the BaseX documentation I provided, – dirkk Jun 08 '20 at 14:53
  • when I run this, I get the error: org.basex.core.BaseXException: Stopped at ., 101/31: [XUDY0014] Node was not created by copy clause: element {...}. – Alexander Rumanovsk Jun 08 '20 at 15:01
  • I find it hard to believe you get this error message when you run the EXACT same function. This error message indicates that you use the variable $element within the update part (instead of the dot). I testes this in BaseX 9.3.3. If you are sure what you are running is the SAME, please give (edit your question) the exact code you execute and the BaseX version you use. – dirkk Jun 08 '20 at 15:25
0

You could use a recursive function that filters out the book elements:

declare function local:filter($node)
{
  typeswitch($node)
    case element(book) return
      if ($node[not(*[(genre='Java') and (genre='Webservice')])])
      then () (: we don't want these books, so return empty sequence :)
      else $node (: this is the book we want :)
    case element() return 
      element { fn:node-name($node) } {
        $node/@*,
        $node/node() ! local:filter(.)
      }
    default return $node
};
Mads Hansen
  • 63,927
  • 12
  • 112
  • 147