Conditional predicate

Suggested by a post in the eXist mailing list . The task is to select a subset of items with a conditonal filter.

Setup test data

This is just a snippet of data. More realistically this would be a large document which was indexed.

XQuery

<data>
  <r type="a">
     <activity>A</activity>
  </r>
    <r>
     <activity>X</activity>
  </r>
  <r type="b">
     <activity>B</activity>
  </r>
  <r type="c">
     <activity>C</activity>
  </r>
  <r type="a">
     <activity>AA</activity>
  </r>
  <r type="b">
     <activity>BB</activity>
  </r>
  <r type="c">
     <activity>CC</activity>
  </r>
  <r>
     <activity>Y</activity>
   </r>
</data>

Result

<data>
    <r type="a">
        <activity>A</activity>
    </r>
    <r>
        <activity>X</activity>
    </r>
    <r type="b">
        <activity>B</activity>
    </r>
    <r type="c">
        <activity>C</activity>
    </r>
    <r type="a">
        <activity>AA</activity>
    </r>
    <r type="b">
        <activity>BB</activity>
    </r>
    <r type="c">
        <activity>CC</activity>
    </r>
    <r>
        <activity>Y</activity>
    </r>
</data>

suggested by Roy Walter

XQuery

    let $type := "a"
    let $result := $context//activity
    
    let $activities :=
    if ( $type ne '' ) then
        for $r in $result[../@type=$type]
        order by $r descending
        return
        $r/..
    else
        for $r in $result[../@type]
        order by $r descending
        return
        $r/..
    return $activities
        

Result

<r type="a">
    <activity>AA</activity>
</r>
<r type="a">
    <activity>A</activity>
</r>

suggested by Joe Wickentowski

XQuery

let $type:= "a"
let $result := $context//activity
    
    
let $filtered-results :=
  if ( $type ne '' ) then
      $result[../@type=$type]
  else
      $result[../@type]
let $activities :=
    for $r in $filtered-results
    order by $r descending
    return
    $r/..
return $activities        

Result

<r type="a">
    <activity>AA</activity>
</r>
<r type="a">
    <activity>A</activity>
</r>

if expression in predicate from Adam Retter

The conditional expression can be used in a predicate but this would inhibit any indexes defined.

XQuery

let $type:= "a"
let $result := $context//activity
    
let $activities :=
    for $r in $result[if($type ne '') then ../@type = $type else exists(../@type)]
    order by $r descending
    return
    $r/..
return $activities        

Result

<r type="a">
    <activity>AA</activity>
</r>
<r type="a">
    <activity>A</activity>
</r>

higher-order function from Adam Retter

This doesnt compile ?? - see Higher-Order Functions in XQuery 3.0

XQuery

let $type:= "a"
let $result := $context//activity
    
let $result-fn := 
    if($type ne '') 
    then  function($t) { $t eq $type }
    else  function($t) { exists($t) }
return
    for $r in $result[$result-fn(../@type)]
    order by $r descending
    return
    $r/..     

Result

compilation error

user defined function from Chris Wallace

user defined functions can be used in a path expression but this would also inhibit index usage.

XQuery

declare function local:select($r,$type) {
    if($type ne '') 
    then  $r/../@type eq $type 
    else  exists($r/../@type)
};

let $type:= "a"
let $result := $context//activity
return
    for $r in $result[local:select(.,$type)]
    order by $r descending
    return
    $r/..     

Result

<r type="a">
    <activity>AA</activity>
</r>
<r type="a">
    <activity>A</activity>
</r>

util:eval from Chris Wallace

if there is a complex conditional predicate where we need to make best use of the indexes, then we canconstruct the expression as a string and use util:eval to evaluate it. In this simple example, the execution line is longer

XQuery

            
let $type:= "a"
let $result := $context//activity
let $filter := concat("$result",if ($type ne "") then "[../@type = $type]" else "[exists(../@type)]")
return
    for $r in util:eval($filter)
    order by $r descending
    return
    $r/..     

Result

<r type="a">
    <activity>AA</activity>
</r>
<r type="a">
    <activity>A</activity>
</r>