4

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.

11
  • So you're saying it's returning the Building node above? Also why is are your conditions structured the way they are (1=1 and extra parens)? I noticed that results is missing from your XML structure, was that just a typo or does the XML you are dealing with not have a results node? Commented Oct 26, 2010 at 22:45
  • Yes. I forgot to add <results>. 1=1 is there because I am adding other additional criteria on the fly. Commented Oct 26, 2010 at 22:49
  • I'm using objective-c, libxml. Commented Oct 26, 2010 at 22:50
  • What tools/programming language are you using? And what should it return? Commented Oct 26, 2010 at 22:50
  • 3
    @Simone: In english, what buildings do you want to select? For example, "All buildings with a vacancy between 1000 and 1100 square feet", "All buildings with vacancies, where all vacancies are between 1000 and 1100 square feet". Commented Oct 26, 2010 at 23:53

5 Answers 5

8

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] ]

Original solution

(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.

Sign up to request clarification or add additional context in comments.

2 Comments

Yes I see your logic but that's not the result I was expecting.
What outis is saying is that all of the conditions in your predicate are met, so you will get that Building every time. You need to look at it from the context of the Building. You're not testing all of the Vacancy/sqft values at once. It's easier to think of the XPath you wrote like this: Does 1 = 1? Yes. Does Building have a Vacancy/sqft greater than 1000? Yes. Does Building have a Vacancy/sqft less than 1100? Yes.
7

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).

Comments

3
//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])]

3 Comments

This expression doesn't match "All buildings with a vacancy between 1000 and 1100 square feet". But maybe the OP added that definition after you posted your answer. +1 for explanation of key concept, existential comparisons.
@LarsH: all the answers were given before the English statement of the query was specified, so there's that. We all headed off in different directions before the goal was set.
@outis: HIWTH (hate it when that happens)
1

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...

11 Comments

Good catch, but "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." (XPath 1.0, §3.4).
Also, that was one of the most complicated sentences I've ever read. Is it just me?
This fails with this data: <Building><Vacancy><sqft>1050</sqft></Vacancy><Vacancy><sqft>1101</sqft></Vacancy></Building> (it will return this Building even though there is a Vacancy/sqft greater than 1100.
There is also one between 1000 and 1100. I was under the impression that she wanted to return building nodes that had sqft in that range...
Yes, thanks, I tested it, but that doesn't make a difference. It already compares it as a number....
|
1

Here are two XPath expressions:

1. The following selects all nodes that you believe should be selected:

/*/*[Vacancy and not(Vacancy[. < 1000 or . > 1100])]
  1. The following selects all nodes that you believe should be selected and all those you are not certain about. It doesn't select any node that you are certain should not be selected:

/*/*[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[. &lt; 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>

2 Comments

I don't think either of these exactly matches "All buildings with a vacancy between 1000 and 1100 square feet"... or else I'm missing something.
@LarsH: One selects all "should" nodes, the other selects these + all "in doubt nodes".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.