1

I have the following XQuery code in the BaseX editor, but it errors on the if-clause, stating

Incomplete FLWOR expression: expecting 'return'

The code that I am trying to use:

import module namespace functx = 'http://www.functx.com';
declare namespace file = "http://expath.org/ns/file";

let $root := 'E:\basex-index-testing\sonar-small\index'
let $ds := file:dir-separator()

(: get files in sonar-small database :)
for $f in db:list('sonar-small')
  (: remove *.xml from doc name :)
  let $corpus := substring($f, 1, string-length($f) - 4)
  for $alpino in db:open('sonar-small', $f)/treebank/alpino_ds
  (: make sure tree has sentence element :)
  where count($alpino/sentence) > 0
      let $sentenceId := data($alpino/@id)

      for $node in $alpino//node
      (: make sure there are less than 500 descendants, 
      less than 100 and more than 0 children :)
      where count($node//node) < 500 and count($node/node) > 0 
            and count($node/node) < 100
        let $catTop := data($node/@cat)

        (: create indexing pattern based on node's direct children :)
        let $childrenRelCat := ()
        for $child in $node/node
          let $childRel := data($child/@rel)
          (: use children's cat or pt attribute, default to '' :)
          let $childCat := data($child/(@cat, @pt, '')[1])
          (: concatenate childrenRelCat sequence (append to list) :)
          let $childrenRelCat := ($childrenRelCat, 
                                  string-join(($childRel, $childCat), '%'))

        let $bf := string-join(functx:sort($childrenRelCat), '_')
        let $sent := <tree id="{$sentenceId}">{$node}</tree>

        let $dir := concat($root, $ds, $catTop)
        (: this if-clause throws an error: missing return statement, 
        incomplete FWLOR:)
        if (file:exists($dir) and file:is-dir($dir))
          then ()
        else file:create-dir($dir)        

        (: append subtree to pattern-file :)
        file:append($dir || $bf || '-index.xml', $sent)
        (: doesn't have to return anything, but FWLOR demands it... :)
        return $f

It seems that I am missing something crucial in the way how XQuery evaluates expressions or expects them to be ordered. What is wrong in the code above?

(This question is titled almost identically, but the answer provided there does not help as there was another error in the code of that OP.)

Bram Vanroy
  • 27,032
  • 24
  • 137
  • 239
  • The `let` needs to be followed by a `return`. I am not sure what you want to achieve with the whole expression, how does the `return 5` relate to the file creation? – Martin Honnen Apr 15 '18 at 20:55
  • @MartinHonnen The return was an example. I often find myself in places where I don't have anything to return. If the let is followed by a return, the return exits the function though, so how would i ever reach the return? And is there a way to do a pythonic `return None` when you donnt want to return anything? – Bram Vanroy Apr 15 '18 at 21:09
  • Would `let $dir := 'hello/world' return if (file:exists($dir) and file:is-dir($dir)) then () else file:create-dir($dir)` do what you want, e.g. allow you to store the file name and then use it in the `if` expression? I am still not sure what you want to do with the `return 5` (and I haven't checked whether `file:create-dir` returns something). – Martin Honnen Apr 15 '18 at 21:12
  • @MartinHonnen I swapped out the dummy code for my actual code. I added comments to explain a little bit what's going on. Hopefully this is clearer? – Bram Vanroy Apr 15 '18 at 22:33
  • Now the last line has `return $file` although that variable has never been declared and been bound to a value. In general the problem is that `let` always has to be part of a https://www.w3.org/TR/xquery-31/#doc-xquery31-FLWORExpr with a `return` and an `if` expression can not be an initial or intermediate clause of such a FLOWRExpr, it can only be into the `return` clause or used inside of a `let` (e.g. `let $foo := if (...) then ... else ...`) or `for` (e.g. `for $bar in (if (...) then ... else ...)`). – Martin Honnen Apr 16 '18 at 07:40
  • @MartinHonnen Oops, my bad. Edited to `$f`. Isn't that an odd construct? I'm not used to XQuery and it's very confusing to me. But forcing one to use variable declarations must not be very memory-efficient, right? How would you rewrite my if-statement? I don't see any sensible way to incorporate the statement inside the return or a let-declaration. – Bram Vanroy Apr 16 '18 at 07:52
  • I was just trying to explain with those examples where an `if` expression can appear and where not and what are the rules for building a FLOWR expression. As for rewriting the code in an allowed way, one way would be `let $dir := concat($root, $ds, $catTop) return (if (file:exists($dir) and file:is-dir($dir)) then () else file:create-dir($dir), file:append($dir || $bf || '-index.xml', $sent))` I think. – Martin Honnen Apr 16 '18 at 08:11

1 Answers1

2

I think the real problem here is that you are using the if-expression

if (file:exists($dir) and file:is-dir($dir))
          then () else file:create-dir($dir) 

in order to achieve side-effects, rather than in order to return a result. Although it's possible for external functions like file:create-dir() to have side effects, it's not really the way XQuery is designed to work, and you therefore need to be very careful about how you use such external functions. Not only that, the detailed rules about what works and what doesn't might vary from one XQuery processor to another.

In addition, of course, your query has to satisfy the grammar, and the grammar for a FLWOR expression says that it consists of a sequence of clauses each of which is a for, let, where, order-by, .... or return clause. So your "if" expression is misplaced.

I don't know BaseX, but I think it's likely that the following will work:

let $dir := concat($root, $ds, $catTop)
return (
        if (file:exists($dir) and file:is-dir($dir))
          then ()
          else file:create-dir($dir),        
        file:append($dir || $bf || '-index.xml', $sent),
        $f
)  
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • 1
    I can confirm that the proposed code will work in BaseX. Side-effecting and non-deterministic functions will never be optimized away.In the given query, it might even be sufficient to get rid of the if expression and always call file:create-dir (if the directory exists, nothing will happen; if it’s a file and not a directory, you will get an error anyway). – Christian Grün Apr 17 '18 at 10:09