Reputation: 52424
This query is returning values less than 1000. It should only be returning values between 1000 and 1100. Why is that?
//results/Building[ 1 = 1 and (( Vacancy/sqft > 1000 ) and ( Vacancy/sqft < 1100 ) ) ]
The query will return the following building, which has vacancies less than 1000 square feet and greater than 1100 square feet:
<Building>
<Vacancy><sqft>900</sqft></Vacancy>
<Vacancy><sqft>1000</sqft></Vacancy>
<Vacancy><sqft>2000</sqft></Vacancy>
<Vacancy><sqft>500</sqft></Vacancy>
</Building>
Why is it included in the results?
Sample data:
<results>
<Building><!--Shouldn't be selected.--></Building>
<Building><!--Should be selected-->
<Vacancy><sqft>1050</sqft></Vacancy>
</Building>
<Building><!--Should be selected-->
<Vacancy><sqft>1025</sqft></Vacancy>
<Vacancy><sqft>1075</sqft></Vacancy>
</Building>
<Building><!--Shouldn't be selected-->
<Vacancy><sqft>10</sqft></Vacancy>
<Vacancy><sqft>50</sqft></Vacancy>
</Building>
<Building><!--Should be selected.-->
<Vacancy><sqft>1050</sqft></Vacancy>
<Vacancy><sqft>2000</sqft></Vacancy>
</Building>
<Building><!--Should be selected.-->
<Vacancy><sqft>900</sqft></Vacancy>
<Vacancy><sqft>1040</sqft></Vacancy>
</Building>
<Building><!--Shouldn't be selected-->
<Vacancy><sqft>10500</sqft></Vacancy>
</Building>
<Building><!--Shouldn't be selected-->
<Vacancy><sqft>900</sqft></Vacancy>
<Vacancy><sqft>1000</sqft></Vacancy>
<Vacancy><sqft>2000</sqft></Vacancy>
<Vacancy><sqft>500</sqft></Vacancy>
</Building>
</results>
Thanks.
Upvotes: 4
Views: 11151
Reputation: 77420
The sample Building has a Vacancy child with sqft of 2000, so Vacancy/sqft > 1000
succeeds. It has a child with sqft of 1000 (and 900 and 500), so Vacancy/sqft < 1100
succeeds. Thus the xpath selects the Building.
The comparison expressions (such as Vacancy/sqft <= 1000
) are implicitly qualified with "there exists"–as in "there exists a Vacancy child that has a sqft child with value > 1000"–because Vacancy/sqft
is a set of nodes, rather than a single node. Moreover, each comparison has its own qualification, so the sqft in Vacancy/sqft > 1000
doesn't need to be the same sqft as in Vacancy/sqft < 1100
. Note that //results/Buildings
is a node set; the predicate [...]
applies separately to each item in the set, which is why there isn't an issue with qualifiers. Translating your original xpath into English, we get:
Select the buildings (in the results) such that 1=1 and there exists a vacancy square footage > 1000 and there exists a vacancy square footage < 1100.
Let's take the English statement of the desired query and make it a little closer to a statement of logic, arriving at one of:
Select the buildings (in the results) such that there exists a vacancy with square footage such that it's > 1000 and it's < 1100
Select the buildings (in the results) such that there exists a vacancy such that the square footage > 1000 and the square footage < 1100
The former leads to jasso's solution, the latter to:
//results/Building[ Vacancy[1000 < sqft and sqft < 1100] ]
(Note: this answered the original question, when it wasn't clear what the OP wanted. The technique may prove useful to others with a similar problem but different requirements, so I'm leaving it in.)
Try the logical double-negation of the condition:
//results/Building[ Vacancy and not (Vacancy/sqft <= 1000 or Vacancy/sqft >= 1100) ]
This predicate includes a test for Vacancy children to filter out cases that are otherwise trivially true, i.e. buildings with no vacancies. The English equivalent of this solution is:
Select buildings (in the results) such that the building has a vacancy and it's not the case that there exists a vacancy square footage <= 1000 or there exists a vacancy square footage >= 1100
In fewer words:
Select all buildings with vacancies where no vacancy has <= 1000 square feet or >= 1100 square feet.
In fewer words still:
Select all buildings with vacancies where all vacancies are between 1000 and 1100 square feet.
Upvotes: 8
Reputation: 243489
Here are two XPath expressions:
1. The following selects all nodes that you believe should be selected:
/*/*[Vacancy and not(Vacancy[. < 1000 or . > 1100])]
/*/*[not(Vacancy) or Vacancy[. > 1000 and not(. > 1100)]]
This XSLT transformation can be used to verifu the correctness of the XPath expressions:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:copy-of select=
"/*/*[Vacancy and not(Vacancy[. < 1000 or . > 1100])]
"/>
===============================
<xsl:copy-of select=
"/*/*[not(Vacancy) or Vacancy[. > 1000 and not(. > 1100)]]
"/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<results>
<Building><!--Should this be selected?--></Building>
<Building><!--Should be selected-->
<Vacancy><sqft>1050</sqft></Vacancy>
</Building>
<Building><!--Should be selected-->
<Vacancy><sqft>1025</sqft></Vacancy>
<Vacancy><sqft>1075</sqft></Vacancy>
</Building>
<Building><!--Shouldn't be selected-->
<Vacancy><sqft>10</sqft></Vacancy>
<Vacancy><sqft>50</sqft></Vacancy>
</Building>
<Building><!--Should this be selected?-->
<Vacancy><sqft>1050</sqft></Vacancy>
<Vacancy><sqft>2000</sqft></Vacancy>
</Building>
<Building><!--Should this be selected?-->
<Vacancy><sqft>900</sqft></Vacancy>
<Vacancy><sqft>1040</sqft></Vacancy>
</Building>
<Building><!--Shouldn't be selected-->
<Vacancy><sqft>10500</sqft></Vacancy>
</Building>
<Building><!--Shouldn't be selected-->
<Vacancy><sqft>900</sqft></Vacancy>
<Vacancy><sqft>1000</sqft></Vacancy>
<Vacancy><sqft>2000</sqft></Vacancy>
<Vacancy><sqft>500</sqft></Vacancy>
</Building>
</results>
the wanted, correct results are produced:
<Building><!--Should be selected-->
<Vacancy>
<sqft>1050</sqft>
</Vacancy>
</Building>
<Building><!--Should be selected-->
<Vacancy>
<sqft>1025</sqft>
</Vacancy>
<Vacancy>
<sqft>1075</sqft>
</Vacancy>
</Building>
===============================
<Building><!--Should this be selected?--></Building>
<Building><!--Should be selected-->
<Vacancy>
<sqft>1050</sqft>
</Vacancy>
</Building>
<Building><!--Should be selected-->
<Vacancy>
<sqft>1025</sqft>
</Vacancy>
<Vacancy>
<sqft>1075</sqft>
</Vacancy>
</Building>
<Building><!--Should this be selected?-->
<Vacancy>
<sqft>1050</sqft>
</Vacancy>
<Vacancy>
<sqft>2000</sqft>
</Vacancy>
</Building>
<Building><!--Should this be selected?-->
<Vacancy>
<sqft>900</sqft>
</Vacancy>
<Vacancy>
<sqft>1040</sqft>
</Vacancy>
</Building>
Upvotes: 1
Reputation: 85086
Try changing your xpath to this:
//results/Building[number(Vacancy/sqft) > 1000 and number(Vacancy/sqft) < 1100 ]
I suspect it's treating your Vacancy/sqft
node like text which could be causing some weirdness...
I removed your 1=1 and extra parens because I didn't see a need for them. The main point is to try the number
function.
UPDATE
This one is a little odd but it grabs the ones you want plus the one you aren't sure if you want (Should this be selected?
):
//results/Building[count(Vacancy[sqft > 1000 and sqft < 1100 ]) = count(Vacancy)]
and if you want to exclude that one:
//results/Building[(count(Vacancy[sqft > 1000 and sqft < 1100 ]) = count(Vacancy)) and count(Vacancy) > 0]
Also I am using this site to text my xpaths, if there is some sort of fundamental difference between how they do it and how objective-c does let me know...
Upvotes: 1
Reputation: 13986
Do you also need to match buildings with some sqft outside your criteria but at least one sqft between 1000-1100 like this
<Building>Should this be selected too?
<Vacancy><sqft>1000</sqft></Vacancy>
<Vacancy><sqft>1050</sqft></Vacancy>
<Vacancy><sqft>2000</sqft></Vacancy>
</Building>
If yes, then use XPath expression
/results/Building[Vacancy/sqft[. > 1000 and 1100 > . ] or not(Vacancy)]
It also selects buildings with no <Vacancy>
element (as requested).
Upvotes: 7
Reputation:
//results /Building[1 = 1 and (( Vacancy/sqft > 1000 ) and (Vacancy/sqft < 1100 ))]
This query is returning values less than 1000. It should only be returning values between 1000 and 1100. Why is that?
From http://www.w3.org/TR/xpath/#booleans
If one object to be compared is a node-set and the other is a number, then the comparison will be true if and only if there is a node in the node-set such that the result of performing the comparison on the number to be compared and on the result of converting the string-value of that node to a number using the number function is true.
Node set comparisons are existencial comparisons. Vacancy/sqft > 1000
means: Is there at least one Vacancy/sqft
greater than 1000?
If you want to select Building
elements having Vacancy/sqft
grand children, and all of them in the range (1000,1100), this XPath expression:
/results/Building[Vacancy/sqft and not(Vacancy/sqft[1000 >= . or . >= 1100])]
Upvotes: 3