BoomShaka
BoomShaka

Reputation: 1791

XQuery: extract number from string to use in comparison

I am very new to XQuery, and frankly find the learning curve incredibly steep.

I have an XML structure that looks something like this:

<root>
    <product>
        <size>500 units</size>
    </product>
    <product>
        <size>1000 units</size>
    </product>
    <product>
        <size>Unlimited units</size>
    </product>
</root>

I need to write an XQuery statement that returns all nodes where the numerical value in size is less than say 1000. So I somehow need to determine this numerical value (ignoring any text) to perform an 'le' operation I assume.

On top of this, there is the possibility that the node will have no digits at all (e.g. 'Unlimited units'), in which case it needs to be treated as having a value of say 1000000.

Is there some way to do this? I've tried various combinations of fn:replace(blah, '\D', '') and casting to xs:int, but I can't seem to get it to work.

Any guidance would be greatly appreciated.

Upvotes: 4

Views: 10872

Answers (4)

Chris Wallace
Chris Wallace

Reputation: 515

A bit of repetition for the sake of clarity

/root/product[not(substring-before(size," ") castable as xs:integer) or xs:integer(substring-before(size," ")) < 1000]

A bit of repetition for the sake of clarity

Upvotes: 0

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243529

Use this XPath 1.0 expression:

/root/product[not(number(substring-before(size, ' ')) >= 1000)]

As we all know well, XPath is a subset of XQuery, so the above is also an XQuery expression.

Upvotes: 4

user357812
user357812

Reputation:

This XQuery:

for $vProduct in /root/product
let $vUnits := number(substring-before($vProduct/size,'units'))
let $vSize := if ($vUnits)
              then $vUnits
              else 1000000
where $vSize le 1000
return $vProduct

Output:

<product>
    <size>500 units</size>
</product>
<product>
    <size>1000 units</size>
</product>

Upvotes: 2

paweloque
paweloque

Reputation: 18864

Probably you should use the fn:tokenize(subject, pattern, flags) (here is an description of the tokenize) function to extract your int values. The tokenize function will return an array with your int values in the first position of the array. You can then do whatever you want with your int values.

To distinguish between the cases where the units are given and the "Unlimited units" case, you could do a regexp fn:match with "Unlimited units" or simply compare the string before tokenizing. If the string is "Unlimited units" you'd treat is ar necessary and in the other case you tokenize the string to extract the int value.

Upvotes: 0

Related Questions